Skip to main content
Commitment levels define how finalized a transaction or account state is on the Solana blockchain. Choosing the right commitment level is crucial for balancing speed, consistency, and security.

Overview

The Commitment enum provides three levels of transaction finality: Package: software.sava.rpc.json.http.request File: Commitment.java:7
public enum Commitment {
    FINALIZED("finalized"),
    CONFIRMED("confirmed"),
    PROCESSED("processed")
}
See Commitment.java:9-11.

Commitment Levels

PROCESSED

PROCESSED
Commitment
Query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as processed
Characteristics:
  • Fastest - Data available immediately
  • Least secure - May be rolled back
  • Use case: Real-time updates, mempool monitoring, UI previews
Risk: Blocks at this level can still be orphaned due to network forks. The transaction may not be included in the final chain. Example:
import software.sava.rpc.json.http.request.Commitment;

// Monitor pending transactions
var signatures = client.getSignaturesForAddress(
    Commitment.PROCESSED,
    address,
    10
).join();

for (var sig : signatures) {
    System.out.println("Pending tx: " + sig.signature());
}

CONFIRMED

CONFIRMED
Commitment
Query the most recent block that has been voted on by supermajority of the cluster
Characteristics:
  • Moderate speed - Usually available within 1-2 seconds
  • Good security - Unlikely to be rolled back
  • Use case: Most applications, wallet confirmations, general queries
  • Default - Recommended for most use cases
Risk: Very low probability of rollback. Only extreme network conditions could cause this. Example:
// Default commitment for production apps
var client = SolanaRpcClient.build()
    .endpoint(URI.create("https://api.mainnet-beta.solana.com"))
    .defaultCommitment(Commitment.CONFIRMED)
    .createClient();

// Check wallet balance
var balance = client.getBalance(walletAddress).join();
System.out.println("Balance: " + balance.lamports() + " lamports");

FINALIZED

FINALIZED
Commitment
Query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout (cannot be rolled back)
Characteristics:
  • Slowest - May take 30-45 seconds
  • Most secure - Cannot be rolled back
  • Use case: High-value transactions, bridges, exchanges, auditing
  • Guarantee: Absolute finality, transaction is permanent
Risk: None. This is the final state of the blockchain. Example:
// Wait for absolute finality before crediting account
var txSig = client.sendTransaction(transaction, feePayer).join();

// Poll for finalized status
var startTime = System.currentTimeMillis();
while (true) {
    var status = client.getSignatureStatuses(List.of(txSig)).join().get(txSig);
    
    if (status.confirmationStatus() == Commitment.FINALIZED) {
        if (status.error() == null) {
            System.out.println("Transaction finalized successfully!");
            creditUserAccount();
        } else {
            System.err.println("Transaction failed: " + status.error());
        }
        break;
    }
    
    Thread.sleep(1000);
    
    if (System.currentTimeMillis() - startTime > 60_000) {
        System.err.println("Timeout waiting for finalization");
        break;
    }
}

Comparison Table

LevelSpeedSecurityRollback RiskTypical Delay
PROCESSEDFastestLowestMedium-High<400ms
CONFIRMEDFastHighVery Low1-2s
FINALIZEDSlowestHighestNone30-45s

When to Use Each Level

Use PROCESSED for:

  • Real-time UI updates
  • Live account balance displays
  • Transaction status polling (initial check)
  • Mempool monitoring
  • Non-critical data display
// Real-time balance display
var ws = SolanaRpcWebsocket.build()
    .uri(SolanaNetwork.MAIN_NET)
    .commitment(Commitment.PROCESSED)
    .create();

ws.accountSubscribe(walletAddress, accountInfo -> {
    updateUI(accountInfo.lamports());
});

Use CONFIRMED for:

  • Standard wallet operations
  • NFT minting confirmations
  • General-purpose queries
  • Most dApp interactions
  • Default application behavior
// Standard dApp usage
var client = SolanaRpcClient.build()
    .defaultCommitment(Commitment.CONFIRMED)
    .createClient();

// Get account info for display
var accountInfo = client.getAccountInfo(publicKey).join();
displayAccountData(accountInfo);

Use FINALIZED for:

  • Exchange deposits/withdrawals
  • Bridge operations
  • Large value transfers
  • Irreversible operations
  • Compliance/audit requirements
  • Settlement finality
// Bridge deposit - require finality
public void processDeposit(String txSig) {
    var status = client.getSignatureStatuses(
        List.of(txSig)
    ).join().get(txSig);
    
    if (status.confirmationStatus() == Commitment.FINALIZED 
        && status.error() == null) {
        // Safe to credit on destination chain
        bridgeToEthereum(txSig);
    }
}

Best Practices

Progressive Confirmation

Use multiple commitment levels for better UX:
public void monitorTransaction(String txSig) {
    // 1. Show as pending immediately (PROCESSED)
    var ws = SolanaRpcWebsocket.build()
        .commitment(Commitment.PROCESSED)
        .create();
    
    ws.signatureSubscribe(txSig, result -> {
        ui.showStatus("Transaction received");
    });
    
    // 2. Show as confirmed (CONFIRMED)
    ws.signatureSubscribe(
        Commitment.CONFIRMED,
        txSig,
        result -> {
            ui.showStatus("Transaction confirmed");
            enableRelatedActions();
        }
    );
    
    // 3. Show as finalized (FINALIZED)
    ws.signatureSubscribe(
        Commitment.FINALIZED,
        txSig,
        result -> {
            ui.showStatus("Transaction finalized");
            markAsPermanent();
        }
    );
}

