Skip to main content

Overview

The SolanaRpcClient provides a comprehensive interface to interact with Solana RPC nodes. This guide covers client creation, making RPC calls, WebSocket subscriptions, and commitment levels.

Creating the RPC Client

Basic Client Setup

import software.sava.rpc.json.http.client.SolanaRpcClient;
import java.net.http.HttpClient;
import java.net.URI;

// Create HTTP client
HttpClient httpClient = HttpClient.newHttpClient();

// Create RPC client with endpoint
URI endpoint = URI.create("https://api.mainnet-beta.solana.com");
SolanaRpcClient rpcClient = SolanaRpcClient.createClient(
    endpoint,
    httpClient
);
Source: SolanaRpcClient.java:80-82

Using Pre-configured Networks

import software.sava.rpc.json.http.SolanaNetwork;

// Use built-in network endpoints
SolanaRpcClient mainnetClient = SolanaRpcClient.createClient(
    SolanaNetwork.MAIN_NET.getEndpoint(),
    httpClient
);

SolanaRpcClient devnetClient = SolanaRpcClient.createClient(
    SolanaNetwork.DEV_NET.getEndpoint(),
    httpClient
);

SolanaRpcClient testnetClient = SolanaRpcClient.createClient(
    SolanaNetwork.TEST_NET.getEndpoint(),
    httpClient
);

Client with Custom Configuration

import software.sava.rpc.json.http.request.Commitment;
import java.time.Duration;

// Create client with custom timeout and commitment
SolanaRpcClient rpcClient = SolanaRpcClient.createClient(
    endpoint,
    httpClient,
    Duration.ofSeconds(30),        // Request timeout
    Commitment.CONFIRMED           // Default commitment
);
Source: SolanaRpcClient.java:67-72

Using the Builder Pattern

import software.sava.rpc.json.http.client.SolanaRpcClientBuilder;

SolanaRpcClient rpcClient = SolanaRpcClient.build()
    .endpoint("https://api.mainnet-beta.solana.com")
    .httpClient(httpClient)
    .requestTimeout(Duration.ofSeconds(30))
    .defaultCommitment(Commitment.CONFIRMED)
    .create();
Source: SolanaRpcClient.java:41-43
1
Step 1: Create HTTP Client
2
Instantiate a Java HttpClient for making HTTP requests.
3
Step 2: Choose Endpoint
4
Select an RPC endpoint (mainnet, devnet, testnet, or custom).
5
Step 3: Configure Client
6
Set timeout, commitment level, and other options.
7
Step 4: Create RPC Client
8
Call createClient() or use the builder pattern.

Making RPC Calls

Getting Account Information

import software.sava.core.accounts.PublicKey;
import software.sava.rpc.json.http.response.AccountInfo;
import java.util.concurrent.CompletableFuture;

PublicKey account = PublicKey.fromBase58Encoded("AccountAddress");

// Get account info (returns raw bytes)
CompletableFuture<AccountInfo<byte[]>> accountFuture =
    rpcClient.getAccountInfo(account);

AccountInfo<byte[]> accountInfo = accountFuture.join();
if (accountInfo != null) {
    System.out.println("Owner: " + accountInfo.owner().toBase58());
    System.out.println("Lamports: " + accountInfo.lamports());
    System.out.println("Executable: " + accountInfo.executable());
    byte[] data = accountInfo.data();
}
Source: SolanaRpcClient.java:109-115

Getting Account with Custom Parser

import java.util.function.BiFunction;

// Custom parser for account data
BiFunction<PublicKey, byte[], MyAccountType> factory = 
    (pubkey, data) -> MyAccountType.parse(data);

CompletableFuture<AccountInfo<MyAccountType>> accountFuture =
    rpcClient.getAccountInfo(account, factory);

AccountInfo<MyAccountType> accountInfo = accountFuture.join();
MyAccountType myAccount = accountInfo.data();
Source: SolanaRpcClient.java:102-107

