use anchor_lang::{
prelude::{zero_copy, AccountMeta, Pubkey},
solana_program::instruction::Instruction,
};
const MAX_FLAGS: usize = 8;
#[derive(Debug, thiserror::Error)]
pub enum InstructionError {
#[error("failed to get wallet")]
FailedToGetWallet,
}
#[zero_copy]
pub struct InstructionAccount {
pub flags: InstructionAccountFlagContainer,
pub pubkey: Pubkey,
}
impl crate::InitSpace for InstructionAccount {
const INIT_SPACE: usize = std::mem::size_of::<Self>();
}
#[derive(num_enum::IntoPrimitive)]
#[repr(u8)]
pub enum InstructionAccountFlag {
Signer,
Writable,
}
crate::flags!(InstructionAccountFlag, MAX_FLAGS, u8);
impl<'a> From<&'a InstructionAccount> for AccountMeta {
fn from(a: &'a InstructionAccount) -> Self {
Self {
pubkey: a.pubkey,
is_signer: a.flags.get_flag(InstructionAccountFlag::Signer),
is_writable: a.flags.get_flag(InstructionAccountFlag::Writable),
}
}
}
pub trait InstructionAccess {
fn wallet(&self) -> Result<Pubkey, InstructionError>;
fn program_id(&self) -> &Pubkey;
fn data(&self) -> &[u8];
fn num_accounts(&self) -> usize;
fn accounts(&self) -> impl Iterator<Item = &InstructionAccount>;
fn to_instruction(
&self,
mark_executor_wallet_as_signer: bool,
) -> Result<Instruction, InstructionError> {
let mut accounts = self
.accounts()
.map(From::from)
.collect::<Vec<AccountMeta>>();
if mark_executor_wallet_as_signer {
let executor_wallet = self.wallet()?;
accounts
.iter_mut()
.filter(|a| a.pubkey == executor_wallet)
.for_each(|a| a.is_signer = true);
}
Ok(Instruction {
program_id: *self.program_id(),
accounts,
data: self.data().to_vec(),
})
}
}