Address states
This article describes the four possible states of a smart contract address on TON Blockchain. Understanding these states is crucial for accurately predicting transaction outcomes and ensuring the correct deployment.
State definitions
Each address exists in one of the following states:
- nonexist: the default state for addresses with no transaction history or that were deleted. Contains no code, data, or balance. All 2256 addresses start in this state. An address returns to this initial state when its
storage_fees_due
exceeds 1 TON or the outgoing message flag is set to 32. - uninit: holds a balance and metadata, but no code and persistent data. An address enters this state when it receives funds. It cannot execute logic but retains funds until the contract code is deployed. An address becomes uninit when a no-bounce message with a nonzero
balance
but withoutstate_init
is sent to anonexist
address. - active: contains code, data, and a balance. Fully deployed and operational, capable of processing messages. An address becomes active when a message with its
state_init
is sent to anuninit
ornonexist
address. Note that the hash ofstate_init
must match the address for deployment. - frozen: occurs when
storage_fees_due
exceeds 0.1 TON. Only the hashes of the previous code and data cells are preserved. While frozen, the contract cannot execute. To unfreeze, send a message with the validstate_init
and sufficient funds for storage fees. Recovery is complex; avoid reaching this state. A project to unfreeze addresses is available here.
State transitions
An address state determines how the network handles incoming messages. We present here a diagram that describes all potential changes in the account state during the receipt of internal or external messages.
Diagram
In the diagram below, there are four nodes representing the four different address states. Each arrow and loop corresponds to a change in the address state at the end of a given transaction. The parameters in the blocks above the arrows (and loops) briefly describe what caused the transaction and also contain some fields that affect the change in the address state.
So, let's look at what changes can occur to a nonexist
address depending on the messages that come to it.
- Receiving external messages: no changes.
- Receiving internal messages:
- With valid
state_init
: the contract is deployed on its address that becomesactive
before processing the message. If there is novalue
in the message, the computation phase is skipped due to thecskip_no_gas
reason. - Without
state_init
or with an invalid one: if the message is bounceable, then it returns to the sender, and the address state isn't changed. Otherwise, with novalue
in the message, the address state isn't changed. Finally, with some TONs it becomesuninit
.
- With valid
With the diagram Legend below, you can inspect all possible changes in the address state when receiving messages with different parameters.
Legend
- type: message type.
- any;
- internal;
- external;
- bounce:
bounce
flag of an internal message.- any;
- true;
- false;
- value: an amount of TON in a message.
- any;
- 0;
- > 0.
- StateInit: StateInit structure.
- any;
- none;
- invalid: the address computed from a given
state_init
does not match the recipient address; - valid: the computed address matches a recipient address;
- valid last state: must be
state_init
of the last successful transaction before the address change to frozen.
- storage_fees_due: the number of storage fees that were charged but could not be conducted. In the diagram this field indicates
storage_fees_due
after Storage phase.- < 0.1;
- [0.1, 1);
- < 1;
- >= 1.
- send_dest_if_zero: is there any out-going message with flag 32 in the action phase?
- any;
- false;
- true;
- invalid: there was no Action phase.
- zero_bal_after_dest_act: did the account balance become zero when sending some of the messages with flag 32? This field is meaningful only if there's at least one such message during Action phase.
- any;
- false;
- true.
- action_phase_is_successful: was Action phase successful?
- false;
- true.
Key points
We additionally review some important points regarding the states except nonexist
.
Sending to uninit
address:
- Messages of any type without
state_init
: changes tononexist
if its storage_fees_due exceeds 1 TON. Although such a case is really rare, you need to keep this in mind. - Internal message with valid
state_init
: changes toactive
.
Sending to frozen
address:
- Messages of any type: changes to
nonexist
if itsstorage_fees_due
exceeds 1 TON. - Internal message with valid
state_init
: changes toactive
if itsstorage_fees_due
becomes less then 0.1 TON.
Sending to active
address:
- Messages of any type: changes to
nonexist
if itsstorage_fees_due
exceeds 1 TON. It also changes tofrozen
if itsstorage_fees_due
exceeds 0.1 TON. - Messages of any type: if in the action list there is an outgoing message with the flag 32 but the action phase was unsuccessful or the balance after this action is positive, the address state doesn't change.
- Internal message with any
state_init
: Newstate_init
will be ignored and therefore doesn't change the address state.
Deployment strategy: the standard practice for deploying a wallet is to first send a non-bounceable message with Toncoin to its address. This transitions the address to the uninit
state. The wallet owner can then deploy the contract in a subsequent transaction, using the pre-funded balance.
Protection against errors: standard wallets and applications manage these complexities by automatically setting the bounce
flag based on the state of the destination address. Developers of custom applications must implement similar logic to prevent fund loss.
Summary
- The address state (
nonexist
,uninit
,active
,frozen
) defines behavior. - Correct handling of
state_init
and thebounce
flag is crucial for successful deployment and avoiding unintended fund transfers. - There are many cases when the address state can become
nonexist
orfrozen
. Keep track of the amount of TON on the account balance! - Each new
state_init
is ignored when the address state is active.