Between "the node accepted it" and "it happened" there are two stages, and conflating them is the origin of the most expensive integration bug there is: crediting twice.
Each stage carries its own guarantee. None of them implies the next, which is why there are six and not one.
Type, amount, destination, nonce and timestamp. The nonce comes from the node; the timestamp is outside consensus beyond having to be positive.
let spec = TxSpec::nova("TRANSFER", 5 * UNIT, nonce, agora_ms())
.para("E7DEST…9A02");
// monta + assina + verifica LOCALMENTE (mesmo caminho que o nó aplicaria)
let tx = cliente.montar(spec)?;The signature covers the canonical payload, which excludes the signatures themselves and the id. The id derives from the payload — re-signing does not produce a new id. The private key never leaves your machine.
POST /tx returns 200 with the verdict in the body. accepted: true means exactly one thing: this node's mempool took the transaction.
curl -s -X POST https://eavscan.com/tx \
-H 'Content-Type: application/json' \
--data @tx.json
{ "accepted": true, "id": "0x8c1f…" }The slot producer orders the mempool by (nonce, timestamp) and simulates execution. A transaction with a nonce gap waits; a permanently invalid one is dropped.
Once /tx/:id starts returning blockHeight, the transaction executed. A reorg can still undo it while the block is not finalized.
let bloco = cliente.aguardar_confirmacao(&id, Duration::from_secs(30))?;
println!("bloco {} · {}", bloco.block_height, bloco.block_hash);finalizedHeight ≥ blockHeight is the BFT guarantee: 2/3+1 of the validators have already produced on top. Nothing below that is reverted.
let estado = cliente.status()?;
let finalizada = estado["finalizedHeight"].as_i64().unwrap_or(-1);
if finalizada >= bloco.block_height as i64 {
// irreversível por BFT: nenhum reorg pode desfazer daqui para trás
}Three states, three different guarantees. Pick the one that matches the decision your code makes.
The mempool is local to each node, bounded and time-limited. A transaction forgotten there is not pending forever.
5 000Cap on resident transactions. When full, the node refuses new submissions.21 600 000 · 6 hLifetime in the mempool. It reaches the nonce gap, which nonce-based pruning never would.500Cap on transactions per block.(nonce, timestamp)Global, not per sender — but a single sender's nonce order is always respected.The signed fields, the signature fields and the id.
The envelope field by field and all 58 protocol types live in Transactions.