Derive Macro anchor_lang::Accounts

source · []
#[derive(Accounts)]
{
    // Attributes available to this derive:
    #[account]
    #[instruction]
}
Expand description

Implements an Accounts deserializer on the given struct. Can provide further functionality through the use of attributes.

Table of Contents

Instruction Attribute

You can access the instruction’s arguments with the #[instruction(..)] attribute. You have to list them in the same order as in the instruction but you can omit all arguments after the last one you need.

Example

...
pub fn initialize(ctx: Context<Create>, bump: u8, authority: Pubkey, data: u64) -> ProgramResult {
    ...
    Ok(())
}
...
#[derive(Accounts)]
#[instruction(bump: u8)]
pub struct Initialize<'info> {
    ...
}

Constraints

There are different types of constraints that can be applied with the #[account(..)] attribute.

Attributes may reference other data structures. When <expr> is used in the tables below, an arbitrary expression may be passed in as long as it evaluates to a value of the expected type, e.g. owner = token_program.key(). If target_account used, the target_account must exist in the struct and the .key() is implicit, e.g. payer = authority.

Normal Constraints

Attribute Description
#[account(signer)]

#[account(signer @ <custom_error>)]
Checks the given account signed the transaction.
Custom errors are supported via @.
Consider using the Signer type if you would only have this constraint on the account.

Example:

#[account(signer)]
pub authority: AccountInfo<'info>,
#[account(signer @ MyError::MyErrorCode)]
pub payer: AccountInfo<'info>
                
#[account(mut)]

#[account(mut @ <custom_error>)]
Checks the given account is mutable.
Makes anchor persist any state changes.
Custom errors are supported via @.

Example:

#[account(mut)]
pub data_account: Account<'info, MyData>,
#[account(mut @ MyError::MyErrorCode)]
pub data_account_two: Account<'info, MyData>
                
#[account(init, payer = <target_account>)]

#[account(init, payer = <target_account>, space = <num_bytes>)]
Creates the account via a CPI to the system program and initializes it (sets its account discriminator).
Marks the account as mutable and is mutually exclusive with mut.
Makes the account rent exempt unless skipped with `rent_exempt = skip`.
  • Requires the payer constraint to also be on the account. The payer account pays for the account creation.
  • Requires the system program to exist on the struct and be called system_program.
  • Requires that the space constraint is specified or, if creating an Account type, the T of Account to implement the rust std Default trait.
    When using the space constraint, one must remember to add 8 to it which is the size of the account discriminator.
    The given number is the size of the account in bytes, so accounts that hold a variable number of items such as a Vec should use the space constraint instead of using the Default trait and allocate sufficient space for all items that may be added to the data structure because account size is fixed. Check out the borsh library (which anchor uses under the hood for serialization) specification to learn how much space different data structures require.

  • Example:
    #[account]
    #[derive(Default)]
    pub struct MyData {
        pub data: u64
    }
    
    #[account]
    pub struct OtherData {
        pub data: u64
    }
    
    #[derive(Accounts)]
    pub struct Initialize<'info> {
        #[account(init, payer = payer)]
        pub data_account: Account<'info, MyData>,
        #[account(init, payer = payer, space = 8 + 8)]
        pub data_account_two: Account<'info, OtherData>,
        #[account(mut)]
        pub payer: Signer<'info>,
        pub system_program: Program<'info, System>,
    }
                    
init can be combined with other constraints (at the same time):
  • By default init sets the owner field of the created account to the currently executing program. Add the owner constraint to specify a different program owner.
  • Use the seeds constraint together with bumpto create PDAs.
    init uses find_program_address to calculate the pda so the bump value can be left empty.
    However, if you want to use the bump in your instruction, you can pass it in as instruction data and set the bump value like shown in the example, using the instruction_data attribute. Anchor will then check that the bump returned by find_program_address equals the bump in the instruction data.
    seeds::program cannot be used together with init because the creation of an account requires its signature which for PDAs only the currently executing program can provide.
