Derive Macro switchboard_solana::prelude::Accounts

#[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) -> anchor_lang::Result<()> {
    ...
    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>, 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.

Use #[account(zero)] for accounts larger than 10 Kibibyte.

init has to be used with additional constraints:
  • 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. When using the space constraint, one must remember to add 8 to it which is the size of the account discriminator. This only has to be done for accounts owned by anchor programs.
    The given space number is the size of the account in bytes, so accounts that hold a variable number of items such as a Vec should allocate sufficient space for all items that may be added to the data structure because account size is fixed. Check out the space reference and the borsh library (which anchor uses under the hood for serialization) specification to learn how much space different data structures require.

  • Example:
    #[account]
    pub struct MyData {
        pub data: u64
    }
    
    #[derive(Accounts)]
    pub struct Initialize<'info> {
        #[account(init, payer = payer, space = 8 + 8)]
        pub data_account_two: Account<'info, MyData>,
        #[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, space = 8 + 8
        seeds = [b"example_seed"], 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"], 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.

Use this constraint if you want to create an account in a previous instruction and then initialize it in your instruction instead of using init. This is necessary for accounts that are larger than 10 Kibibyte because those accounts cannot be created via a CPI (which is what init would do).

Anchor adds internal data to the account when using zero just like it does with init which is why zero implies mut.

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>
                
#[account(realloc = <space>, realloc::payer = <target>, realloc::zero = <bool>)] Used to realloc program account space at the beginning of an instruction.

The account must be marked as mut and applied to either Account or AccountLoader types.

If the change in account data length is additive, lamports will be transferred from the realloc::payer into the program account in order to maintain rent exemption. Likewise, if the change is subtractive, lamports will be transferred from the program account back into the realloc::payer.

The realloc::zero constraint is required in order to determine whether the new memory should be zero initialized after reallocation. Please read the documentation on the AccountInfo::realloc function linked above to understand the caveats regarding compute units when providing truefalse to this flag.

The manual use of `AccountInfo::realloc` is discouraged in favor of the `realloc` constraint group due to the lack of native runtime checks to prevent reallocation over the `MAX_PERMITTED_DATA_INCREASE` limit (which can unintentionally cause account data overwrite other accounts). The constraint group also ensure account reallocation idempotency but checking and restricting duplicate account reallocation within a single ix.

Example:
#[derive(Accounts)]
pub struct Example {
    #[account(mut)]
    pub payer: Signer<'info>,
    #[account(
        mut,
        seeds = [b"example"],
        bump,
        realloc = 8 + std::mem::size_of::() + 100,
        realloc::payer = payer,
        realloc::zero = false,
    )]
    pub acc: Account<'info, MyType>,
    pub system_program: Program<'info, System>,
}
                

§SPL Constraints

Anchor provides constraints that make verifying SPL accounts easier.

Attribute Description
#[account(token::mint = <target_account>, token::authority = <target_account>)]

#[account(token::mint = <target_account>, token::authority = <target_account>, token::token_program = <target_account>)]
Can be used as a check or with init to create a token account with the given mint address and authority.
When used as a check, it's possible to only specify a subset of the constraints.

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 be used as a check or with init to create a mint account with the given mint decimals and mint authority.
The freeze authority is optional when used with init.
When used as a check, it's possible to only specify a subset of the constraints.

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>)]

#[account(associated_token::mint = <target_account>, associated_token::authority = <target_account>, associated_token::token_program = <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>
                
#[account(*::token_program = <target_account>)] The token_program can optionally be overridden.

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

#[account(
    mint::token_program = token_a_token_program,
)]
pub token_a_mint: Box>,
#[account(
    mint::token_program = token_b_token_program,
)]
pub token_b_mint: Box>,
#[account(
    init,
    payer = payer,
    token::mint = token_a_mint,
    token::authority = payer,
    token::token_program = token_a_token_program,
)]
pub token_a_account: Box>,
#[account(
    init,
    payer = payer,
    token::mint = token_b_mint,
    token::authority = payer,
    token::token_program = token_b_token_program,
)]
pub token_b_account: Box>,
pub token_a_token_program: Interface<'info, TokenInterface>,
pub token_b_token_program: Interface<'info, TokenInterface>,
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>