gmsol_utils/
instruction.rs

1use anchor_lang::{
2    prelude::{zero_copy, AccountMeta, Pubkey},
3    solana_program::instruction::Instruction,
4};
5
6const MAX_FLAGS: usize = 8;
7
8/// Instruction error.
9#[derive(Debug, thiserror::Error)]
10pub enum InstructionError {
11    /// Failed to get wallet.
12    #[error("failed to get wallet")]
13    FailedToGetWallet,
14}
15
16/// Instruction Account.
17#[zero_copy]
18pub struct InstructionAccount {
19    /// Flags.
20    pub flags: InstructionAccountFlagContainer,
21    /// Pubkey.
22    pub pubkey: Pubkey,
23}
24
25impl crate::InitSpace for InstructionAccount {
26    const INIT_SPACE: usize = std::mem::size_of::<Self>();
27}
28
29/// Flags of Instruction Accounts.
30#[derive(num_enum::IntoPrimitive)]
31#[repr(u8)]
32pub enum InstructionAccountFlag {
33    /// Is signer.
34    Signer,
35    /// Is mutable.
36    Writable,
37    // CHECK: cannot have more than `MAX_FLAGS` flags.
38}
39
40crate::flags!(InstructionAccountFlag, MAX_FLAGS, u8);
41
42impl<'a> From<&'a InstructionAccount> for AccountMeta {
43    fn from(a: &'a InstructionAccount) -> Self {
44        Self {
45            pubkey: a.pubkey,
46            is_signer: a.flags.get_flag(InstructionAccountFlag::Signer),
47            is_writable: a.flags.get_flag(InstructionAccountFlag::Writable),
48        }
49    }
50}
51
52/// Instruction Access.
53pub trait InstructionAccess {
54    /// Get wallet.
55    fn wallet(&self) -> Result<Pubkey, InstructionError>;
56
57    /// Get program ID.
58    fn program_id(&self) -> &Pubkey;
59
60    /// Get data.
61    fn data(&self) -> &[u8];
62
63    /// Get the number of accounts.
64    fn num_accounts(&self) -> usize;
65
66    /// Get accounts.
67    fn accounts(&self) -> impl Iterator<Item = &InstructionAccount>;
68
69    /// Convert to instruction.
70    fn to_instruction(
71        &self,
72        mark_executor_wallet_as_signer: bool,
73    ) -> Result<Instruction, InstructionError> {
74        let mut accounts = self
75            .accounts()
76            .map(From::from)
77            .collect::<Vec<AccountMeta>>();
78
79        // When performing a CPI, the PDA doesn't need to be explicitly marked as a signer,
80        // so we've made it optional to reduce computational overhead.
81        if mark_executor_wallet_as_signer {
82            let executor_wallet = self.wallet()?;
83            accounts
84                .iter_mut()
85                .filter(|a| a.pubkey == executor_wallet)
86                .for_each(|a| a.is_signer = true);
87        }
88
89        Ok(Instruction {
90            program_id: *self.program_id(),
91            accounts,
92            data: self.data().to_vec(),
93        })
94    }
95}