Example:
#[derive(Accounts)]
#[instruction(bump: u8)]
pub struct Initialize<'info> {
    #[account(
        init, payer = payer,
        seeds = [b"example_seed".as_ref()], bump = bump
    )]
    pub pda_data_account: Account<'info, MyData>,
    #[account(
        init, payer = payer,
        space = 8 + 8, owner = other_program.key()
    )]
    pub account_for_other_program: AccountInfo<'info>,
    #[account(
        init,payer = payer, space = 8 + 8,
        owner = other_program.key(),
        seeds = [b"other_seed".as_ref()], bump
    )]
    pub pda_for_other_program: AccountInfo<'info>,
    #[account(mut)]
    pub payer: Signer<'info>,
    pub system_program: Program<'info, System>,
    pub other_program: Program<'info, OtherProgram>
}
                
#[account(init_if_needed, payer = <target_account>)]

#[account(init_if_needed, payer = <target_account>, space = <num_bytes>)]
Exact same functionality as the init constraint but only runs if the account does not exist yet.
If the account does exist, it still checks whether the given init constraints are correct, e.g. that the account has the expected amount of space and, if it's a PDA, the correct seeds etc.

This feature should be used with care and is therefore behind a feature flag. You can enable it by importing anchor-lang with the init-if-needed cargo feature.
When using init_if_needed, you need to make sure you properly protect yourself against re-initialization attacks. You need to include checks in your code that check that the initialized account cannot be reset to its initial settings after the first time it was initialized (unless that it what you want).
Because of the possibility of re-initialization attacks and the general guideline that instructions should avoid having multiple execution flows (which is important so they remain easy to understand), consider breaking up your instruction into two instructions - one for initializing and one for using the account - unless you have a good reason not to do so.

Example:
#[account]
#[derive(Default)]
pub struct MyData {
    pub data: u64
}

#[account]
pub struct OtherData {
    pub data: u64
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(init_if_needed, payer = payer)]
    pub data_account: Account<'info, MyData>,
    #[account(init_if_needed, payer = payer, space = 8 + 8)]
    pub data_account_two: Account<'info, OtherData>,
    #[account(mut)]
    pub payer: Signer<'info>,
    pub system_program: Program<'info, System>
}
                
#[account(seeds = <seeds>, bump)]

#[account(seeds = <seeds>, bump, seeds::program = <expr>)]

#[account(seeds = <seeds>, bump = <expr>)]

#[account(seeds = <seeds>, bump = <expr>, seeds::program = <expr>)]

Checks that given account is a PDA derived from the currently executing program, the seeds, and if provided, the bump. If not provided, anchor uses the canonical bump.
Add seeds::program = <expr> to derive the PDA from a different program than the currently executing one.
This constraint behaves slightly differently when used with init. See its description.

Example:

#[derive(Accounts)]
#[instruction(first_bump: u8, second_bump: u8)]
pub struct Example {
    #[account(seeds = [b"example_seed], bump)]
    pub canonical_pda: AccountInfo<'info>,
    #[account(
        seeds = [b"example_seed],
        bump,
        seeds::program = other_program.key()
    )]
    pub canonical_pda_two: AccountInfo<'info>,
    #[account(seeds = [b"other_seed], bump = first_bump)]
    pub arbitrary_pda: AccountInfo<'info>
    #[account(
        seeds = [b"other_seed],
        bump = second_bump,
        seeds::program = other_program.key()
    )]
    pub arbitrary_pda_two: AccountInfo<'info>,
    pub other_program: Program<'info, OtherProgram>
}
                
#[account(has_one = <target_account>)]

#[account(has_one = <target_account> @ <custom_error>)]
Checks the target_account field on the account matches the key of the target_account field in the Accounts struct.
Custom errors are supported via @.

Example:

#[account(mut, has_one = authority)]
pub data: Account<'info, MyData>,
pub authority: Signer<'info>
                
In this example has_one checks that data.authority = authority.key()
#[account(address = <expr>)]

#[account(address = <expr> @ <custom_error>)]
Checks the account key matches the pubkey.
Custom errors are supported via @.

Example:

#[account(address = crate::ID)]
pub data: Account<'info, MyData>,
#[account(address = crate::ID @ MyError::MyErrorCode)]
pub data_two: Account<'info, MyData>
                
#[account(owner = <expr>)]

#[account(owner = <expr> @ <custom_error>)]
Checks the account owner matches expr.
Custom errors are supported via @.

Example:

