Solana Commitment Levels: The Line That Sets Fill Rate

Which commitment to use for reads, for sends, and for accounting — and why one default across all three is wrong in both directions.

BoltTx Team··10 min read
solanacommitmentrpctransaction-landingconfirmationtrading-bot

Most Solana codebases set a commitment once, in the Connection constructor, and never think about it again. That single choice is then applied to reads, to sends, to confirmations, and to accounting — four situations with four different correct answers.

★The cost of getting it wrong is asymmetric.★ Too weak and you act on state that gets rolled back. Too strong and you are waiting slots for certainty you did not need.

What the Three Levels Mean

Level Meaning Can it be rolled back?
processed ★A validator has executed it★ yes
confirmed A supermajority has voted on the block very rarely
finalized 31+ blocks have been built on top ★no★

processed is the fastest thing available and the least certain. finalized is certainty and costs roughly thirty blocks of waiting. confirmed sits between them and is where most application logic belongs.

★The practical gap between confirmed and finalized is much larger than the gap between processed and confirmed.★ That asymmetry is why "just use finalized to be safe" is a more expensive default than people expect.

Three Situations, Three Answers

Reading state you will act on: confirmed.

const connection = new Connection(url, "confirmed");

Rollbacks at this level are rare enough that most trading logic treats them as noise, and the latency is acceptable.

Detecting an event you race on: processed.

connection.onProgramAccountChange(
  PROGRAM_ID,
  (info) => handleLaunch(info),
  "processed",              // ★fastest available★
);

If you are competing for a launch, waiting for confirmed means learning about it after the people who did not wait. ★Accept that a small fraction may be rolled back, and verify before anything irreversible.★

Accounting and reconciliation: finalized.

const tx = await connection.getTransaction(sig, {
  commitment: "finalized",             // ★will not change★
  maxSupportedTransactionVersion: 0,
});

Anything that becomes a number in a report should be read at finalized. A P&L computed from confirmed data is a number that can still change.

If your commitment choices are right and transactions still land late, a free BoltTx key is one line to test the submission path.

Preflight Has Its Own Commitment

The setting people miss entirely:

await connection.sendRawTransaction(raw, {
  skipPreflight: false,
  preflightCommitment: "processed",     // ★separate from the connection default★
});

If you leave skipPreflight: false and the preflightCommitment defaults to finalized, ★your simulation runs against state roughly thirty blocks old.★ A transaction that is perfectly valid against current state can fail preflight because the account it needs was created twenty blocks ago.

This produces one of the more confusing bug reports in Solana development: the transaction works when you retry it a minute later, and nobody can explain why.

Two fixes, and the second is usually better:

Match preflight to your send commitment. processed if you are sending against current state.

Skip preflight entirely. For trading, this is generally correct — it removes a round trip and simulates the wrong slot regardless.

Confirmation Should Not Use finalized

A common and expensive pattern:

// ★Waits ~30 blocks for something you already know.★
await connection.confirmTransaction(sig, "finalized");

For a trading bot this is close to always wrong. By the time it resolves, the opportunity is gone and you have been blocking on certainty that your next action does not require.

// Poll at confirmed, stop at expiry.
const { value } = await connection.getSignatureStatuses([sig]);
const st = value[0];

if (st?.confirmationStatus === "confirmed" || st?.confirmationStatus === "finalized") {
  return st.err ? "reverted" : "success";
}

getSignatureStatuses returns the level reached, so you can decide per call rather than committing to one in advance.★ That is more useful than confirmTransaction, which makes the decision for you.

Where processed Actually Bites

The rollback risk is real, and it concentrates in one situation: acting irreversibly on state that has not been confirmed.

// ★Dangerous shape.★
const balance = await connection.getBalance(user, "processed");
await creditUserInDatabase(balance);      // off-chain, not reversible

If that block gets rolled back, the chain forgets and your database does not. Now your records disagree with reality, and nothing will correct it automatically.

The rule that keeps this safe: ★use processed to decide what to do, and confirmed or finalized to record what happened.★ Detection at processed is fine because the worst case is a wasted transaction. Bookkeeping at processed is not, because the worst case is a permanent inconsistency.

Commitment Affects Your Blockhash Too

getLatestBlockhash takes a commitment, and it changes how long your transaction stays valid:

// ★Older blockhash, longer remaining validity window.★
const { blockhash, lastValidBlockHeight } =
  await connection.getLatestBlockhash("confirmed");

A processed blockhash is the newest available, which means the full validity window ahead of it — but it comes from a block that could still be rolled back, and a transaction signed against a rolled-back blockhash is rejected.

A finalized blockhash cannot be rolled back, but it is already around thirty blocks old, so you have burned a fifth of your window before sending.

confirmed is the default worth reaching for: a supermajority has voted on the block, so a rollback is rare enough to plan around, and you give up only a few blocks of the window to get it.★

Mixed Commitments in One Flow

