Struct switchboard_solana::prelude::Account
pub struct Account<'info, T>{ /* private fields */ }
Expand description
Wrapper around AccountInfo
that verifies program ownership and deserializes underlying data into a Rust type.
§Table of Contents
§Basic Functionality
Account checks that Account.info.owner == T::owner()
.
This means that the data type that Accounts wraps around (=T
) needs to
implement the Owner trait.
The #[account]
attribute implements the Owner trait for
a struct using the crate::ID
declared by declare_id
in the same program. It follows that Account can also be used
with a T
that comes from a different program.
Checks:
Account.info.owner == T::owner()
!(Account.info.owner == SystemProgram && Account.info.lamports() == 0)
§Example
use anchor_lang::prelude::*;
use other_program::Auth;
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
mod hello_anchor {
use super::*;
pub fn set_data(ctx: Context<SetData>, data: u64) -> Result<()> {
if (*ctx.accounts.auth_account).authorized {
(*ctx.accounts.my_account).data = data;
}
Ok(())
}
}
#[account]
#[derive(Default)]
pub struct MyData {
pub data: u64
}
#[derive(Accounts)]
pub struct SetData<'info> {
#[account(mut)]
pub my_account: Account<'info, MyData> // checks that my_account.info.owner == Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS
pub auth_account: Account<'info, Auth> // checks that auth_account.info.owner == FEZGUxNhZWpYPj9MJCrZJvUo1iF9ys34UHx52y4SzVW9
}
// In a different program
...
declare_id!("FEZGUxNhZWpYPj9MJCrZJvUo1iF9ys34UHx52y4SzVW9");
#[account]
#[derive(Default)]
pub struct Auth {
pub authorized: bool
}
...
§Using Account with non-anchor programs
Account can also be used with non-anchor programs. The data types from
those programs are not annotated with #[account]
so you have to
- create a wrapper type around the structs you want to wrap with Account
- implement the functions required by Account yourself
instead of using
#[account]
. You only have to implement a fraction of the functions#[account]
generates. See the example below for the code you have to write.
The mint wrapper type that Anchor provides out of the box for the token program (source)
#[derive(Clone)]
pub struct Mint(spl_token::state::Mint);
// This is necessary so we can use "anchor_spl::token::Mint::LEN"
// because rust does not resolve "anchor_spl::token::Mint::LEN" to
// "spl_token::state::Mint::LEN" automatically
impl Mint {
pub const LEN: usize = spl_token::state::Mint::LEN;
}
// You don't have to implement the "try_deserialize" function
// from this trait. It delegates to
// "try_deserialize_unchecked" by default which is what we want here
// because non-anchor accounts don't have a discriminator to check
impl anchor_lang::AccountDeserialize for Mint {
fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self> {
spl_token::state::Mint::unpack(buf).map(Mint)
}
}
// AccountSerialize defaults to a no-op which is what we want here
// because it's a foreign program, so our program does not
// have permission to write to the foreign program's accounts anyway
impl anchor_lang::AccountSerialize for Mint {}
impl anchor_lang::Owner for Mint {
fn owner() -> Pubkey {
// pub use spl_token::ID is used at the top of the file
ID
}
}
// Implement the "std::ops::Deref" trait for better user experience
impl Deref for Mint {
type Target = spl_token::state::Mint;
fn deref(&self) -> &Self::Target {
&self.0
}
}
§Out of the box wrapper types
§Accessing BPFUpgradeableLoader Data
Anchor provides wrapper types to access data stored in programs owned by the BPFUpgradeableLoader such as the upgrade authority. If you’re interested in the data of a program account, you can use
Account<'info, BpfUpgradeableLoaderState>
and then match on its contents inside your instruction function.
Alternatively, you can use
Account<'info, ProgramData>
to let anchor do the matching for you and return the ProgramData variant of BpfUpgradeableLoaderState.
§Example
use anchor_lang::prelude::*;
use crate::program::MyProgram;
declare_id!("Cum9tTyj5HwcEiAmhgaS7Bbj4UczCwsucrCkxRECzM4e");
#[program]
pub mod my_program {
use super::*;
pub fn set_initial_admin(
ctx: Context<SetInitialAdmin>,
admin_key: Pubkey
) -> Result<()> {
ctx.accounts.admin_settings.admin_key = admin_key;
Ok(())
}
pub fn set_admin(...){...}
pub fn set_settings(...){...}
}
#[account]
#[derive(Default, Debug)]
pub struct AdminSettings {
admin_key: Pubkey
}
#[derive(Accounts)]
pub struct SetInitialAdmin<'info> {
#[account(init, payer = authority, seeds = [b"admin"], bump)]
pub admin_settings: Account<'info, AdminSettings>,
#[account(mut)]
pub authority: Signer<'info>,
#[account(constraint = program.programdata_address()? == Some(program_data.key()))]
pub program: Program<'info, MyProgram>,
#[account(constraint = program_data.upgrade_authority_address == Some(authority.key()))]
pub program_data: Account<'info, ProgramData>,
pub system_program: Program<'info, System>,
}
This example solves a problem you may face if your program has admin settings: How do you set the admin key for restricted functionality after deployment? Setting the admin key itself should be a restricted action but how do you restrict it without having set an admin key? You’re stuck in a loop. One solution is to use the upgrade authority of the program as the initial (or permanent) admin key.
§SPL Types
Anchor provides wrapper types to access accounts owned by the token program. Use
use anchor_spl::token::TokenAccount;
#[derive(Accounts)]
pub struct Example {
pub my_acc: Account<'info, TokenAccount>
}
to access token accounts and
use anchor_spl::token::Mint;
#[derive(Accounts)]
pub struct Example {
pub my_acc: Account<'info, Mint>
}
to access mint accounts.
Implementations§
§impl<'a, T> Account<'a, T>
impl<'a, T> Account<'a, T>
pub fn reload(&mut self) -> Result<(), Error>
pub fn reload(&mut self) -> Result<(), Error>
Reloads the account from storage. This is useful, for example, when observing side effects after CPI.
pub fn into_inner(self) -> T
pub fn set_inner(&mut self, inner: T)
pub fn set_inner(&mut self, inner: T)
Sets the inner account.
Instead of this:
pub fn new_user(ctx: Context<CreateUser>, new_user:User) -> Result<()> {
(*ctx.accounts.user_to_create).name = new_user.name;
(*ctx.accounts.user_to_create).age = new_user.age;
(*ctx.accounts.user_to_create).address = new_user.address;
}
You can do this:
pub fn new_user(ctx: Context<CreateUser>, new_user:User) -> Result<()> {
ctx.accounts.user_to_create.set_inner(new_user);
}
§impl<'a, T> Account<'a, T>
impl<'a, T> Account<'a, T>
pub fn try_from(info: &'a AccountInfo<'a>) -> Result<Account<'a, T>, Error>
pub fn try_from(info: &'a AccountInfo<'a>) -> Result<Account<'a, T>, Error>
Deserializes the given info
into a Account
.
pub fn try_from_unchecked(
info: &'a AccountInfo<'a>
) -> Result<Account<'a, T>, Error>
pub fn try_from_unchecked( info: &'a AccountInfo<'a> ) -> Result<Account<'a, T>, Error>
Deserializes the given info
into a Account
without checking
the account discriminator. Be careful when using this and avoid it if
possible.
Trait Implementations§
§impl<'info, B, T> Accounts<'info, B> for Account<'info, T>
impl<'info, B, T> Accounts<'info, B> for Account<'info, T>
§fn try_accounts(
_program_id: &Pubkey,
accounts: &mut &'info [AccountInfo<'info>],
_ix_data: &[u8],
_bumps: &mut B,
_reallocs: &mut BTreeSet<Pubkey>
) -> Result<Account<'info, T>, Error>
fn try_accounts( _program_id: &Pubkey, accounts: &mut &'info [AccountInfo<'info>], _ix_data: &[u8], _bumps: &mut B, _reallocs: &mut BTreeSet<Pubkey> ) -> Result<Account<'info, T>, Error>
Mint
account from the SPL token program in a particular
field, then it should be impossible for this method to return Ok
if
any other account type is given–from the SPL token program or elsewhere. Read more§impl<'info, T> AccountsClose<'info> for Account<'info, T>
impl<'info, T> AccountsClose<'info> for Account<'info, T>
§impl<'info, T> AccountsExit<'info> for Account<'info, T>
impl<'info, T> AccountsExit<'info> for Account<'info, T>
§impl<'info, T> AsRef<AccountInfo<'info>> for Account<'info, T>
impl<'info, T> AsRef<AccountInfo<'info>> for Account<'info, T>
§fn as_ref(&self) -> &AccountInfo<'info>
fn as_ref(&self) -> &AccountInfo<'info>
§impl<'info, T> ToAccountInfos<'info> for Account<'info, T>
impl<'info, T> ToAccountInfos<'info> for Account<'info, T>
fn to_account_infos(&self) -> Vec<AccountInfo<'info>>
§impl<'info, T> ToAccountMetas for Account<'info, T>
impl<'info, T> ToAccountMetas for Account<'info, T>
§fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta>
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta>
is_signer
is given as an optional override for the signer meta field.
This covers the edge case when a program-derived-address needs to relay
a transaction from a client to another program but sign the transaction
before the relay. The client cannot mark the field as a signer, and so
we have to override the is_signer meta field given by the client.Auto Trait Implementations§
impl<'info, T> Freeze for Account<'info, T>where
T: Freeze,
impl<'info, T> !RefUnwindSafe for Account<'info, T>
impl<'info, T> !Send for Account<'info, T>
impl<'info, T> !Sync for Account<'info, T>
impl<'info, T> Unpin for Account<'info, T>where
T: Unpin,
impl<'info, T> !UnwindSafe for Account<'info, T>
Blanket Implementations§
source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
source§impl<T> IntoEither for T
impl<T> IntoEither for T
source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self
into a Left
variant of Either<Self, Self>
if into_left
is true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moresource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self
into a Left
variant of Either<Self, Self>
if into_left(&self)
returns true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read more