#[account(owner = Token::ID @ MyError::MyErrorCode)]
pub data: Account<'info, MyData>,
#[account(owner = token_program.key())]
pub data_two: Account<'info, MyData>,
pub token_program: Program<'info, Token>
                
#[account(executable)] Checks the account is executable (i.e. the account is a program).
You may want to use the Program type instead.

Example:

#[account(executable)]
pub my_program: AccountInfo<'info>
                
#[account(rent_exempt = skip)]

#[account(rent_exempt = enforce)]
Enforces rent exemption with = enforce.
Skips rent exemption check that would normally be done through other constraints with = skip, e.g. when used with the zero constraint

Example:

#[account(zero, rent_exempt = skip)]
pub skipped_account: Account<'info, MyData>,
#[account(rent_exempt = enforce)]
pub enforced_account: AccountInfo<'info>
                
#[account(zero)] Checks the account discriminator is zero.
Enforces rent exemption unless skipped with rent_exempt = skip

Example:

#[account(zero)]
pub my_account: Account<'info, MyData>
                
#[account(close = <target_account>)] Marks the account as closed at the end of the instruction’s execution (sets its discriminator to the CLOSED_ACCOUNT_DISCRIMINATOR) and sends its lamports to the specified account.
Setting the discriminator to a special variant makes account revival attacks (where a subsequent instruction adds the rent exemption lamports again) impossible.
Requires mut to exist on the account.

Example:

#[account(mut, close = receiver)]
pub data_account: Account<'info, MyData>,
#[account(mut)]
pub receiver: SystemAccount<'info>
                
#[account(constraint = <expr>)]

#[account(constraint = <expr> @ <custom_error>)]
Constraint that checks whether the given expression evaluates to true.
Use this when no other constraint fits your use case.

Example:

#[account(constraint = one.keys[0].age == two.apple.age)]
pub one: Account<'info, MyData>,
pub two: Account<'info, OtherData>
                

SPL Constraints

Anchor provides constraints that make verifying SPL accounts easier.

Attribute Description
#[account(token::mint = <target_account>, token::authority = <target_account>)] Can currently only be used with init to create a token account with the given mint address and authority.

Example:
use anchor_spl::{mint, token::{TokenAccount, Mint, Token}};
...

#[account(
    init,
    payer = payer,
    token::mint = mint,
    token::authority = payer,
)]
pub token: Account<'info, TokenAccount>,
#[account(address = mint::USDC)]
pub mint: Account<'info, Mint>,
#[account(mut)]
pub payer: Signer<'info>,
pub token_program: Program<'info, Token>,
pub system_program: Program<'info, System>
                
#[account(mint::authority = <target_account>, mint::decimals = <expr>)]

#[account(mint::authority = <target_account>, mint::decimals = <expr>, mint::freeze_authority = <target_account>)]
Can currently only be used with init to create a mint account with the given mint decimals and mint authority.
The freeze authority is optional.

Example:
use anchor_spl::token::{Mint, Token};
...

#[account(
    init,
    payer = payer,
    mint::decimals = 9,
    mint::authority = payer,
)]
pub mint_one: Account<'info, Mint>,
#[account(
    init,
    payer = payer,
    mint::decimals = 9,
    mint::authority = payer,
    mint::freeze_authority = payer
)]
pub mint_two: Account<'info, Mint>,
#[account(mut)]
pub payer: Signer<'info>,
pub token_program: Program<'info, Token>,
pub system_program: Program<'info, System>
                
#[account(associated_token::mint = <target_account>, associated_token::authority = <target_account>)] Can be used as a standalone as a check or with init to create an associated token account with the given mint address and authority.

Example:
use anchor_spl::{
    associated_token::AssociatedToken,
    mint,
    token::{TokenAccount, Mint, Token}
};
...

#[account(
    init,
    payer = payer,
    associated_token::mint = mint,
    associated_token::authority = payer,
)]
pub token: Account<'info, TokenAccount>,
#[account(
    associated_token::mint = mint,
    associated_token::authority = payer,
)]
pub second_token: Account<'info, TokenAccount>,
#[account(address = mint::USDC)]
pub mint: Account<'info, Mint>,
#[account(mut)]
pub payer: Signer<'info>,
pub token_program: Program<'info, Token>,
pub associated_token_program: Program<'info, AssociatedToken>,
pub system_program: Program<'info, System>