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(mut)] #[account(mut @ <custom_error>)]
|
Checks the given account is mutable. Makes anchor persist any state changes. Custom errors are supported via @ .Example:
|
#[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:
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):
#[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>)] |
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:
|
#[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:
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(owner = <expr>)] #[account(owner = <expr> @ <custom_error>)]
|
Checks the account owner matches expr .Custom errors are supported via @ .Example:
|
#[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(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 constraintExample:
|
#[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(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(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(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 true false 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:: |
§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 |