pub use crate::{
token_2022::{PermanentDelegateInitialize, *},
token_2022_extensions::*,
};
use {
anchor_lang::{
accounts::{InterfaceAccount, SlabInit, SlabSchema},
programs::{Token, Token2022 as Token2022Program},
require, require_eq, AccountConstraint, AnchorAccount, Id, Ids,
},
bytemuck::{Pod, Zeroable},
core::ops::Deref,
pinocchio::account::AccountView,
solana_address::Address,
solana_program_error::ProgramError,
spl_token_2022_interface::{
extension::{
BaseStateWithExtensions, ExtensionType as Token2022ExtensionType,
PodStateWithExtensions,
},
pod::{PodAccount, PodMint},
},
};
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct Interface<T>(T);
unsafe impl<T: Pod> Pod for Interface<T> {}
unsafe impl<T: Zeroable> Zeroable for Interface<T> {}
impl<T> Deref for Interface<T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &T {
&self.0
}
}
pub type TokenAccount = Interface<crate::TokenAccount>;
pub type Mint = Interface<crate::Mint>;
pub trait TokenInterfaceAccountExtensions {
fn get_extension<T: crate::extensions::ExtensionType>(&self) -> Result<&T, ProgramError>;
}
impl TokenInterfaceAccountExtensions for InterfaceAccount<Mint> {
#[inline(always)]
fn get_extension<T: crate::extensions::ExtensionType>(&self) -> Result<&T, ProgramError> {
let account = self.account();
require!(
account.owned_by(&Token2022Program::id()),
ProgramError::IllegalOwner
);
let data = unsafe { account.borrow_unchecked() };
let state = PodStateWithExtensions::<PodMint>::unpack(data)?;
let extension = state.get_extension::<T>()?;
let extension_ptr = extension as *const T;
Ok(unsafe { &*extension_ptr })
}
}
impl TokenInterfaceAccountExtensions for InterfaceAccount<TokenAccount> {
#[inline(always)]
fn get_extension<T: crate::extensions::ExtensionType>(&self) -> Result<&T, ProgramError> {
let account = self.account();
require!(
account.owned_by(&Token2022Program::id()),
ProgramError::IllegalOwner
);
let data = unsafe { account.borrow_unchecked() };
let state = PodStateWithExtensions::<PodAccount>::unpack(data)?;
let extension = state.get_extension::<T>()?;
let extension_ptr = extension as *const T;
Ok(unsafe { &*extension_ptr })
}
}
pub struct TokenInterface;
impl Ids for TokenInterface {
#[inline(always)]
fn ids() -> &'static [Address] {
static IDS: [Address; 2] = [
anchor_lang::address!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"),
anchor_lang::address!("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"),
];
&IDS
}
}
impl SlabSchema for Interface<crate::TokenAccount> {
const DATA_OFFSET: usize = 0;
const MIN_DATA_LEN: usize = core::mem::size_of::<Self>();
#[inline(always)]
fn validate(view: &AccountView, data: &[u8]) -> Result<(), ProgramError> {
require!(
view.owned_by(&Token::id()) || view.owned_by(&Token2022Program::id()),
ProgramError::IllegalOwner
);
PodStateWithExtensions::<PodAccount>::unpack(data)?;
crate::token::validate_token_account_initialized(data)?;
Ok(())
}
}
impl SlabSchema for Interface<crate::Mint> {
const DATA_OFFSET: usize = 0;
const MIN_DATA_LEN: usize = core::mem::size_of::<Self>();
#[inline(always)]
fn validate(view: &AccountView, data: &[u8]) -> Result<(), ProgramError> {
require!(
view.owned_by(&Token::id()) || view.owned_by(&Token2022Program::id()),
ProgramError::IllegalOwner
);
PodStateWithExtensions::<PodMint>::unpack(data)?;
crate::mint::validate_mint_initialized(data)?;
Ok(())
}
}
impl anchor_lang::Space for Interface<crate::TokenAccount> {
const INIT_SPACE: usize = core::mem::size_of::<crate::TokenAccount>();
}
impl anchor_lang::Space for Interface<crate::Mint> {
const INIT_SPACE: usize = core::mem::size_of::<crate::Mint>();
}
#[doc(hidden)]
impl anchor_lang::IdlAccountType for Interface<crate::TokenAccount> {}
#[doc(hidden)]
impl anchor_lang::IdlAccountType for Interface<crate::Mint> {}
#[derive(Default)]
pub struct InterfaceTokenAccountInitParams<'a> {
pub mint: Option<&'a AccountView>,
pub authority: Option<&'a AccountView>,
pub token_program: Option<&'a AccountView>,
}
impl SlabInit for Interface<crate::TokenAccount> {
type Params<'a> = InterfaceTokenAccountInitParams<'a>;
#[cold]
fn create_and_initialize<'a>(
payer: &AccountView,
account: &AccountView,
_space: usize,
params: &Self::Params<'a>,
signer_seeds: Option<&[&[u8]]>,
payer_signer_seeds: Option<&[&[u8]]>,
) -> Result<(), ProgramError> {
let mint = params.mint.ok_or(ProgramError::InvalidArgument)?;
let authority = params.authority.ok_or(ProgramError::InvalidArgument)?;
let token_program = params.token_program.ok_or(ProgramError::InvalidArgument)?;
let program_id = token_program.address();
crate::token_shared::validate_token_interface_program(program_id)?;
let space = token_account_init_space(mint, program_id)?;
anchor_lang::create_account_with_signers(
payer,
account,
space,
program_id,
signer_seeds,
payer_signer_seeds,
)?;
pinocchio_token_2022::instructions::InitializeAccount3 {
account,
mint,
owner: authority.address(),
token_program: program_id,
}
.invoke()
}
}
#[inline(always)]
fn token_account_init_space(
mint: &AccountView,
token_program: &Address,
) -> Result<usize, ProgramError> {
if !anchor_lang::address_eq(token_program, &Token2022Program::id()) {
return Ok(core::mem::size_of::<crate::TokenAccount>());
}
let mint_data = unsafe { mint.borrow_unchecked() };
let mint_state = PodStateWithExtensions::<PodMint>::unpack(mint_data)?;
let mint_extensions = mint_state.get_extension_types()?;
let required_extensions =
Token2022ExtensionType::get_required_init_account_extensions(&mint_extensions);
Token2022ExtensionType::try_calculate_account_len::<PodAccount>(&required_extensions)
}
#[derive(Default)]
pub struct InterfaceMintInitParams<'a> {
pub decimals: Option<u8>,
pub authority: Option<&'a AccountView>,
pub freeze_authority: Option<&'a AccountView>,
pub token_program: Option<&'a AccountView>,
}
impl SlabInit for Interface<crate::Mint> {
type Params<'a> = InterfaceMintInitParams<'a>;
#[cold]
fn create_and_initialize<'a>(
payer: &AccountView,
account: &AccountView,
_space: usize,
params: &Self::Params<'a>,
signer_seeds: Option<&[&[u8]]>,
payer_signer_seeds: Option<&[&[u8]]>,
) -> Result<(), ProgramError> {
let decimals = params.decimals.ok_or(ProgramError::InvalidArgument)?;
let authority = params.authority.ok_or(ProgramError::InvalidArgument)?;
let token_program = params.token_program.ok_or(ProgramError::InvalidArgument)?;
let program_id = token_program.address();
crate::token_shared::validate_token_interface_program(program_id)?;
let space = core::mem::size_of::<crate::Mint>();
anchor_lang::create_account_with_signers(
payer,
account,
space,
program_id,
signer_seeds,
payer_signer_seeds,
)?;
pinocchio_token_2022::instructions::InitializeMint2 {
mint: account,
decimals,
mint_authority: authority.address(),
freeze_authority: params.freeze_authority.map(|v| v.address()),
token_program: program_id,
}
.invoke()
}
}
impl AccountConstraint<InterfaceAccount<TokenAccount>> for crate::token::MintConstraint {
type Value = Address;
#[inline(always)]
fn check(
account: &InterfaceAccount<TokenAccount>,
expected: &Address,
) -> Result<(), ProgramError> {
require!(
anchor_lang::address_eq(account.mint(), expected),
ProgramError::InvalidAccountData
);
Ok(())
}
}
impl AccountConstraint<InterfaceAccount<TokenAccount>> for crate::token::AuthorityConstraint {
type Value = Address;
#[inline(always)]
fn check(
account: &InterfaceAccount<TokenAccount>,
expected: &Address,
) -> Result<(), ProgramError> {
require!(
anchor_lang::address_eq(account.owner(), expected),
ProgramError::InvalidAccountData
);
Ok(())
}
}
impl AccountConstraint<InterfaceAccount<TokenAccount>> for crate::token::TokenProgramConstraint {
type Value = Address;
#[inline(always)]
fn check(
account: &InterfaceAccount<TokenAccount>,
expected: &Address,
) -> Result<(), ProgramError> {
require!(
AsRef::<AccountView>::as_ref(account).owned_by(expected),
ProgramError::IllegalOwner
);
Ok(())
}
}
impl AccountConstraint<InterfaceAccount<Mint>> for crate::mint::AuthorityConstraint {
type Value = Address;
#[inline(always)]
fn check(account: &InterfaceAccount<Mint>, expected: &Address) -> Result<(), ProgramError> {
require_eq!(
account.mint_authority(),
Some(expected),
ProgramError::InvalidAccountData
);
Ok(())
}
}
impl AccountConstraint<InterfaceAccount<Mint>> for crate::mint::FreezeAuthorityConstraint {
type Value = Address;
#[inline(always)]
fn check(account: &InterfaceAccount<Mint>, expected: &Address) -> Result<(), ProgramError> {
require_eq!(
account.freeze_authority(),
Some(expected),
ProgramError::InvalidAccountData
);
Ok(())
}
}
impl AccountConstraint<InterfaceAccount<Mint>> for crate::mint::DecimalsConstraint {
type Value = u8;
#[inline(always)]
fn check(account: &InterfaceAccount<Mint>, expected: &u8) -> Result<(), ProgramError> {
require_eq!(
account.decimals(),
*expected,
ProgramError::InvalidAccountData
);
Ok(())
}
}
impl AccountConstraint<InterfaceAccount<Mint>> for crate::mint::TokenProgramConstraint {
type Value = Address;
#[inline(always)]
fn check(account: &InterfaceAccount<Mint>, expected: &Address) -> Result<(), ProgramError> {
require!(
AsRef::<AccountView>::as_ref(account).owned_by(expected),
ProgramError::IllegalOwner
);
Ok(())
}
}