Default to CONFIRMED

Set CONFIRMED as default and override when needed:
var client = SolanaRpcClient.build()
    .defaultCommitment(Commitment.CONFIRMED)
    .createClient();

// Use default (CONFIRMED)
var balance = client.getBalance(address).join();

// Override for high-value check (FINALIZED)
var finalizedBalance = client.getBalance(
    Commitment.FINALIZED,
    treasuryAddress
).join();

// Override for real-time updates (PROCESSED)
var liveBalance = client.getBalance(
    Commitment.PROCESSED,
    userAddress
).join();

Handle Commitment in WebSocket Subscriptions

var ws = SolanaRpcWebsocket.build()
    .uri(SolanaNetwork.MAIN_NET)
    .commitment(Commitment.CONFIRMED)  // Default
    .create();

// Use default CONFIRMED
ws.accountSubscribe(account, updates -> {
    displayToUser(updates);
});

// Override with PROCESSED for real-time
ws.accountSubscribe(
    Commitment.PROCESSED,
    speedAccount,
    liveUpdates -> {
        updateChart(liveUpdates);
    }
);

// Override with FINALIZED for security
ws.accountSubscribe(
    Commitment.FINALIZED,
    vaultAccount,
    secureUpdates -> {
        auditLog(secureUpdates);
    }
);

Implementation Details

getValue()

Get the string value for JSON-RPC requests:
Commitment commitment = Commitment.CONFIRMED;
String value = commitment.getValue();  // Returns "confirmed"
This value is used in JSON-RPC request payloads. See Commitment.java:19-21.

Parsing Commitment

The enum includes a parser for deserializing from JSON responses:
public static final CharBufferFunction<Commitment> PARSER
See Commitment.java:23-33.

Common Patterns

Transaction Confirmation Flow

public class TransactionMonitor {
    private final SolanaRpcClient client;
    private final SolanaRpcWebsocket ws;
    
    public void sendAndMonitor(Transaction tx, Signer signer) {
        // 1. Send transaction
        String txSig = client.sendTransaction(tx, signer).join();
        System.out.println("Transaction sent: " + txSig);
        
        // 2. Wait for PROCESSED (immediate feedback)
        ws.signatureSubscribe(
            Commitment.PROCESSED,
            txSig,
            result -> System.out.println("✓ Processed")
        );
        
        // 3. Wait for CONFIRMED (safe for most purposes)
        ws.signatureSubscribe(
            Commitment.CONFIRMED,
            txSig,
            result -> {
                System.out.println("✓ Confirmed");
                if (result.error() == null) {
                    notifyUser("Transaction successful!");
                }
            }
        );
        
        // 4. Wait for FINALIZED (absolute certainty)
        ws.signatureSubscribe(
            Commitment.FINALIZED,
            txSig,
            result -> {
                System.out.println("✓ Finalized");
                if (result.error() == null) {
                    recordInDatabase(txSig);
                }
                ws.signatureUnsubscribe(Commitment.FINALIZED, txSig);
            }
        );
    }
}

Account Consistency Check

public void verifyAccountConsistency(PublicKey account) {
    // Get data at different commitment levels
    var processed = client.getAccountInfo(
        Commitment.PROCESSED,
        account
    ).join();
    
    var confirmed = client.getAccountInfo(
        Commitment.CONFIRMED,
        account
    ).join();
    
    var finalized = client.getAccountInfo(
        Commitment.FINALIZED,
        account
    ).join();
    
    // Compare states
    System.out.println("Processed slot: " + processed.context().slot());
    System.out.println("Confirmed slot: " + confirmed.context().slot());
    System.out.println("Finalized slot: " + finalized.context().slot());
    
    if (processed.lamports() != finalized.lamports()) {
        System.out.println("⚠️ Balance differs between PROCESSED and FINALIZED");
    }
}

Security Considerations

Never Use PROCESSED for Financial Operations

// ❌ WRONG - Deposits could be rolled back
public void processDeposit(String txSig) {
    var status = client.getSignatureStatuses(
        List.of(txSig)
    ).join().get(txSig);
    
    if (status.confirmationStatus() == Commitment.PROCESSED) {
        creditUserAccount();  // DANGEROUS!
    }
}

// ✅ CORRECT - Wait for finality
public void processDeposit(String txSig) {
    var status = client.getSignatureStatuses(
        List.of(txSig)
    ).join().get(txSig);
    
    if (status.confirmationStatus() == Commitment.FINALIZED
        && status.error() == null) {
        creditUserAccount();  // Safe
    }
}

Bridge Safety

public class BridgeValidator {
    private static final Commitment REQUIRED_COMMITMENT = Commitment.FINALIZED;
    
    public boolean canBridgeTransaction(String txSig) {
        var status = client.getSignatureStatuses(
            List.of(txSig)
        ).join().get(txSig);
        
        return status.confirmationStatus() == REQUIRED_COMMITMENT
            && status.error() == null;
    }
}

See Also

Build docs developers (and LLMs) love