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
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
Step 1: Create HTTP Client
Instantiate a Java HttpClient for making HTTP requests.
Select an RPC endpoint (mainnet, devnet, testnet, or custom).
Set timeout, commitment level, and other options.
Step 4: Create RPC Client
Call createClient() or use the builder pattern.
Making RPC Calls
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
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
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
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
Signing and Sending Learn how to send transactions via RPC
Token Operations Work with SPL tokens using the RPC client