# Processed, safe, finalized: what "final" means on Solana, Ethereum, Stellar, TON and sequencer L2s

> Every chain has a word for "this won't change any more", and the words do not line up. A mapping of the five models, what each API lets you ask for, and how to pick a threshold for a deposit versus a UI.

- Canonical: https://triport.io/blog/finality-commitment-levels-across-chains
- Published: 2026-09-22
- Updated: 2026-09-22
- Author: Triport Engineering
- Category: Fundamentals
- Networks: Solana, Ethereum, Stellar, TON, Robinhood Chain

## TL;DR

- "Confirmed" is not portable: on Solana it is a commitment level below finalized, on Ethereum there is no such tag (you choose latest, safe or finalized), Stellar has one state (in a closed ledger or not), TON is final once a masterchain block references the shard block, and a sequencer's pending view is not a stage at all.
- What you can ask the API for differs too: Solana methods take a commitment parameter, Ethereum reads take a block tag, and Triport's Robinhood Chain contract supports latest, earliest and pending but not safe or finalized, so confirmation there has to be depth-based.
- Pick the threshold per use: show activity in a UI at the earliest level the chain offers, credit deposits only at the level your product can defend if the chain reorganises, and store which level every record was read at.

Multichain code tends to grow a helper called something like `waitForConfirmation(tx)`. On the first chain it wraps one concept. By the third chain it wraps three different concepts behind one name, and the bugs live in the gap. This post lays the five models side by side — Solana, Ethereum, Stellar, TON and a sequencer-based L2 (Robinhood Chain) — and ends with a way to choose thresholds that does not pretend they are the same.

It deliberately says nothing about how long each level takes. The question here is what each level *means*, and what you are entitled to conclude when you read data at it.

## Why "confirmed" is not portable

Three different things get called "confirmed" in practice:

- **Seen by a node.** The transaction is in a block the node has processed, but the block could still be dropped.
- **Agreed by the network under its fork-choice rule.** Very unlikely to be dropped, but the protocol still allows it.
- **Irreversible under the protocol's own rules.** Reverting it would require the protocol's safety assumptions to fail.

Solana names all three. Ethereum names the second and third with block tags. Stellar is built so that the first answer its API gives you is already the third. TON makes the step from the first to the third an explicit reference you can check. A sequencer L2 adds a fourth, earlier thing — "the sequencer currently shows it" — that is not any of the above.

## Solana: processed, confirmed, finalized