Getting Multiple Accounts

import java.util.List;

List<PublicKey> accounts = List.of(
    PublicKey.fromBase58Encoded("Account1"),
    PublicKey.fromBase58Encoded("Account2"),
    PublicKey.fromBase58Encoded("Account3")
);

// Get multiple accounts in one call
CompletableFuture<List<AccountInfo<byte[]>>> accountsFuture =
    rpcClient.getAccounts(accounts);

List<AccountInfo<byte[]>> accountInfos = accountsFuture.join();
for (AccountInfo<byte[]> info : accountInfos) {
    if (info != null) {
        System.out.println("Account: " + info.pubKey().toBase58());
    }
}
Source: SolanaRpcClient.java:427-434

Getting Program Accounts

import software.sava.core.rpc.Filter;
import java.util.Collection;

PublicKey programId = PublicKey.fromBase58Encoded("ProgramId");

// Get all accounts owned by program
CompletableFuture<List<AccountInfo<byte[]>>> accountsFuture =
    rpcClient.getProgramAccounts(programId);

List<AccountInfo<byte[]>> accounts = accountsFuture.join();
System.out.println("Found " + accounts.size() + " accounts");

// With filters
Collection<Filter> filters = List.of(
    Filter.createDataSizeFilter(165),  // Token account size
    Filter.createMemCompFilter(32, ownerPublicKey)  // Owner offset
);

CompletableFuture<List<AccountInfo<byte[]>>> filteredFuture =
    rpcClient.getProgramAccounts(programId, filters);
Source: SolanaRpcClient.java:496-509

Blockchain Queries

Getting Block Information

import software.sava.rpc.json.http.response.Block;

long slot = 123456789L;

// Get block
CompletableFuture<Block> blockFuture = rpcClient.getBlock(slot);

Block block = blockFuture.join();
System.out.println("Block time: " + block.blockTime());
System.out.println("Block hash: " + block.blockHash());
System.out.println("Transactions: " + block.transactions().size());
Source: SolanaRpcClient.java:175-177

Getting Slot Information

import java.util.concurrent.CompletableFuture;

// Get current slot
CompletableFuture<Long> slotFuture = rpcClient.getSlot();
long currentSlot = slotFuture.join();
System.out.println("Current slot: " + currentSlot);

// Get block height
CompletableFuture<BlockHeight> heightFuture = rpcClient.getBlockHeight();
long blockHeight = heightFuture.join().value();
System.out.println("Block height: " + blockHeight);
Source: SolanaRpcClient.java:760-762, SolanaRpcClient.java:238-240

Getting Latest Blockhash

import software.sava.rpc.json.http.response.LatestBlockHash;

CompletableFuture<LatestBlockHash> blockHashFuture =
    rpcClient.getLatestBlockHash();

LatestBlockHash latestBlockHash = blockHashFuture.join();
byte[] blockHash = latestBlockHash.blockHash();
long lastValidBlockHeight = latestBlockHash.lastValidBlockHeight();

System.out.println("Blockhash valid until height: " + lastValidBlockHeight);
Source: SolanaRpcClient.java:96-98

Getting Transaction

import software.sava.rpc.json.http.response.Tx;

String signature = "5j7s...";

// Get transaction
CompletableFuture<Tx> txFuture = rpcClient.getTransaction(signature);

Tx transaction = txFuture.join();
if (transaction != null) {
    System.out.println("Slot: " + transaction.slot());
    System.out.println("Block time: " + transaction.blockTime());
    
    // Access transaction metadata
    var meta = transaction.meta();
    System.out.println("Fee: " + meta.fee());
    System.out.println("Success: " + (meta.err() == null));
}
Source: SolanaRpcClient.java:819-823

Commitment Levels

Solana supports different commitment levels for query consistency.

Using Commitment Levels

import software.sava.rpc.json.http.request.Commitment;

