What is the account status?
The account status is a formal indicator of what actions can occur with an account, what it can store, whether it contains a contract code or not. 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 accumulate 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 execute. To unfreeze, send a message with the valid
state_init
and 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. Beside 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 the deployment. So, the uninit
account always has the positive balance and some addtional information for which it must pay storage fee.
Sending an internal message with 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 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 valid
state_init
and sufficient value: the contract is deployed on its address that becomesactive
before processing the message. - Without/with invalid
state_init
or with insufficient value: if the message is bounceable, then it returns to the sender, and the account status isn’t changed. Otherwise, with novalue
in the message, the account status isn’t changed. Finally, it becomesuninit
if received a validstate_init
but insufficient nanotons or ifstate_init
is absent or invalid.
- With valid
Legend
type
: message type.- any;
- internal;
- external;
bounce
:bounce
flag of an internal message.- any;
- true;
- false;
value
: an amount of nanotons 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 account change to 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 conducted. In the diagram this field indicatesstorage_fees_due
after Storage phase.- 0;
- < 0.1;
- < 1;
- >= 0.1;
- >= 1.
send_dest_if_zero
: is there any out-going message with flag 32 in 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 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 tononexist
if its balance becomes zero. - Messages of any type with valid
state_init
: changes toactive
if the balance is at least 40000 nanotons.
frozen
account:
- Messages of any type: Changes to
nonexist
if itsstorage_fees_due
exceeds 1 TON and the balance is zero. - Internal message with valid
state_init
(non-bounceable): changes toactive
if itsstorage_fees_due
becomes zero. - Internal message with valid
state_init
(bounceable): changes toactive
with the same debt and the account balance equals message’s balance minus compute and action fees.
active
account:
- Messages of any type: changes to
frozen
if itsstorage_fees_due
exceeds 0.1 TON and the account’s state has ever changed. - Messages of any type: changes to
uninit
if itsstorage_fees_due
exceeds 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_init
will 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 statuses (
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 account status can become
nonexist
orfrozen
. Keep track of the amount of TON on the account balance! - Each new
state_init
is ignored when the account status is active.