Account Information
In this article we will look into how an account is stored in Substrate and how its underlying data structure looks like. We will see that each account is associated with three reference counters, and how they are used to manage its lifecycle. If your runtime stores data associated with accounts on-chain, it is important to understand how to use these reference counters in your runtime logic.
Account StorageMap
In Substrate, the entry point of account information is stored in
frame-system pallet,
extracted below.
/// The full account information for a particular account ID.
#[pallet::storage]
#[pallet::getter(fn account)]
pub type Account<T: Config> = StorageMap<
  _,
  Blake2_128Concat,
  T::AccountId,
  AccountInfo<T::Index, T::AccountData>,
  ValueQuery,
>;
Account storage map takes in five type parameters, of which the first argument is used in macro
expansion. Then it specifies using Blake2_128Concat as the hashing algorithm, and mapping
T::AccountId as key over AccountInfo<T::Index, T::AccountData> struct. See
StorageMap API doc
for details.
AccountInfo structure
Looking further down in the source code, AccountInfo struct is defined as:
#[derive(Clone, Eq, PartialEq, Default, RuntimeDebug, Encode, Decode)]
pub struct AccountInfo<Index, AccountData> {
  /// The number of transactions this account has sent.
  pub nonce: Index,
  /// The number of other modules that currently depend on this account's existence. The account
  /// cannot be reaped until this is zero.
  pub consumers: RefCount,
  /// The number of other modules that allow this account to exist. The account may not be reaped
  /// until this and `sufficients` are both zero.
  pub providers: RefCount,
  /// The number of modules that allow this account to exist for their own purposes only. The
  /// account may not be reaped until this and `providers` are both zero.
  pub sufficients: RefCount,
  /// The additional data that belongs to this account. Used to store the balance(s) in a lot of
  /// chains.
  pub data: AccountData,
}
Every account has an AccountInfo consisting of a nonce indicating the number of transactions the
account has sent, three reference counters, and an AccountData structure which can be configured
to hold different kinds of data, further explained below.
Account reference counters
Let's look deeper into the three reference counters, consumers, providers, and sufficients.
These counters track references of an account being depended upon within runtime, for example when
we store data under a map controlled by an account. With these counters we are telling Substrate to
not destroy these accounts, which is usually triggered when users trying to transfer their
existential deposit away.
consumersindicates how many times modules depend on this account. An example of using this counter is inSessionpallet when an account setting its session key(s) prior of becoming a validator [1].providershas to be greater than zero beforeconsumercan be incremented. See below.providersindicates if an account is active/ready to be depended upon. One usage example is that the counter is incremented when a new account is created with more than the existential deposit [2].consumersandprovidersare designed to be used together.providerstells Substrate pallets not to store data about that account until it is active (i.e.providers> 0), andconsumerstells Substrate not to remove an account until data about the account is removed in all pallets (i.e.consumers== 0). This is to keep users accountable for their data stored on-chain. If users want to remove their accounts and get back the existential deposit, they need to remove the dependencies from those on-chain pallets, such as clearing data stored on-chain for those pallets, which decrementconsumerscounter. Pallets also have cleanup functions to decrementprovidersto mark the account as deactivated within the pallet-managed scope. When the accountprovidersreaches 0, with the prerequisite thatconsumershas reached 0 by this point, this account is considered deactivated by all on-chain pallets.sufficientsindicates if there are any reasons this account is self-sufficient to exist by itself. An example of using this counter is inAssetspallet when an account has sufficient amount of certain assets but not owning any native account balance [3].
Runtime developers can update these counters via inc_consumers(), dec_consumers(),
inc_providers(), dec_providers(), inc_sufficients(), and dec_sufficients() exposed by
frame-system. Each increment call of a certain counter should accompanied by a corresponding decrement call of the counter in an account life cycle, else it is a design bug.
There are also three query functions to ease usage on these counters:
can_inc_consumer()to check if an account is ready to be used (providers> 0);can_dec_provider()to check if an account is no longer referenced in runtime whatsoever (consumers== 0) before decrementingprovidersto 0; andis_provider_required()to check if an account has outstanding consumer references (consumers> 0).
Refer to frame-system API doc
for details.
AccountData trait and implementation
Back to the AccountInfo struct. The last piece of data in AccountInfo is AccountData. It can be any struct as long as it satisfies the associated type AccountData trait bound defined in frame-system::pallet::Config trait. Out of the box Substrate runtime configures it to be AccountData struct defined in pallet-balances as shown below.
pub struct AccountData<Balance> {
  /// Non-reserved part of the balance. There may still be restrictions on this, but it is the
  /// total pool what may in principle be transferred, reserved and used for tipping.
  ///
  /// This is the only balance that matters in terms of most operations on tokens. It
  /// alone is used to determine the balance when in the contract execution environment.
  pub free: Balance,
  /// Balance which is reserved and may not be used at all.
  ///
  /// This can still get slashed, but gets slashed last of all.
  ///
  /// This balance is a 'reserve' balance that other subsystems use in order to set aside tokens
  /// that are still 'owned' by the account holder, but which are suspendable.
  pub reserved: Balance,
  /// The amount that `free` may not drop below when withdrawing for *anything except transaction
  /// fee payment*.
  pub misc_frozen: Balance,
  /// The amount that `free` may not drop below when withdrawing specifically for transaction
  /// fee payment.
  pub fee_frozen: Balance,
}
It contains:
Free balance
free. The portion of a balance that is not reserved. The free balance is usually what matters for most operations.Reserved balance
reserved. Reserved balance still belongs to the account holder, but is suspended. Reserved balance can be slashed, but only after all the free balance has been slashed. The total balance of an account is the sum of its free balance and reserved balance.Frozen balance, split into
misc_frozenandfee_frozen, represents balance that free balance cannot drop below.fee_frozenare specifically for transaction fee payment andmisc_frozenfor everything else. The actual frozen balance is the max of these two, and they are set when accounts are locked for transactions.
Conclusion
By now, you have a clear picture how account data is stored in Substrate. You can dig deeper by
looking into the frame_system::AccountInfo API doc
and pallet_balances::AccountData API doc.