What is the account status?
The account status is a formal indicator of what actions can occur with an account, what it can store, and whether it contains a contract code. In other words, it is one of the main factors determining the behavior of a given account during a transaction. The account status at the beginning and end of a transaction is recorded in the corresponding TL-B block in the fieldsorig_status and end_status. Thus, it allows developers to always view the current status of an account before sending it a message and restore the history of its status changes.
Status variety
Each account exists in one of the following statuses:- nonexist: the default status for accounts with no transaction history or that were deleted. Contains no code, data, or balance. All 2256 accounts start in this status.
- uninit: holds a balance and metadata, but no code and persistent data. It cannot execute logic but retains funds (and accumulates the storage fee) until the contract code is deployed.
- active: contains code, data, and a balance. Fully deployed and operational, capable of processing messages.
- frozen: occurs when the storage debt of an active account exceeds 0.1 TON. Only the hashes of the previous code and data cells are preserved. While frozen, the contract cannot be executed. To unfreeze, send a message with the valid
StateInitand sufficient funds for storage fees. Recovery is complex; avoid reaching this state. A project to unfreeze accounts is available here.
Why exactly these four statuses?
Although the need for theactive and nonexist statuses is obvious, the purpose of the uninit and frozen statuses is not immediately clear.
nonexist vs uninit:
As was mentioned above, each account starts in the nonexist status. Besides code and persistent data, in this status, an account also has no metadata, so it does not accumulate the storage fee. At the same time, the uninit status of an account indicates that some actions were performed with it and, possibly, it is prepared for deployment. So, the uninit account always has a positive balance and some additional information for which it must pay a storage fee.
Sending an internal message with a valid state_init and proper value to nonexist account results in its deployment and the status changes to active. The key point is that the deployment occurs during the compute phase, which requires a suitable number of nanotons to run. But often you want to be able to deploy an account through an external message, to which you can also attach state_init, but it is impossible to attach value! This is also the purpose for which uninit exists. You can initially transfer the balance to the account via an internal message, notifying the rest of the blockchain participants of your intention to deploy an account in the future. And only then, including through an external message, to deploy.
frozen vs uninit:
When the active account’s storage debt exceeds 0.1 TON, it becomes frozen or uninit. It is possible to restore the account from these statuses by paying off the debt and attaching state_init. If the account code and its persistent data have not changed during the lifetime of the account, then there is no problem restoring it from uninit. But what if they have changed? Since the uninit status does not allow you to store any information about the account’s history, its last state will be lost forever. To prevent such situations, the frozen status exists.
The main difference between the uninit and frozen statuses is that in addition to metadata, the frozen account contains hash of the last account state. Thus, it becomes possible to restore the last state of an account before it was frozen by sending a state_nint to it, whose hash matches the one recorded on the account.
Status transitions
We present here a diagram that describes all potential changes in the account status during the receipt of internal or external messages.Diagram
In the diagram below, there are four nodes representing the four different account statuses. Each arrow and loop corresponds to a change in the account status 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 account status.nonexist account depending on the messages that come to it.
- Receiving external messages: no changes.
- Receiving internal messages:
- With a valid
state_initand sufficient value: the contract is deployed on its address that becomesactivebefore processing the message. - Without/with invalid
state_initor with insufficient value: if the message is bounceable, then it returns to the sender, and the account status isn’t changed. Otherwise, with novaluein the message, the account status isn’t changed. Finally, it becomesuninitif it received a validstate_initbut insufficient nanotons or ifstate_initis absent or invalid.
- With a valid
Legend
type: message type.- any;
- internal;
- external;
bounce:bounceflag of an internal message.- any;
- true;
- false;
value: an amount of nanotons in a message.- any;
- 0;
- > 0.
state_init:StateInitstructure.- any;
- none;
- invalid: the address computed from a given
state_initdoes not match the recipient address; - valid: the computed address matches a recipient address;
- valid last state: must be
state_initof the last successful transaction before the account becomes frozen.
balance: The account balance in nanotons after Storage phase of the transaction.- 0;
- > 0;
- < 40000;
- >= 40000;
- (0, 40000).
storage_fees_due: the number of storage fees that were charged but could not be collected. In the diagram, this field indicatesstorage_fees_dueafter the Storage phase.- 0;
- < 0.1;
- < 1;
- >= 0.1;
- >= 1.
send_dest_if_zero: is there any outgoing message with flag 32 in Action phase?- any;
- false;
- true;
- invalid: there was no Action phase.
zero_bal_after_dest_act: Whether the account balance became zero when sending some of the messages with the 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.
account_state_changed: has the account’s state changed during its lifetime?- false;
- true.
Key points
We additionally review some important points regarding the statuses exceptnonexist.
Sending to uninit account:
- Messages of any type without
state_init: changes tononexistif its balance becomes zero. - Messages of any type with valid
state_init: changes toactiveif the balance is at least 40000 nanotons.
frozen account:
- Messages of any type: Changes to
nonexistif itsstorage_fees_dueexceeds 1 TON and the balance is zero. - Internal message with valid
state_init(non-bounceable): changes toactiveif itsstorage_fees_duebecomes zero. - Internal message with valid
state_init(bounceable): changes toactivewith the same debt, and the account balance equals the message’s balance minus compute and action fees.
active account:
- Messages of any type: changes to
frozenif itsstorage_fees_dueexceeds 0.1 TON and the account’s state has ever changed. - Messages of any type: changes to
uninitif itsstorage_fees_dueexceeds 0.1 TON and the account’s state has never changed. - Messages of any type: if in the action list there is an outgoing message with the flag 32 but Action phase was unsuccessful or the balance after this action is positive, the account status doesn’t change.
- Messages of any type with any
state_init: newstate_initwill be ignored and therefore doesn’t change the account status.
uninit status. 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 status of the destination account. Developers of custom applications must implement similar logic to prevent fund loss.
Summary
- The account status (
nonexist,uninit,active,frozen) defines behavior. - Correct handling of
state_initand thebounceflag is crucial for successful deployment and avoiding unintended fund transfers. - There are many cases when the account status can become
nonexistorfrozen. Keep track of the amount of TON on the account balance! - Each new
state_initis ignored when the account status is active.