Solana Programs Part 2: Understanding SPL Associated Token Account

April 11, 2022

Following Part 1: understanding SPL Token Mint, this article introduces the technical details of the SPL associated token program, another popular official Solana smart contract.

The program provides a convenient way to manage a user’s token accounts from a wallet address. Its program id: ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL

The idea is to create a PDA (i.e., program-derived account) from a user’s wallet address and a token mint. There are two main benefits:

  1. The PDA can serve as the user’s identity token account for each token they own (with respect to a mint)
  2. Tokens can be sent to any user directly using the user’s PDA as the transfer destination, e.g., in airdrop.

A Few Take-Aways on Associated Token Account

A Few Take-Aways on Associated Token Account (ATA):

  • Every ATA is a PDA
  • Every ATA is a token account
  • Every ATA corresponds to a public key (e.g., wallet address)* and a token mint, and is owned by the *wallet address* (see below for *)
  • ATA may be created by anybody (not necessarily the wallet address’ signer)
  • Nested ATA may be created and the RecoverNested instruction can be used to close a nested ATA.

*wallet address — in fact, it can be any pubkey that can be signed by an authority; e.g., a System account or a PDA (see deep dive on Solana account model).

Details

In the following, we will elaborate on two instructions:

  • AssociatedTokenAccountInstruction::Create
  • AssociatedTokenAccountInstruction::RecoverNested
Instructions provided by the SPL associated token program

Create AssociatedTokenAccount

To create a new associated token account (ATA), users need to supply the following six accounts to function process_create_associated_token_account:

  1. funder_info — Funding account (must be a system account)
  2. associated_token_account_infothe ATA address to be created
  3. wallet_account_info — Wallet address for the new ATA
  4. spl_token_mint_info — The token mint for the new ATA
  5. system_program_info— System program
  6. spl_token_program—SPL Token program

The function first calls get_associated_token_address_and_bump_seed_internal to compute the associated_token_address (i.e., the PDA) and validates the user input:

Internally, the PDA is computed using Pubkey::find_program_address and is uniquely determined by three addresses (in seeds):

  • wallet_address
  • token_program_id
  • token_mint_address

Then, the ATA is created by calling create_pda_account , and its program owner is set to spl_token_program_id (i.e., only the spl_token_program can modify the PDA account’s data):

In spl_token_2022 , it also ensures that the ATA’s owner cannot be changed by calling initialize_immutable_owner:

Finally, the ATA (which is a Token account) is initialized by setting its account.mint to token_mint_address and account.owner to wallet_address :

The initialize_account3 function initializes a token account

Note 1: account.owner here is not the program-level owner (i.e., the spl_token_program) of the ATA, but its real authority (i.e., the wallet_address). In other words, wallet_address fully owns the newly created ATA and the owner of wallet_address must sign to transfer tokens from the ATA.

Note 2: If the ATA for a given wallet_address does not yet exist, it may be created by anybody by issuing a transaction containing the AssociatedTokenAccountInstruction::Create instruction.

RecoverNested

A caveat of using the associated token program is that users may (unintentionally) create a nested ATA:

an associated token account owned by an associated token account

Once that happened, users cannot move funds from a nested ATA because it is not owned by the user’s wallet address, but a PDA.

The RecoverNested instruction is used to address this issue. It closes a nested ATA and transfers its tokens to the wallet’s ATA (and transfers its lamports to the wallet).

The input wallet_account_info account must be signed, and the destination_associated_token_account_info account is the recipient of the transferred tokens:

The transferred token amount and mint decimals are extracted from the nested ATA. The signer_seeds (i.e., owner_associated_token_account_signer_seeds) include three addresses and the bump_seed:

  1. wallet_account_info.key
  2. spl_token_program_id
  3. owner_token_mint_info.key
  4. bump_seed

The bump_seed is the bump seed returned from creating owner_associated_token_address , which is the PDA owner of the nested ATA (i.e., nested_associated_token_account_info ).

In the next few articles, we will continue to highlight the technical details of a few more Solana programs that are frequently used.


Soteria Audit

Soteria is founded by leading minds in the fields of blockchain security and software verification.

We are pleased to provide audit services to high-impact Dapps on Solana. Please visit soteria.dev or email contact@soteria.dev