1

When looking at the docs it seems that both Accounts and account attributes are useful for account serialization, deserialization, and validation. Furthermore, Accounts seems to be a derive attribute while account is a normal attribute (I'm not too sure what the difference is here either).

While going through a tutorial I see some code like this, and I'm having trouble creating a mental model of what is actually happening:

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(init, payer = user, space = 64 + 64)]
    pub base_account: Account<'info, BaseAccount>,
    #[account(mut)]
    pub user: Signer<'info>,
    pub system_program: Program<'info, System>,
}

Within the docs it describes Account as such:

...Implementations of this trait should perform any and all requisite constraint checks on accounts to ensure the accounts maintain any invariants required for the program to run securely...

So it makes sense to me that Accounts will deserialize and validate the inputs.

But then the account attribute is tacked onto multiple other fields too, and this is where I get confused. By the looks of it, account seems to handle some sort of verification too, in accordance with the arguments passed in, but what about account attributes with no arguments, or fields with no account attributes?

It seems redundant to me that account is overlapping with Accounts and I feel like I'm missing something here.

1 Answer 1

3

Accounts is just a trait, while #[account] is a macro that generates code.

Accounts trait is literally just the below. There is no "functionality".

pub trait Accounts<'info>: ToAccountMetas + ToAccountInfos<'info> + Sized {
    fn try_accounts(
        program_id: &Pubkey,
        accounts: &mut &[AccountInfo<'info>],
        ix_data: &[u8],
        bumps: &mut BTreeMap<String, u8>,
        reallocs: &mut BTreeSet<Pubkey>,
    ) -> Result<Self>;
}

#[account] (and #[program]) is doing all the heavy lifting here, including generating an implementation for your Accounts struct. If you expand the macros, you will see:

#[automatically_derived]
impl<'info> anchor_lang::Accounts<'info> for Initialize<'info>
where
    'info: 'info,
{
    #[inline(never)]
    fn try_accounts(
        program_id: &anchor_lang::solana_program::pubkey::Pubkey,
        accounts: &mut &[anchor_lang::solana_program::account_info::AccountInfo<'info>],
        ix_data: &[u8],
        __bumps: &mut std::collections::BTreeMap<String, u8>,
    ) -> anchor_lang::Result<Self> {
        // ... Generated code handing deserialisation ...
    }
}

This would be implementing the deserialisation of the incoming instruction accounts, from a low-level array of AccountInfos to a more ergonomic struct format.

As for a mental model, just imagine that all the Accounts struct fields implicitly have #[account] on them. We just sometimes need to add more metadata to the account validation, like also check if they are writable so we add an explicit #[account(mut)].

Source code references:


Btw, you may also encounter #[account] on structs themselves, like one that would be on BaseAccount struct from your tutorial. That has a different purpose. That allows you to define the layout for a "data" account used by the program. In that case, again code is generated to ease serialisation and deserialisation of that account, between low-level byte vectors and a nice struct.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.