General principle
A wallet smart contract stores two critical variables — the public key and the sequence number (seqno
). Together they secure the wallet.
Using the public key, the contract verifies that the request comes from the wallet owner; using seqno
, it protects against replayed transactions (see How replay protection works).
Why wallet contracts don’t store a balance
field
In TON, an account’s balance is not stored inside the smart contract’s persistent data. It is a part of the protocol-level account state, maintained by validators and updated automatically when value is received/sent and when fees are paid.
Therefore, a wallet contract does not and should not keep a separate balance
variable in storage. Doing so would be redundant and could drift out of sync with the actual account state.
From outside, the balance is exposed via RPC and blockchain explorers as part of the public account state.
How ownership verification works
Wallet contracts use the Ed25519 signature scheme. You generate a private key (keep it safe) and a public key (stored in the contract; readable by anyone).ImportantThe public key is not the wallet address. The address is derived from the contract’s
StateInit
and other parameters. See address formats.How replay protection works
Why do we need replay protection?Imagine Alice has 100 TON. Alice sends 10 TON to Bob as a gift. Bob, being sneaky, forwards the exact same message to Alice’s wallet and would receive another 10 TON — unless the wallet prevents replays.
seqno
) that must be unique and strictly increasing for every outgoing transaction. Because seqno
changes, the message hash changes as well, making it impossible to reuse a previous signature.
Role of valid_until
valid_until
is a Unix timestamp embedded into the wallet’s signing message. The wallet rejects any external message if the current time exceeds this value. This limits the lifetime of a signature and prevents an attacker from replaying a captured message indefinitely.
It also makes retries safe: a client may re-send the same signed message until the deadline; after it expires, the client must re-sign with a new valid_until
. Combined with seqno
equality (the contract accepts only the exact current seqno
and increments it on success), this provides robust replay protection.
Subwallet ID
subwallet_id
is a 32‑bit identifier tied to a wallet instance. It is set at wallet creation time and is included in the signing message. On each external call, the wallet verifies that the provided subwallet_id
matches its own; otherwise the message is rejected.
By varying subwallet_id
, you can deploy multiple independent wallet contracts controlled by the same public key, each with its own address and seqno
. This is useful for separating accounts, environments, or business lines while keeping a single keypair.
ImportantChanging
subwallet_id
creates a different wallet address. Funds are not shared across subwallets even if they use the same public key.