// Processed - fastest, least certain
CompletableFuture<AccountInfo<byte[]>> processed =
    rpcClient.getAccountInfo(Commitment.PROCESSED, account);

// Confirmed - good balance (recommended)
CompletableFuture<AccountInfo<byte[]>> confirmed =
    rpcClient.getAccountInfo(Commitment.CONFIRMED, account);

// Finalized - slowest, most certain
CompletableFuture<AccountInfo<byte[]>> finalized =
    rpcClient.getAccountInfo(Commitment.FINALIZED, account);
Source: Commitment.java:9-11

Default Commitment

// Set default commitment at client creation
SolanaRpcClient rpcClient = SolanaRpcClient.createClient(
    endpoint,
    httpClient,
    Commitment.CONFIRMED  // Default for all calls
);

// Access default commitment
Commitment defaultCommitment = rpcClient.defaultCommitment();
Source: SolanaRpcClient.java:74-78, SolanaRpcClient.java:88
CONFIRMED provides the best balance between speed and certainty for most applications.

WebSocket Subscriptions

Subscribe to real-time updates via WebSocket.

Creating WebSocket Client

import software.sava.rpc.json.http.ws.SolanaRpcWebsocket;
import java.net.http.HttpClient;
import java.net.URI;

HttpClient httpClient = HttpClient.newHttpClient();

SolanaRpcWebsocket ws = SolanaRpcWebsocket.build()
    .uri("wss://api.mainnet-beta.solana.com")
    .webSocketBuilder(httpClient)
    .commitment(Commitment.CONFIRMED)
    .create();

// Connect
ws.connect().join();
Source: SolanaRpcWebsocket.java:41-43

Account Subscriptions

import software.sava.rpc.json.http.response.AccountInfo;

PublicKey account = PublicKey.fromBase58Encoded("AccountAddress");

// Subscribe to account changes
boolean subscribed = ws.accountSubscribe(
    account,
    accountInfo -> {
        System.out.println("Account updated!");
        System.out.println("Lamports: " + accountInfo.lamports());
        byte[] data = accountInfo.data();
        // Process account data
    }
);

if (subscribed) {
    System.out.println("Successfully subscribed to account");
}
Source: SolanaRpcWebsocket.java:66-68

Signature Subscriptions

import software.sava.rpc.json.http.response.TxResult;

String signature = "5j7s...";

// Subscribe to transaction confirmation
boolean subscribed = ws.signatureSubscribe(
    signature,
    result -> {
        System.out.println("Transaction confirmed!");
        if (result.err() == null) {
            System.out.println("Success");
        } else {
            System.out.println("Failed: " + result.err());
        }
    }
);
Source: SolanaRpcWebsocket.java:111-115

Program Subscriptions

import software.sava.core.rpc.Filter;
import java.util.List;

PublicKey programId = PublicKey.fromBase58Encoded("ProgramId");

// Subscribe to program account changes
List<Filter> filters = List.of(
    Filter.createDataSizeFilter(165)
);

boolean subscribed = ws.programSubscribe(
    programId,
    filters,
    accountInfo -> {
        System.out.println("Program account updated: " + 
                         accountInfo.pubKey().toBase58());
    }
);
Source: SolanaRpcWebsocket.java:176-180

Slot Subscriptions

import software.sava.rpc.json.http.response.ProcessedSlot;

// Subscribe to slot updates
boolean subscribed = ws.slotSubscribe(
    slot -> {
        System.out.println("New slot: " + slot.slot());
        System.out.println("Parent: " + slot.parent());
        System.out.println("Root: " + slot.root());
    }
);
Source: SolanaRpcWebsocket.java:229-233

Unsubscribing

// Unsubscribe from account
boolean unsubscribed = ws.accountUnsubscribe(account);

// Unsubscribe from signature
ws.signatureUnsubscribe(signature);

