gmsol_utils/
instruction.rs

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