use anchor_lang::solana_program::account_info::AccountInfo; use anchor_lang::solana_program::program_pack::Pack; use anchor_lang::solana_program::pubkey::Pubkey; use anchor_lang::Result; use anchor_lang::{context::CpiContext, Accounts}; use std::ops::Deref; pub use spl_token; pub use spl_token::ID; pub fn transfer<'info>( ctx: CpiContext<'_, '_, '_, 'info, Transfer<'info>>, amount: u64, ) -> Result<()> { let ix = spl_token::instruction::transfer( &spl_token::ID, ctx.accounts.from.key, ctx.accounts.to.key, ctx.accounts.authority.key, &[], amount, )?; anchor_lang::solana_program::program::invoke_signed( &ix, &[ctx.accounts.from, ctx.accounts.to, ctx.accounts.authority], ctx.signer_seeds, ) .map_err(Into::into) } pub fn transfer_checked<'info>( ctx: CpiContext<'_, '_, '_, 'info, TransferChecked<'info>>, amount: u64, decimals: u8, ) -> Result<()> { let ix = spl_token::instruction::transfer_checked( &spl_token::ID, ctx.accounts.from.key, ctx.accounts.mint.key, ctx.accounts.to.key, ctx.accounts.authority.key, &[], amount, decimals, )?; anchor_lang::solana_program::program::invoke_signed( &ix, &[ ctx.accounts.from, ctx.accounts.mint, ctx.accounts.to, ctx.accounts.authority, ], ctx.signer_seeds, ) .map_err(Into::into) } pub fn mint_to<'info>( ctx: CpiContext<'_, '_, '_, 'info, MintTo<'info>>, amount: u64, ) -> Result<()> { let ix = spl_token::instruction::mint_to( &spl_token::ID, ctx.accounts.mint.key, ctx.accounts.to.key, ctx.accounts.authority.key, &[], amount, )?; anchor_lang::solana_program::program::invoke_signed( &ix, &[ctx.accounts.to, ctx.accounts.mint, ctx.accounts.authority], ctx.signer_seeds, ) .map_err(Into::into) } pub fn burn<'info>(ctx: CpiContext<'_, '_, '_, 'info, Burn<'info>>, amount: u64) -> Result<()> { let ix = spl_token::instruction::burn( &spl_token::ID, ctx.accounts.from.key, ctx.accounts.mint.key, ctx.accounts.authority.key, &[], amount, )?; anchor_lang::solana_program::program::invoke_signed( &ix, &[ctx.accounts.from, ctx.accounts.mint, ctx.accounts.authority], ctx.signer_seeds, ) .map_err(Into::into) } pub fn approve<'info>( ctx: CpiContext<'_, '_, '_, 'info, Approve<'info>>, amount: u64, ) -> Result<()> { let ix = spl_token::instruction::approve( &spl_token::ID, ctx.accounts.to.key, ctx.accounts.delegate.key, ctx.accounts.authority.key, &[], amount, )?; anchor_lang::solana_program::program::invoke_signed( &ix, &[ ctx.accounts.to, ctx.accounts.delegate, ctx.accounts.authority, ], ctx.signer_seeds, ) .map_err(Into::into) } pub fn approve_checked<'info>( ctx: CpiContext<'_, '_, '_, 'info, ApproveChecked<'info>>, amount: u64, decimals: u8, ) -> Result<()> { let ix = spl_token::instruction::approve_checked( &spl_token::ID, ctx.accounts.to.key, ctx.accounts.mint.key, ctx.accounts.delegate.key, ctx.accounts.authority.key, &[], amount, decimals, )?; anchor_lang::solana_program::program::invoke_signed( &ix, &[ ctx.accounts.to, ctx.accounts.mint, ctx.accounts.delegate, ctx.accounts.authority, ], ctx.signer_seeds, ) .map_err(Into::into) } pub fn revoke<'info>(ctx: CpiContext<'_, '_, '_, 'info, Revoke<'info>>) -> Result<()> { let ix = spl_token::instruction::revoke( &spl_token::ID, ctx.accounts.source.key, ctx.accounts.authority.key, &[], )?; anchor_lang::solana_program::program::invoke_signed( &ix, &[ctx.accounts.source, ctx.accounts.authority], ctx.signer_seeds, ) .map_err(Into::into) } pub fn initialize_account<'info>( ctx: CpiContext<'_, '_, '_, 'info, InitializeAccount<'info>>, ) -> Result<()> { let ix = spl_token::instruction::initialize_account( &spl_token::ID, ctx.accounts.account.key, ctx.accounts.mint.key, ctx.accounts.authority.key, )?; anchor_lang::solana_program::program::invoke_signed( &ix, &[ ctx.accounts.account, ctx.accounts.mint, ctx.accounts.authority, ctx.accounts.rent, ], ctx.signer_seeds, ) .map_err(Into::into) } pub fn initialize_account3<'info>( ctx: CpiContext<'_, '_, '_, 'info, InitializeAccount3<'info>>, ) -> Result<()> { let ix = spl_token::instruction::initialize_account3( &spl_token::ID, ctx.accounts.account.key, ctx.accounts.mint.key, ctx.accounts.authority.key, )?; anchor_lang::solana_program::program::invoke_signed( &ix, &[ctx.accounts.account, ctx.accounts.mint], ctx.signer_seeds, ) .map_err(Into::into) } pub fn close_account<'info>(ctx: CpiContext<'_, '_, '_, 'info, CloseAccount<'info>>) -> Result<()> { let ix = spl_token::instruction::close_account( &spl_token::ID, ctx.accounts.account.key, ctx.accounts.destination.key, ctx.accounts.authority.key, &[], // TODO: support multisig )?; anchor_lang::solana_program::program::invoke_signed( &ix, &[ ctx.accounts.account, ctx.accounts.destination, ctx.accounts.authority, ], ctx.signer_seeds, ) .map_err(Into::into) } pub fn freeze_account<'info>( ctx: CpiContext<'_, '_, '_, 'info, FreezeAccount<'info>>, ) -> Result<()> { let ix = spl_token::instruction::freeze_account( &spl_token::ID, ctx.accounts.account.key, ctx.accounts.mint.key, ctx.accounts.authority.key, &[], // TODO: Support multisig signers. )?; anchor_lang::solana_program::program::invoke_signed( &ix, &[ ctx.accounts.account, ctx.accounts.mint, ctx.accounts.authority, ], ctx.signer_seeds, ) .map_err(Into::into) } pub fn thaw_account<'info>(ctx: CpiContext<'_, '_, '_, 'info, ThawAccount<'info>>) -> Result<()> { let ix = spl_token::instruction::thaw_account( &spl_token::ID, ctx.accounts.account.key, ctx.accounts.mint.key, ctx.accounts.authority.key, &[], // TODO: Support multisig signers. )?; anchor_lang::solana_program::program::invoke_signed( &ix, &[ ctx.accounts.account, ctx.accounts.mint, ctx.accounts.authority, ], ctx.signer_seeds, ) .map_err(Into::into) } pub fn initialize_mint<'info>( ctx: CpiContext<'_, '_, '_, 'info, InitializeMint<'info>>, decimals: u8, authority: &Pubkey, freeze_authority: Option<&Pubkey>, ) -> Result<()> { let ix = spl_token::instruction::initialize_mint( &spl_token::ID, ctx.accounts.mint.key, authority, freeze_authority, decimals, )?; anchor_lang::solana_program::program::invoke_signed( &ix, &[ctx.accounts.mint, ctx.accounts.rent], ctx.signer_seeds, ) .map_err(Into::into) } pub fn initialize_mint2<'info>( ctx: CpiContext<'_, '_, '_, 'info, InitializeMint2<'info>>, decimals: u8, authority: &Pubkey, freeze_authority: Option<&Pubkey>, ) -> Result<()> { let ix = spl_token::instruction::initialize_mint2( &spl_token::ID, ctx.accounts.mint.key, authority, freeze_authority, decimals, )?; anchor_lang::solana_program::program::invoke_signed(&ix, &[ctx.accounts.mint], ctx.signer_seeds) .map_err(Into::into) } pub fn set_authority<'info>( ctx: CpiContext<'_, '_, '_, 'info, SetAuthority<'info>>, authority_type: spl_token::instruction::AuthorityType, new_authority: Option, ) -> Result<()> { let mut spl_new_authority: Option<&Pubkey> = None; if new_authority.is_some() { spl_new_authority = new_authority.as_ref() } let ix = spl_token::instruction::set_authority( &spl_token::ID, ctx.accounts.account_or_mint.key, spl_new_authority, authority_type, ctx.accounts.current_authority.key, &[], // TODO: Support multisig signers. )?; anchor_lang::solana_program::program::invoke_signed( &ix, &[ctx.accounts.account_or_mint, ctx.accounts.current_authority], ctx.signer_seeds, ) .map_err(Into::into) } pub fn sync_native<'info>(ctx: CpiContext<'_, '_, '_, 'info, SyncNative<'info>>) -> Result<()> { let ix = spl_token::instruction::sync_native(&spl_token::ID, ctx.accounts.account.key)?; anchor_lang::solana_program::program::invoke_signed( &ix, &[ctx.accounts.account], ctx.signer_seeds, ) .map_err(Into::into) } #[derive(Accounts)] pub struct Transfer<'info> { pub from: AccountInfo<'info>, pub to: AccountInfo<'info>, pub authority: AccountInfo<'info>, } #[derive(Accounts)] pub struct TransferChecked<'info> { pub from: AccountInfo<'info>, pub mint: AccountInfo<'info>, pub to: AccountInfo<'info>, pub authority: AccountInfo<'info>, } #[derive(Accounts)] pub struct MintTo<'info> { pub mint: AccountInfo<'info>, pub to: AccountInfo<'info>, pub authority: AccountInfo<'info>, } #[derive(Accounts)] pub struct Burn<'info> { pub mint: AccountInfo<'info>, pub from: AccountInfo<'info>, pub authority: AccountInfo<'info>, } #[derive(Accounts)] pub struct Approve<'info> { pub to: AccountInfo<'info>, pub delegate: AccountInfo<'info>, pub authority: AccountInfo<'info>, } #[derive(Accounts)] pub struct ApproveChecked<'info> { pub to: AccountInfo<'info>, pub mint: AccountInfo<'info>, pub delegate: AccountInfo<'info>, pub authority: AccountInfo<'info>, } #[derive(Accounts)] pub struct Revoke<'info> { pub source: AccountInfo<'info>, pub authority: AccountInfo<'info>, } #[derive(Accounts)] pub struct InitializeAccount<'info> { pub account: AccountInfo<'info>, pub mint: AccountInfo<'info>, pub authority: AccountInfo<'info>, pub rent: AccountInfo<'info>, } #[derive(Accounts)] pub struct InitializeAccount3<'info> { pub account: AccountInfo<'info>, pub mint: AccountInfo<'info>, pub authority: AccountInfo<'info>, } #[derive(Accounts)] pub struct CloseAccount<'info> { pub account: AccountInfo<'info>, pub destination: AccountInfo<'info>, pub authority: AccountInfo<'info>, } #[derive(Accounts)] pub struct FreezeAccount<'info> { pub account: AccountInfo<'info>, pub mint: AccountInfo<'info>, pub authority: AccountInfo<'info>, } #[derive(Accounts)] pub struct ThawAccount<'info> { pub account: AccountInfo<'info>, pub mint: AccountInfo<'info>, pub authority: AccountInfo<'info>, } #[derive(Accounts)] pub struct InitializeMint<'info> { pub mint: AccountInfo<'info>, pub rent: AccountInfo<'info>, } #[derive(Accounts)] pub struct InitializeMint2<'info> { pub mint: AccountInfo<'info>, } #[derive(Accounts)] pub struct SetAuthority<'info> { pub current_authority: AccountInfo<'info>, pub account_or_mint: AccountInfo<'info>, } #[derive(Accounts)] pub struct SyncNative<'info> { pub account: AccountInfo<'info>, } #[derive(Clone, Debug, Default, PartialEq, Copy)] pub struct TokenAccount(spl_token::state::Account); impl TokenAccount { pub const LEN: usize = spl_token::state::Account::LEN; } impl anchor_lang::AccountDeserialize for TokenAccount { fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result { spl_token::state::Account::unpack(buf) .map(TokenAccount) .map_err(Into::into) } } impl anchor_lang::AccountSerialize for TokenAccount {} impl anchor_lang::Owner for TokenAccount { fn owner() -> Pubkey { ID } } impl Deref for TokenAccount { type Target = spl_token::state::Account; fn deref(&self) -> &Self::Target { &self.0 } } #[derive(Clone, Debug, Default, PartialEq, Copy)] pub struct Mint(spl_token::state::Mint); impl Mint { pub const LEN: usize = spl_token::state::Mint::LEN; } impl anchor_lang::AccountDeserialize for Mint { fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result { spl_token::state::Mint::unpack(buf) .map(Mint) .map_err(Into::into) } } impl anchor_lang::AccountSerialize for Mint {} impl anchor_lang::Owner for Mint { fn owner() -> Pubkey { ID } } impl Deref for Mint { type Target = spl_token::state::Mint; fn deref(&self) -> &Self::Target { &self.0 } } #[derive(Clone)] pub struct Token; impl anchor_lang::Id for Token { fn id() -> Pubkey { ID } } // Field parsers to save compute. All account validation is assumed to be done // outside of these methods. pub mod accessor { use super::*; pub fn amount(account: &AccountInfo) -> Result { let bytes = account.try_borrow_data()?; let mut amount_bytes = [0u8; 8]; amount_bytes.copy_from_slice(&bytes[64..72]); Ok(u64::from_le_bytes(amount_bytes)) } pub fn mint(account: &AccountInfo) -> Result { let bytes = account.try_borrow_data()?; let mut mint_bytes = [0u8; 32]; mint_bytes.copy_from_slice(&bytes[..32]); Ok(Pubkey::new_from_array(mint_bytes)) } pub fn authority(account: &AccountInfo) -> Result { let bytes = account.try_borrow_data()?; let mut owner_bytes = [0u8; 32]; owner_bytes.copy_from_slice(&bytes[32..64]); Ok(Pubkey::new_from_array(owner_bytes)) } }