// Unsubscribe from program
ws.programUnsubscribe(programId);

// Unsubscribe from slots
ws.slotUnsubscribe();

// Close WebSocket
ws.close();
Source: SolanaRpcWebsocket.java:86-88, SolanaRpcWebsocket.java:156-158

Advanced RPC Features

Transaction Simulation

import software.sava.rpc.json.http.response.FeeForMessage;

// Get fee estimate
String base64Message = transaction.base64EncodeToString();

CompletableFuture<FeeForMessage> feeFuture =
    rpcClient.getFeeForMessage(base64Message);

FeeForMessage fee = feeFuture.join();
System.out.println("Estimated fee: " + fee.value() + " lamports");
Source: SolanaRpcClient.java:92-94

Getting Signatures for Address

import software.sava.rpc.json.http.response.TxSig;
import java.util.List;

PublicKey address = PublicKey.fromBase58Encoded("Address");
int limit = 100;

CompletableFuture<List<TxSig>> sigsFuture =
    rpcClient.getSignaturesForAddress(address, limit);

List<TxSig> signatures = sigsFuture.join();
for (TxSig sig : signatures) {
    System.out.println("Signature: " + sig.signature());
    System.out.println("Slot: " + sig.slot());
    System.out.println("Block time: " + sig.blockTime());
}
Source: SolanaRpcClient.java:722-726

Checking Health

import software.sava.rpc.json.http.response.NodeHealth;

CompletableFuture<NodeHealth> healthFuture = rpcClient.getHealth();

try {
    NodeHealth health = healthFuture.join();
    System.out.println("Node is healthy");
} catch (Exception e) {
    System.err.println("Node is unhealthy: " + e.getMessage());
}
Source: SolanaRpcClient.java:90

Getting Epoch Information

import software.sava.rpc.json.http.response.EpochInfo;

CompletableFuture<EpochInfo> epochFuture = rpcClient.getEpochInfo();

EpochInfo epoch = epochFuture.join();
System.out.println("Epoch: " + epoch.epoch());
System.out.println("Slot index: " + epoch.slotIndex());
System.out.println("Slots in epoch: " + epoch.slotsInEpoch());
System.out.println("Absolute slot: " + epoch.absoluteSlot());
Source: SolanaRpcClient.java:278-280

Error Handling

Handling RPC Exceptions

import software.sava.rpc.json.http.response.JsonRpcException;

try {
    AccountInfo<byte[]> account = rpcClient
        .getAccountInfo(publicKey)
        .join();
    
    if (account == null) {
        System.out.println("Account does not exist");
    } else {
        // Process account
    }
} catch (JsonRpcException e) {
    System.err.println("RPC Error: " + e.getMessage());
    System.err.println("Error code: " + e.getCode());
} catch (Exception e) {
    System.err.println("Unexpected error: " + e.getMessage());
}

Async Error Handling

rpcClient.getAccountInfo(account)
    .thenAccept(accountInfo -> {
        if (accountInfo != null) {
            System.out.println("Found account");
        }
    })
    .exceptionally(error -> {
        System.err.println("Failed: " + error.getMessage());
        return null;
    });

Best Practices

  • Reuse HTTP client instances across requests
  • Configure appropriate timeouts for your use case
  • Use connection pooling for high-throughput applications
  • Use CONFIRMED for most applications
  • Use PROCESSED only for real-time updates where speed is critical
  • Use FINALIZED for applications requiring absolute certainty
  • Implement reconnection logic for WebSocket failures
  • Unsubscribe from channels when no longer needed
  • Handle connection lifecycle with onOpen/onClose handlers
  • Batch account queries with getMultipleAccounts
  • Use filters with getProgramAccounts to reduce response size
  • Cache frequently accessed data (e.g., mint decimals)

Signing and Sending

Learn how to send transactions via RPC

Token Operations

Work with SPL tokens using the RPC client

Build docs developers (and LLMs) love