A realistic trading flow uses three different levels, and that is correct rather than sloppy:

// 1. Detect fast.
subscribeToLaunches("processed");

// 2. Verify before committing money.
const pool = await connection.getAccountInfo(poolAddress, "confirmed");
if (!isValid(pool)) return;

// 3. Sign against a blockhash with room to retry.
const { blockhash, lastValidBlockHeight } =
  await connection.getLatestBlockhash("confirmed");

// 4. Send without preflight.
const sig = await connection.sendRawTransaction(raw, {
  skipPreflight: true,
  maxRetries: 0,
});

// 5. Resolve at confirmed, stop at expiry.
const outcome = await resolve(sig, lastValidBlockHeight);

// 6. Record at finalized.
if (outcome.status === "success") {
  await recordForAccounting(sig, "finalized");
}

★Setting one commitment on the Connection and inheriting it everywhere is what produces both failure modes at once★ — too slow at detection and too weak at accounting.

What Landing Looks Like

Real transactions through our delivery nodes: median confirmation 336ms — under one slot.

★Commitment determines when you learn a transaction landed, not when it lands.★ These are separate questions, and confusing them is why teams sometimes think a faster confirmation setting made their bot faster.

Where BoltTx Fits

We handle submission. Commitment is a client-side choice and stays entirely yours.

Submissions route through our own delivery nodes in four regions with stake-weighted routing and no public mempool exposure, so a transaction is not observable in transit before it lands. Your reads and confirmations keep whatever setup you already have.

You sign locally. We never hold funds, never sign, and never modify transaction contents. The tip travels inside the transaction, paid on chain from your own wallet, and reverts with the transaction if it fails, because that is how Solana handles atomic transactions. You pay only on transactions that reach the chain.

Get a free API key. No monthly fee:

const connection = new Connection(
  "https://la.bolttx.io/?api-key=YOUR_KEY",
  "confirmed",
);

FAQ

What is the difference between processed, confirmed, and finalized? processed means a validator executed it and it can still be rolled back. confirmed means a supermajority voted on the block, which is rarely reversed. finalized means 31 or more blocks were built on top and it cannot change.

Which commitment should I use for a Solana trading bot? Different ones per purpose: processed for detection, confirmed for reads you act on and for your blockhash, finalized for accounting. One global setting is wrong for at least two of the three.

Is processed commitment safe to use? For deciding what to do, yes — the worst case is a wasted transaction. For recording what happened, no, because a rollback leaves your off-chain records permanently disagreeing with the chain.

Why does my transaction fail preflight but succeed on retry? Most likely preflightCommitment is defaulting to finalized, so simulation runs against state around thirty blocks old. An account created recently does not exist yet from that view.

Should I use finalized for confirmTransaction? Rarely. It waits roughly thirty blocks for certainty most trading logic does not need. Poll getSignatureStatuses at confirmed and stop at blockhash expiry instead.

What commitment should getLatestBlockhash use? confirmed in most cases. processed gives the newest blockhash but risks being rolled back, and finalized is already thirty blocks old, which spends part of your validity window before you send.

How long does finalized take on Solana? Roughly 31 blocks of additional confirmations after the block is produced. That is why it belongs in accounting rather than in a hot path.

Does commitment level affect transaction speed? No. It affects when you learn about the outcome, not how fast the transaction reaches a block producer. Landing speed comes from fees, routing, and retry behaviour.

Can a confirmed transaction be reverted? It is possible but very rare, since it requires a supermajority-voted block to be dropped. For anything that becomes a permanent record, finalized is the level that carries no such risk.

What is preflightCommitment and why does it matter? It is the commitment your preflight simulation runs against, and it is separate from your connection default. Set too high, it simulates against stale state and rejects valid transactions.

Should I set commitment on the Connection or per call? Set a sensible default on the Connection and override per call where it matters. Most methods accept a commitment argument, and the important calls deserve an explicit one.

Which commitment for WebSocket subscriptions? processed when you are racing, since waiting for confirmed means learning after everyone who did not wait. Verify at a higher level before acting on money.

Why do my balances look wrong at processed? Because you may be reading a block that gets rolled back. Balances used for display can tolerate this; balances used for accounting or for crediting a user cannot.

Does commitment affect getSignatureStatuses? The response tells you which level the signature reached, so you can decide per call. That is more flexible than confirmTransaction, which fixes the decision in advance.

What happens if my blockhash comes from a rolled-back block? The transaction is rejected as invalid, since the blockhash it references is not in the canonical chain. That is the risk of using processed for getLatestBlockhash.

Is finalized required for exchange deposits? For anything crediting real value off chain, yes. That is exactly the case where a rollback would leave your system permanently inconsistent with the chain.

How do I choose commitment for a read? Ask what happens if the value turns out to be wrong. If the answer is a wasted transaction, processed is fine. If the answer is a permanent record that must be corrected by hand, use finalized.

Back to all posts