1
use {
2
    solana_sdk::{instruction::CompiledInstruction, message::AccountKeys, pubkey::Pubkey},
3
    solana_transaction_status::InnerInstruction,
4
};
5

            
6
#[derive(Debug)]
7
pub struct BonsolInstruction {
8
    /// Whether we picked up an instruction that was an inner instruction
9
    /// found in the metadata.
10
    pub cpi: bool,
11
    pub accounts: Vec<Pubkey>,
12
    pub data: Vec<u8>,
13
    pub last_known_block: u64,
14
}
15

            
16
impl BonsolInstruction {
17
2
    pub const fn new(
18
2
        cpi: bool,
19
2
        accounts: Vec<Pubkey>,
20
2
        data: Vec<u8>,
21
2
        last_known_block: u64,
22
2
    ) -> Self {
23
2
        Self {
24
2
            cpi,
25
2
            accounts,
26
2
            data,
27
2
            last_known_block,
28
2
        }
29
2
    }
30
1
    const fn inner(accounts: Vec<Pubkey>, data: Vec<u8>, last_known_block: u64) -> Self {
31
1
        Self::new(true, accounts, data, last_known_block)
32
1
    }
33
1
    const fn outer(accounts: Vec<Pubkey>, data: Vec<u8>, last_known_block: u64) -> Self {
34
1
        Self::new(false, accounts, data, last_known_block)
35
1
    }
36
}
37

            
38
/// Conversion trait for Inner and Outer instructions to become Bonsol instructions.
39
/// This does not use From and Into because it requires context other than just
40
/// the instructions themselves.
41
pub trait IntoBonsolInstruction {
42
    /// Convert an instruction into a [`BonsolInstruction`].
43
    fn into_bonsol_ix(self, acc: &AccountKeys, last_known_block: u64) -> BonsolInstruction;
44
    /// Get the index of the `Pubkey` that represents the program in the `AccountKeys` map.
45
    fn program_id_index(&self) -> u8;
46
}
47

            
48
impl IntoBonsolInstruction for InnerInstruction {
49
1
    fn into_bonsol_ix(self, acc: &AccountKeys, last_known_block: u64) -> BonsolInstruction {
50
1
        BonsolInstruction::inner(
51
1
            self.instruction
52
1
                .accounts
53
1
                .into_iter()
54
2
                .filter_map(|idx| acc.get(idx as usize).copied())
55
1
                .collect(),
56
1
            self.instruction.data,
57
1
            last_known_block,
58
1
        )
59
1
    }
60
1
    fn program_id_index(&self) -> u8 {
61
1
        self.instruction.program_id_index
62
1
    }
63
}
64

            
65
impl IntoBonsolInstruction for CompiledInstruction {
66
1
    fn into_bonsol_ix(self, acc: &AccountKeys, last_known_block: u64) -> BonsolInstruction {
67
1
        BonsolInstruction::outer(
68
1
            self.accounts
69
1
                .into_iter()
70
2
                .filter_map(|idx| acc.get(idx as usize).copied())
71
1
                .collect(),
72
1
            self.data,
73
1
            last_known_block,
74
1
        )
75
1
    }
76
1
    fn program_id_index(&self) -> u8 {
77
1
        self.program_id_index
78
1
    }
79
}
80

            
81
/// Filter instructions that can be converted to `BonsolInstruction`s given
82
/// a closure which returns a boolean representing some condition that must be met
83
/// for an instruction to be converted to `Some(BonsolInstruction)`.
84
2
pub fn filter_bonsol_instructions<'a, I>(
85
2
    ixs: Vec<I>,
86
2
    acc: &'a AccountKeys,
87
2
    program: &'a Pubkey,
88
2
    last_known_block: u64,
89
2
    program_filter: impl Fn(&AccountKeys, &Pubkey, usize) -> bool + 'a,
90
2
) -> impl Iterator<Item = BonsolInstruction> + 'a
91
2
where
92
2
    I: IntoBonsolInstruction + 'a,
93
2
{
94
2
    ixs.into_iter().filter_map(move |ix| {
95
2
        program_filter(acc, program, ix.program_id_index() as usize)
96
2
            .then(|| ix.into_bonsol_ix(acc, last_known_block))
97
2
    })
98
2
}
99

            
100
pub enum CallbackStatus {
101
    Completed,
102
    Failure,
103
}
104

            
105
#[derive(Clone, Debug, PartialEq, Eq)]
106
pub struct ProgramExec {
107
    pub program_id: Pubkey,
108
    pub instruction_prefix: Vec<u8>,
109
}