Solana token program (source code) is among the most frequently executed Solana smart contracts. Its program id: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
In this article, we elaborate on the SPL tokens and introduce the internals of those most commonly used instructions in the token program:
These instructions are implemented with three important data structures:
The InitializeMint instruction is the first instruction to call before anything else in the SPL token mint process. It will invoke the function _process_initialize_mint to initialize a mint account (on line 36 mint_info supplied as the first account in the accounts vector):
The mint account’s data is deserialized into mint of the Mint data type, and then checked for re-initialization error:
The Mint struct has five attributes:
Finally, the mint account is initialized by setting its mint_authority , decimals , is_initialized flag true, and an optional freeze_authority.
Note that in the initialization code above, the mint’s supply is not set but has a default value 0.
The instruction will invoke the function _process_initialize_account to initialize a token account (on line 90 new_account_info supplied as the first account in the accounts vector):
Similar to InitializeMint, the token account’s data is deserialized into account of the Token data type, and then checked for re-initialization error:
The Token struct has eight attributes:
Finally, the token account is initialized by setting its mint, owner, close_authority (default None), delegate (default None), delegated_amount (default 0), state (Initialized), is_native flag and amount(i.e. the amount of tokens this account holds):
Note that when the token mint is the native mint, i.e., WSOL (the wrapped sol, program_id: So11111111111111111111111111111111111111112), is_native is true and amount is initialized to the amount of lamports (minus rent) in the token account.
After a mint and a token account have been initialized, the MintTo instruction can be called to mint tokens by function process_mint_to:
The MintTo instruction takes three user-supplied accounts:
It also takes two user-supplied instruction data:
Note that there are several validity checks for the MintTo instruction:
Finally, if the MintTo instruction is processed successfully, the destination account’s token amount (destination_account.amount) and the mint’s supply (mint.supply) will both be incremented by amount :
The Burn instruction is (mostly) opposite to MintTo . It calls the function process_burn to burn a certain amount of tokens from a source token account:
If the Burn instruction is successful, the source account’s token amount (source_account.amount) and the mint’s supply (mint.supply) will both be decremented by amount :
A main difference is that the signed authority account is not the mint’s authority, but the source account’s owner or delegate. For the latter, the logic is slightly complex:
The Transfer instruction will invoke the function process_transfer to transfer a certain amount of token from a source account to a destination account:
This function has several important validity checks:
Note: source_account may be the same as destination_account . If that’s the case, then it is a self-transfer, and the function simply returns Ok. In other words, self-transfer is allowed by the token program.
Finally, if the Transfer instruction is successful, the source_account’s token amount is decremented by amount and the destination_account’s token amount is incremented by amount :
Note: For native token accounts (i.e., the token mint is WSOL), in order to transfer SOL directly using the token program, the token amount should be “sync-ed” with the amount of lamports in the account.
Thus, the lamports of source_account and destination_account must also be updated accordingly:
For a native token account, when the token amount is less than the amount of lamports in the account (possibly caused by a system_instruction transfer of lamports to the token account), the token program has a SyncNative instruction to sync these two values:
In the above, the native account’s amount is updated to the amount of lamports in the account minus rent (line 768).
The Approve instruction will invoke the function process_approve to set a delegate account for a source account and a delegate amount by the source account’s authority:
Note that a token account allows only one delegate at a time.
The Approve instruction will override the previous delegate and the delegate amount set by the last Approve instruction. Even if the previous delegate amount is not spent or only spent partially, it will still be overwritten.
The Revoke instruction will call the function process_revoke to set a source account’s delegate to None and the delegate amount to 0:
The FreezeAccount and ThawAccount instructions will call the function process_toggle_freeze_account to set a source account’s state to Frozen and Initialized respectively:
Note that this instruction must be signed by the freeze_authority of the mint account (line 716 mint_info). It will fail if the mint’s free_authority is not set.
The CloseAccount instruction will call the function process_close_account to close a token account (source_account) and transfer all its lamports to another token account (destination_account) by the source code’s authority:
The close of a token account includes three steps:
Note that the source account must be not the same as the destination account:
The SetAuthority instruction is used to set a new authority for a mint or token account. The authority could be either a single account (wallet or PDA) or a Multisignature account (will elaborate shortly).
The type of authority to set is specified by the authority_type . There are four different types:
A Multisig account has the Multisig data type with four attributes:
A Multisig account can be initialized by the InitializeMultisig instruction (line 175 multisig_info account):
In the initialization, m is passed as a parameter, n is the length of valid signer accounts (line 192 signer_infos supplied in the accounts vector):
We will continue to introduce the technical details of other popular Solana programs (e.g., token-swap, stake-pool, associated-token-account) in the next few articles.
sec3 is a security research firm that prepares Solana projects for millions of users. sec3’s Launch Audit is a rigorous, researcher-led code examination that investigates and certifies mainnet-grade smart contracts; sec3’s continuous auditing software platform, X-ray, integrates with GitHub to progressively scan pull requests, helping projects fortify code before deployment; and sec3’s post-deployment security solution, WatchTower, ensures funds stay safe. sec3 is building technology-based scalable solutions for Web3 projects to ensure protocols stay safe as they scale.
To learn more about sec3, please visit https://www.sec3.dev