Solana read methods take a `commitment` parameter with three values; [`getTransaction`](https://triport.io/docs/sol/json-rpc/getTransaction), for example, accepts `processed`, `confirmed` or `finalized`.

- **processed**: in Solana's RPC documentation, "the node's most recent processed block. This is the newest view, but it can still be rolled back." Triport's streaming reference calls this level revertible.
- **confirmed**: "a block directly voted on by a supermajority of stake, meaning more than two-thirds of the network's active stake." It is the default commitment on Triport's Solana streams.
- **finalized**: "a block the cluster recognizes as finalized with maximum lockout." Triport's `rootSubscribe` reference describes a rooted slot as one "finalized by supermajority vote" that "can never be rolled back", and the stream reference labels `finalized` irreversible.

The commitment you pass changes the answer, not only its reliability. `getTransaction` for a signature that exists but has not reached the requested commitment returns `null`, which is a successful response, not an error. The method's own notes suggest polling at `confirmed` (or `processed`) for early visibility and re-reading at `finalized` once settled. Code that treats `null` at `finalized` as "this transaction does not exist" will mis-classify every recent payment.

## Ethereum: latest, safe, finalized

Ethereum JSON-RPC reads take a block parameter, which is either a block number or a named tag. [`eth_getBlockByNumber`](https://triport.io/docs/eth/json-rpc/eth_getBlockByNumber) accepts five:

| Tag | Meaning |
|---|---|
| `latest` | The most recent block the node has imported |
| `earliest` | The genesis block |
| `pending` | The pending state and transactions not yet in a block |
| `safe` | The latest block considered safe by the fork-choice rule |
| `finalized` | The latest block that has reached finality |

So Ethereum's three rungs are `latest` (seen), `safe` (agreed under fork choice), and `finalized` (irreversible under the protocol). There is no tag spelled "confirmed", and "N confirmations" — counting blocks on top of yours — is a policy you apply to `latest`, not something the node reports.

Two practical consequences. A balance read at `latest` and a balance read at `finalized` can legitimately differ, and both are correct answers to different questions. And `pending` is not a rung on this ladder at all: it is the node's view of what might go into the next block.

## Stellar: a ledger closes, or it doesn't

Stellar has no commitment parameter and no block tags, because it has one relevant state. In every Stellar Consensus Protocol round, the network reaches consensus on which transaction set to apply to the last closed ledger; a transaction is either in a closed ledger or it is not. SCP, in Stellar's own words, "prioritizes fault tolerance and safety over liveness" — the protocol is designed to stop closing ledgers rather than close conflicting ones.

For an application this simplifies the ladder to one rung, with one trap: **included is not the same as succeeded.** A transaction can be included in a closed ledger and still have failed, which is why Horizon transaction records carry a `successful` flag and why Triport's guide to [monitoring Stellar payments](https://triport.io/docs/stellar/guides/monitor-payments-with-horizon-sse) tells you never to credit without checking it. The same guide adds the other half: `successful: true` means the transaction succeeded, not that your business rules are satisfied.

## TON: the masterchain reference

TON splits work across a masterchain and shardchains, and finality is defined by the link between them. TON's glossary describes the masterchain as "the main chain that references shard and workchain blocks; a shard block is finalized once a Masterchain block references it." Its payment-processing guide says the same from the application side: once a transaction from a shardchain appears in a masterchain block, it becomes irreversible, and when monitoring payments you should verify masterchain inclusion rather than trusting the shardchain alone.

On Triport's [TON surface](https://triport.io/docs/ton) (product overview: [TON RPC](https://triport.io/ton-rpc)) this is something you check, not something you pass: the 13 read-only methods take `seqno`, shard and logical-time parameters, and there is no commitment argument. A deposit flow therefore reads the account's transactions and confirms that the shard block holding each one is referenced by a masterchain block before crediting.

## L2 sequencers: pending is not a stage

Sequencer-based L2s add a view that the other chains do not have: what the sequencer shows *right now*. Triport's Robinhood Chain docs are blunt about it: `pending` "is a snapshot of what the sequencer presents right now. It is not a durable stage, public mempool, or finality signal." A pending block or transaction can disappear, be replaced or move into another block; its hash may be `null` and may later change.

The second difference is what the API lets you ask for. On Triport's [Robinhood Chain contract](https://triport.io/docs/robinhood/pending), the supported block tags are `latest`, `earliest` and `pending`; `safe` and `finalized` are not part of the contract. The [wallet backend guide](https://triport.io/docs/robinhood/guides/build-a-wallet-backend) draws the conclusion: confirmation has to be depth-based — record the receipt's `blockNumber`, compare it with the head from `eth_blockNumber`, treat the transaction as settled only after the number of blocks your product requires, and reconcile by hash against later canonical blocks. A shared "read at finalized" helper carried over from Ethereum will fail on this contract, and the guide calls that failure the good outcome; the bad one is a silent fallback to `latest` while the UI claims finality.

## Mapping table

| | Earliest signal | Network-agreed | Irreversible | How you ask |
|---|---|---|---|---|
| Solana | `processed` (can be rolled back) | `confirmed` (supermajority of stake voted) | `finalized` (rooted, maximum lockout) | `commitment` parameter |
| Ethereum | `latest` | `safe` (fork-choice rule) | `finalized` | Block tag |
| Stellar | — | — | In a closed ledger (check `successful`) | Read the ledger or transaction record |
| TON | In a shard block | — | Shard block referenced by a masterchain block | Check masterchain inclusion yourself |
| Robinhood Chain on Triport | `pending` (a sequencer snapshot, not a stage) | `latest` plus your block-depth rule | Not exposed as a tag | `latest` / `pending` tags; depth policy |

Read the table by column, not by row. The "Irreversible" column is the only one where the chains agree on intent, and even there the mechanism differs: a tag you request, a commitment you pass, a record you read, a reference you verify, or a rule you apply.

## Picking thresholds for deposits vs UI

A useful policy separates *showing* from *acting*:

1. **UI feedback** can use the earliest signal each chain offers: Solana `processed` or `confirmed`, Ethereum `latest`, a Robinhood Chain receipt. Label it as unconfirmed if your product cares about the difference.
2. **Crediting a deposit** should wait for the level you could defend if the chain reorganised: Solana `finalized`, Ethereum `finalized` (or `safe`, if your risk policy says so), a Stellar transaction in a closed ledger with `successful: true`, a TON transaction whose shard block the masterchain references, and on Robinhood Chain a depth you chose and wrote down.
3. **Store the level with the data.** Every balance or transaction you persist should record the commitment, tag or depth it was read at, so a later audit can tell a finalized record from an early one.
4. **Never convert one chain's level into another's name.** A `status: "confirmed"` column that means `finalized` on one chain and `latest` on another is the bug this post is about.

## When this is the wrong approach

- **You need settlement guarantees across a bridge.** Finality of the destination chain says nothing about the source chain or the bridge contract; that is a separate trust analysis.
- **You treat L1 finality as L2 finality.** How and when an L2's state becomes final on its L1 is defined by that rollup's own design and is outside the scope of the tags covered here.
- **You rely on time instead of state.** "Wait N seconds" is not a finality rule on any of these chains; read the level.

## Sources

1. Triport, [`getTransaction` on Solana](https://triport.io/docs/sol/json-rpc/getTransaction): commitment values, `null` below the requested commitment, re-read at `finalized`. Triport streaming reference (`/ws/sol-stream`): `processed` revertible, `confirmed` default, `finalized` irreversible. Triport `rootSubscribe` reference: rooted = finalized, never rolled back. Solana RPC documentation, commitment levels — https://solana.com/docs/rpc, read 2026-09-22.
2. Triport, [`eth_getBlockByNumber` on Ethereum](https://triport.io/docs/eth/json-rpc/eth_getBlockByNumber): the five block tags and their meanings.
3. Stellar Docs, Ledgers — https://developers.stellar.org/docs/learn/fundamentals/stellar-data-structures/ledgers; Stellar Consensus Protocol — https://developers.stellar.org/docs/learn/fundamentals/stellar-consensus-protocol; both read 2026-09-22.
4. Triport, [Monitoring Stellar payments with Horizon SSE](https://triport.io/docs/stellar/guides/monitor-payments-with-horizon-sse): `successful`, included vs succeeded.
5. TON Docs, Glossary — https://docs.ton.org/foundations/glossary; Payment processing overview — https://docs.ton.org/applications/payments/overview; both read 2026-09-22.
6. Triport, [TON overview](https://triport.io/docs/ton): 13 read-only methods, parameter shapes.
7. Triport, [Robinhood Chain pending data](https://triport.io/docs/robinhood/pending) and [wallet backend guide](https://triport.io/docs/robinhood/guides/build-a-wallet-backend): `pending` is not a stage; `safe` and `finalized` are not part of the contract; depth-based confirmation.

## Try it

- Reference: [eth_getBlockByNumber — latest, safe and finalized tags](https://triport.io/docs/eth/json-rpc/eth_getBlockByNumber)
- Network: [Stellar RPC — Horizon and Soroban on one key](https://triport.io/stellar-rpc)
