2021-12-29 07:18:34 -08:00
//! Account container that checks ownership on deserialization.
2021-09-07 13:06:15 -07:00
use crate ::error ::ErrorCode ;
use crate ::* ;
use solana_program ::account_info ::AccountInfo ;
use solana_program ::instruction ::AccountMeta ;
use solana_program ::pubkey ::Pubkey ;
2022-01-27 13:55:13 -08:00
use std ::collections ::BTreeMap ;
2021-11-20 07:14:06 -08:00
use std ::fmt ;
2021-09-07 13:06:15 -07:00
use std ::ops ::{ Deref , DerefMut } ;
2021-12-29 07:18:34 -08:00
/// Wrapper around [`AccountInfo`](crate::solana_program::account_info::AccountInfo)
/// that verifies program ownership and deserializes underlying data into a Rust type.
///
2022-01-06 13:30:44 -08:00
/// # Table of Contents
/// - [Basic Functionality](#basic-functionality)
/// - [Using Account with non-anchor types](#using-account-with-non-anchor-types)
/// - [Out of the box wrapper types](#out-of-the-box-wrapper-types)
///
/// # Basic Functionality
///
2021-12-29 07:18:34 -08:00
/// 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](crate::Owner).
/// The `#[account]` attribute implements the Owner trait for
/// a struct using the `crate::ID` declared by [`declareId`](crate::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)`
///
2022-01-06 12:26:43 -08:00
/// # Example
2021-12-29 07:18:34 -08:00
/// ```ignore
/// use anchor_lang::prelude::*;
/// use other_program::Auth;
///
/// declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
///
/// #[program]
/// mod hello_anchor {
/// use super::*;
2022-02-20 14:28:24 -08:00
/// pub fn set_data(ctx: Context<SetData>, data: u64) -> Result<()> {
2021-12-29 07:18:34 -08:00
/// 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
/// }
/// ...
/// ```
///
2022-01-06 12:26:43 -08:00
/// # Using Account with non-anchor programs
///
2021-12-29 07:18:34 -08:00
/// 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.
///
2022-01-06 13:30:44 -08:00
/// The mint wrapper type that Anchor provides out of the box for the token program ([source](https://github.com/project-serum/anchor/blob/master/spl/src/token.rs))
2021-12-29 07:18:34 -08:00
/// ```ignore
/// #[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 {
2022-02-20 14:28:24 -08:00
/// fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self> {
2021-12-29 07:18:34 -08:00
/// 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
/// }
/// }
/// ```
2022-01-06 13:30:44 -08:00
///
/// ## 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
/// ```ignore
/// Account<'info, BpfUpgradeableLoaderState>
/// ```
/// and then match on its contents inside your instruction function.
///
/// Alternatively, you can use
/// ```ignore
/// Account<'info, ProgramData>
/// ```
/// to let anchor do the matching for you and return the ProgramData variant of BpfUpgradeableLoaderState.
///
/// # Example
/// ```ignore
/// 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
2022-02-20 14:28:24 -08:00
/// ) -> Result<()> {
2022-01-06 13:30:44 -08:00
/// 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
/// ```ignore
/// use anchor_spl::token::TokenAccount;
///
/// #[derive(Accounts)]
/// pub struct Example {
/// pub my_acc: Account<'info, TokenAccount>
/// }
/// ```
/// to access token accounts and
/// ```ignore
/// use anchor_spl::token::Mint;
///
/// #[derive(Accounts)]
/// pub struct Example {
/// pub my_acc: Account<'info, Mint>
/// }
/// ```
/// to access mint accounts.
2021-09-07 13:06:15 -07:00
#[ derive(Clone) ]
pub struct Account < ' info , T : AccountSerialize + AccountDeserialize + Owner + Clone > {
account : T ,
info : AccountInfo < ' info > ,
}
2021-11-20 07:14:06 -08:00
impl < ' info , T : AccountSerialize + AccountDeserialize + Owner + Clone + fmt ::Debug > fmt ::Debug
for Account < ' info , T >
{
fn fmt ( & self , f : & mut fmt ::Formatter < '_ > ) -> fmt ::Result {
f . debug_struct ( " Account " )
. field ( " account " , & self . account )
. field ( " info " , & self . info )
. finish ( )
}
}
2021-09-07 13:06:15 -07:00
impl < ' a , T : AccountSerialize + AccountDeserialize + Owner + Clone > Account < ' a , T > {
fn new ( info : AccountInfo < ' a > , account : T ) -> Account < ' a , T > {
Self { info , account }
}
/// Deserializes the given `info` into a `Account`.
#[ inline(never) ]
2022-02-20 14:28:24 -08:00
pub fn try_from ( info : & AccountInfo < ' a > ) -> Result < Account < ' a , T > > {
2021-11-18 07:42:07 -08:00
if info . owner = = & system_program ::ID & & info . lamports ( ) = = 0 {
return Err ( ErrorCode ::AccountNotInitialized . into ( ) ) ;
}
2021-09-07 13:06:15 -07:00
if info . owner ! = & T ::owner ( ) {
2021-12-17 04:34:43 -08:00
return Err ( ErrorCode ::AccountOwnedByWrongProgram . into ( ) ) ;
2021-09-07 13:06:15 -07:00
}
let mut data : & [ u8 ] = & info . try_borrow_data ( ) ? ;
Ok ( Account ::new ( info . clone ( ) , T ::try_deserialize ( & mut data ) ? ) )
}
/// Deserializes the given `info` into a `Account` without checking
/// the account discriminator. Be careful when using this and avoid it if
/// possible.
#[ inline(never) ]
2022-02-20 14:28:24 -08:00
pub fn try_from_unchecked ( info : & AccountInfo < ' a > ) -> Result < Account < ' a , T > > {
2021-11-18 07:42:07 -08:00
if info . owner = = & system_program ::ID & & info . lamports ( ) = = 0 {
return Err ( ErrorCode ::AccountNotInitialized . into ( ) ) ;
}
2021-09-07 13:06:15 -07:00
if info . owner ! = & T ::owner ( ) {
2021-12-17 04:34:43 -08:00
return Err ( ErrorCode ::AccountOwnedByWrongProgram . into ( ) ) ;
2021-09-07 13:06:15 -07:00
}
let mut data : & [ u8 ] = & info . try_borrow_data ( ) ? ;
Ok ( Account ::new (
info . clone ( ) ,
T ::try_deserialize_unchecked ( & mut data ) ? ,
) )
}
/// Reloads the account from storage. This is useful, for example, when
/// observing side effects after CPI.
2022-02-20 14:28:24 -08:00
pub fn reload ( & mut self ) -> Result < ( ) > {
2021-09-07 13:06:15 -07:00
let mut data : & [ u8 ] = & self . info . try_borrow_data ( ) ? ;
self . account = T ::try_deserialize ( & mut data ) ? ;
Ok ( ( ) )
}
pub fn into_inner ( self ) -> T {
self . account
}
2021-12-20 12:16:26 -08:00
2022-01-06 13:30:44 -08:00
/// Sets the inner account.
///
/// Instead of this:
/// ```ignore
2022-02-20 14:28:24 -08:00
/// pub fn new_user(ctx: Context<CreateUser>, new_user:User) -> Result<()> {
2022-01-06 13:30:44 -08:00
/// (*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:
/// ```ignore
2022-02-20 14:28:24 -08:00
/// pub fn new_user(ctx: Context<CreateUser>, new_user:User) -> Result<()> {
2022-01-06 13:30:44 -08:00
/// ctx.accounts.user_to_create.set_inner(new_user);
/// }
/// ```
2021-12-20 12:16:26 -08:00
pub fn set_inner ( & mut self , inner : T ) {
self . account = inner ;
}
2021-09-07 13:06:15 -07:00
}
impl < ' info , T : AccountSerialize + AccountDeserialize + Owner + Clone > Accounts < ' info >
for Account < ' info , T >
where
T : AccountSerialize + AccountDeserialize + Owner + Clone ,
{
#[ inline(never) ]
fn try_accounts (
_program_id : & Pubkey ,
accounts : & mut & [ AccountInfo < ' info > ] ,
_ix_data : & [ u8 ] ,
2022-01-27 13:55:13 -08:00
_bumps : & mut BTreeMap < String , u8 > ,
2022-02-20 14:28:24 -08:00
) -> Result < Self > {
2021-09-07 13:06:15 -07:00
if accounts . is_empty ( ) {
return Err ( ErrorCode ::AccountNotEnoughKeys . into ( ) ) ;
}
let account = & accounts [ 0 ] ;
* accounts = & accounts [ 1 .. ] ;
Account ::try_from ( account )
}
}
impl < ' info , T : AccountSerialize + AccountDeserialize + Owner + Clone > AccountsExit < ' info >
for Account < ' info , T >
{
2022-02-20 14:28:24 -08:00
fn exit ( & self , program_id : & Pubkey ) -> Result < ( ) > {
2021-09-07 13:06:15 -07:00
// Only persist if the owner is the current program.
if & T ::owner ( ) = = program_id {
let info = self . to_account_info ( ) ;
let mut data = info . try_borrow_mut_data ( ) ? ;
2022-02-12 12:42:41 -08:00
self . account . try_serialize ( & mut data ) ? ;
2021-09-07 13:06:15 -07:00
}
Ok ( ( ) )
}
}
impl < ' info , T : AccountSerialize + AccountDeserialize + Owner + Clone > AccountsClose < ' info >
for Account < ' info , T >
{
2022-02-20 14:28:24 -08:00
fn close ( & self , sol_destination : AccountInfo < ' info > ) -> Result < ( ) > {
2021-09-07 13:06:15 -07:00
crate ::common ::close ( self . to_account_info ( ) , sol_destination )
}
}
impl < ' info , T : AccountSerialize + AccountDeserialize + Owner + Clone > ToAccountMetas
for Account < ' info , T >
{
fn to_account_metas ( & self , is_signer : Option < bool > ) -> Vec < AccountMeta > {
let is_signer = is_signer . unwrap_or ( self . info . is_signer ) ;
let meta = match self . info . is_writable {
false = > AccountMeta ::new_readonly ( * self . info . key , is_signer ) ,
true = > AccountMeta ::new ( * self . info . key , is_signer ) ,
} ;
vec! [ meta ]
}
}
impl < ' info , T : AccountSerialize + AccountDeserialize + Owner + Clone > ToAccountInfos < ' info >
for Account < ' info , T >
{
fn to_account_infos ( & self ) -> Vec < AccountInfo < ' info > > {
vec! [ self . info . clone ( ) ]
}
}
impl < ' info , T : AccountSerialize + AccountDeserialize + Owner + Clone > AsRef < AccountInfo < ' info > >
for Account < ' info , T >
{
fn as_ref ( & self ) -> & AccountInfo < ' info > {
& self . info
}
}
2021-12-27 07:37:47 -08:00
impl < ' info , T : AccountSerialize + AccountDeserialize + Owner + Clone > AsRef < T >
for Account < ' info , T >
{
fn as_ref ( & self ) -> & T {
& self . account
}
}
2021-09-07 13:06:15 -07:00
impl < ' a , T : AccountSerialize + AccountDeserialize + Owner + Clone > Deref for Account < ' a , T > {
type Target = T ;
fn deref ( & self ) -> & Self ::Target {
& ( * self ) . account
}
}
impl < ' a , T : AccountSerialize + AccountDeserialize + Owner + Clone > DerefMut for Account < ' a , T > {
fn deref_mut ( & mut self ) -> & mut Self ::Target {
#[ cfg(feature = " anchor-debug " ) ]
if ! self . info . is_writable {
solana_program ::msg! ( " The given Account is not mutable " ) ;
panic! ( ) ;
}
& mut self . account
}
}
2022-02-08 10:32:23 -08:00
#[ cfg(not(feature = " deprecated-layout " )) ]
impl < ' a , T : AccountSerialize + AccountDeserialize + Owner + Clone > Bump for Account < ' a , T > {
fn seed ( & self ) -> u8 {
self . info . data . borrow ( ) [ 1 ]
}
}