How many confirmations should I wait for before trusting a transaction? · Jumpstart Blockchain
How many confirmations should I wait for before trusting a transaction?
On Bitcoin, scale confirmation depth with the amount at risk, since finality is probabilistic. On Ethereum, wait for the finalized checkpoint. On rollups, wait for the batch to be final on L1.
It depends on the chain's finality model and on how much you lose if the transaction disappears. Bitcoin has only probabilistic finality: each extra block makes a reversal exponentially less likely but never impossible, so you pick a depth that fits the amount. Ethereum has economic finality: once a block is behind a finalized checkpoint, reverting it would mean destroying at least a third of all staked ETH, so you wait for finalized rather than counting blocks. Rollups add a layer on top: a fast soft confirmation from the sequencer, then hard finality once their data is final on L1.
Bitcoin: probabilistic finality
A confirmation is a block on top of (and including) your transaction's block. To reverse a transaction with z confirmations, an attacker has to mine a competing chain from before that block and overtake the honest chain. The Bitcoin whitepaper models this as a race and shows that the attacker's chance falls exponentially in z as long as their share of hash rate q is below half.
From the whitepaper's table of the depth z needed to push the attacker's success probability below 0.1%:
Attacker hash share q
Confirmations z
10%
5
20%
11
30%
24
40%
89
That's where the "6 confirmations" folklore comes from. It's a reasonable default for large amounts against a minority attacker, not a magic number. In practice:
0 confirmations means the transaction is only in mempools. Now that full RBF is widely deployed, the sender can easily replace it with a conflicting transaction. Don't release anything of value.
1 confirmation protects against casual double-spends. One-block reorgs from stale blocks still happen from time to time.
More confirmations for larger amounts. Scale the depth with value at risk and with what an attacker could gain by renting hash rate for a short reorg.
A bridge is only as safe as whatever verifies cross-chain messages. Most major hacks came from stolen validator keys, verification bugs, or bad upgrades, not the chains themselves.
Check the depth from your node, and watch for negative confirmations, which mean a conflicting transaction was mined:
bitcoin-cli -rpcwallet=mywallet gettransaction <txid>
# "confirmations": 3 -> in the active chain, 3 deep# "confirmations": -2 -> conflicted: a double-spend is 2 deep
Your deposit handler has to cope with reorgs too. Store the block hash, not only the height, and re-check that it is still in the active chain before crediting.
Ethereum: finalized checkpoints
Proof-of-stake Ethereum groups 12-second slots into 32-slot epochs. Validators vote on the first block of each epoch (the checkpoint). When two-thirds of staked ETH attests, a checkpoint becomes justified. A justified checkpoint followed by another justified one becomes finalized. Under normal conditions a block is finalized within about two to three epochs, on the order of 15 minutes, depending on where in the epoch it landed.
Finalized means that reverting the block would require validators holding at least one-third of total stake to be provably slashed. That is a much stronger guarantee than "N blocks deep", and it doesn't grow with more blocks: a block is either finalized or it isn't.
Nodes expose three useful block tags:
Tag
Meaning
latest
Head of the chain; can be reorged
safe
Very unlikely to reorg under normal network conditions
finalized
Behind a finalized checkpoint; reverting needs mass slashing
Check a deposit against finalized directly instead of counting blocks:
import { JsonRpcProvider } from"ethers";
const provider = newJsonRpcProvider(process.env.RPC_URL);
exportasyncfunctionisFinalized(txHash: string): Promise<boolean> {
const receipt = await provider.getTransactionReceipt(txHash);
if (!receipt || receipt.status !== 1) returnfalse;
const finalized = await provider.getBlock("finalized");
if (!finalized || receipt.blockNumber > finalized.number) returnfalse;
// Make sure the receipt's block is the canonical one at that heightconst canonical = await provider.getBlock(receipt.blockNumber);
return canonical?.hash === receipt.blockHash;
}
If the network stops finalizing (more than a third of validators offline), finalized stops advancing. Your system should pause crediting rather than fall back to latest.
Rollups: soft vs hard finality
On an L2 such as Arbitrum, OP Mainnet or Base, a transaction goes through stages:
Soft confirmation. The sequencer includes it and returns a receipt, usually within a second or two. You're trusting the sequencer not to reorder or drop it. That's fine for UX, but weak for high-value settlement.
Posted to L1. The batch containing it lands in an L1 block. Ordering is now fixed by L1 data, but that L1 block can itself still reorg.
L1-finalized. The L1 block holding the batch is finalized. The L2 transaction's inclusion and order are now as final as Ethereum. On OP Stack chains this is what the L2 node's finalized tag reports, and Arbitrum nodes also expose safe and finalized based on L1.
Withdrawing to L1 is a separate clock. Optimistic rollups make you wait for the challenge window (about a week) before the canonical bridge releases funds. ZK rollups release funds once a validity proof covering the batch is verified on L1.
Other chains
Many chains offer fast deterministic finality through BFT-style consensus (Cosmos SDK chains, Aptos, Sui, and others): once a block is committed, it's final. Solana exposes processed, confirmed and finalized commitment levels. Use finalized for anything irreversible. Whatever the chain, find out what "final" means there and query for it explicitly.
Checklist
Never treat 0-confirmation Bitcoin payments as settled. Scale confirmation depth with the amount at risk.
On Ethereum, credit high-value deposits only once the block is at or below finalized, and verify the block hash.
On rollups, separate UX (sequencer receipt) from settlement (batch finalized on L1) from withdrawals (challenge window or proof).
Store block hashes, handle reorgs, and pause crediting if finality stalls.
BIP-39 encodes random entropy as 12-24 words and stretches them into a seed; BIP-32/44 derive every key from it. Store the words offline on durable media in several places, never digitally.
Bitcoin tracks unspent outputs and wallets compute balances, with implicit fees and change outputs. Ethereum stores a balance and nonce per account, so nonce management replaces coin selection.