seqno
Sequence number on a TON wallet: 32-bit counter / nonce the contract increments on every outgoing transaction to defend against replay attacks.
Aliases: sequence number, ton seqno
seqno (sequence number) is a 32-bit counter stored in every TON wallet’s data cell, incremented by 1 after every outgoing transaction. Signed messages include seqno in the body; if the seqno in the signed message doesn’t equal the current one in storage, the contract rejects the transaction with THROWIFNOT 33.
Why it matters
- Replay protection: an attacker can’t replay your old transaction — its seqno is already used.
- Ordering guarantee: transactions execute strictly in ascending seqno order.
SDK usage
@ton/ton SDK reads seqno from the contract before sending:
const seqno = await walletContract.getSeqno();
await walletContract.sendTransfer({ seqno, secretKey, messages: [...] });
Wallet V5 introduced an extended model: extensions can have their own seqno-namespace and sign independently of the main owner.
What to do if a transaction “gets stuck”
If sendTransfer returned but no transaction shows up in the explorer for 30+ seconds, the possible causes:
- External message rejected by the validator (low fee, bad signature). The seqno in storage did not increment — you can safely retry with the same seqno.
- Network is congested — message is in the queue, wait another 15-30 seconds.
- Race condition with another signature — if a parallel call (e.g. a second wallet window) sent a transaction with the same seqno, yours will be rejected with throw 33.
Check via API:
const stored = await walletContract.getSeqno();
if (stored === seqno) {
// transaction didn't go through — safe to retry
} else if (stored > seqno) {
// transaction was included (or a parallel send happened)
}
Never “guess” a seqno manually — always read the fresh value before each signature.
Batch sends
One seqno covers a single external message, but inside it you can ship up to 4 outgoing messages (Wallet V4) or up to 255 (Wallet V5 batch mode). They all execute atomically within one transaction; if any one fails in the compute phase — the whole batch reverts.
Wallet V5 namespaces — in detail
In Wallet V5 the data layout is richer: alongside the main signature_auth_seqno there are separate counters for every signed extension. This enables:
- Multi-sig with multiple keys, no race conflicts.
- Delegating sign authority to a dApp (via TON Connect 2) within a limited namespace.
- Gasless flows: a relayer pays gas, the user only signs the operation body.
When working with a V5 wallet, always use getSeqno({ extensionId }) if you are an extension, otherwise you’ll catch a 33-throw from someone else’s namespace.