bonfida_utils/
accounts.rs

1use crate::{borsh_size::BorshSize, WrappedPod};
2use borsh::{BorshDeserialize, BorshSerialize};
3use bytemuck::{bytes_of, NoUninit};
4use solana_program::{
5    instruction::{AccountMeta, Instruction},
6    pubkey::Pubkey,
7};
8
9pub trait InstructionsAccount {
10    fn get_accounts_vec(&self) -> Vec<AccountMeta>;
11
12    fn get_instruction<P: BorshDeserialize + BorshSerialize + BorshSize>(
13        &self,
14        program_id: Pubkey,
15        instruction_id: u8,
16        params: P,
17    ) -> Instruction {
18        let cap = 1 + params.borsh_len();
19        let mut data = Vec::with_capacity(cap);
20        #[allow(clippy::uninit_vec)]
21        unsafe {
22            data.set_len(cap);
23        }
24        data[0] = instruction_id;
25        let mut data_pointer = &mut data[1..];
26        params.serialize(&mut data_pointer).unwrap();
27        // We check that we have written to the whole buffer to ensure that no undefined bytes remain at the end of data.
28        if !data_pointer.is_empty() {
29            panic!()
30        }
31
32        let accounts_vec = self.get_accounts_vec();
33        Instruction {
34            program_id,
35            accounts: accounts_vec,
36            data,
37        }
38    }
39    fn get_instruction_cast<P: NoUninit>(
40        &self,
41        program_id: Pubkey,
42        instruction_id: u8,
43        params: P,
44    ) -> Instruction {
45        let cap = 8 + std::mem::size_of::<P>();
46        let mut data = Vec::with_capacity(cap);
47        data.push(instruction_id);
48        data.extend([0; 7].iter());
49        data.extend(bytes_of(&params));
50
51        Instruction {
52            program_id,
53            accounts: self.get_accounts_vec(),
54            data,
55        }
56    }
57    fn get_instruction_wrapped_pod<'a, P: WrappedPod<'a>>(
58        &self,
59        program_id: Pubkey,
60        instruction_id: u8,
61        params: P,
62    ) -> Instruction {
63        let cap = 8 + params.size();
64        let mut data = Vec::with_capacity(cap);
65        data.push(instruction_id);
66        data.extend([0; 7].iter());
67        params.export(&mut data);
68
69        Instruction {
70            program_id,
71            accounts: self.get_accounts_vec(),
72            data,
73        }
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::InstructionsAccount;
80    use crate::borsh_size::BorshSize;
81    use bonfida_macros::{BorshSize, InstructionsAccount};
82    use borsh::{BorshDeserialize, BorshSerialize};
83    use solana_program::pubkey::Pubkey;
84
85    // #[cfg(feature = "instruction_params_casting")]
86    // use bytemuck::{Pod, Zeroable};
87
88    mod accounts {
89        use super::*;
90        #[derive(InstructionsAccount, Clone)]
91        pub struct Accounts<'a, T> {
92            #[cons(writable)]
93            pub a: &'a T,
94            pub b: &'a T,
95            #[cons(writable)]
96            pub c: &'a [T],
97            pub d: &'a [T],
98            #[cons(writable, signer)]
99            pub e: &'a T,
100            #[cons(signer)]
101            pub f: &'a T,
102            #[cons(writable, signer)]
103            pub g: &'a [T],
104            #[cons(signer)]
105            pub h: &'a [T],
106            #[cons(signer)]
107            pub i: Option<&'a T>,
108        }
109    }
110    #[derive(BorshDeserialize, BorshSerialize, BorshSize, Clone, Copy)]
111    // #[cfg_attr(feature = "instruction_params_casting", derive(Zeroable, Pod))]
112    #[repr(C)]
113    pub struct Params {
114        pub match_limit: u64,
115    }
116
117    mod accounts_with_vec {
118        use super::*;
119        #[derive(InstructionsAccount, Clone)]
120        pub struct Accounts<'a, T> {
121            #[cons(writable)]
122            pub a: &'a T,
123            pub b: &'a T,
124            #[cons(writable)]
125            pub c: &'a [T],
126            pub d: &'a [T],
127            #[cons(writable, signer)]
128            pub e: &'a T,
129            #[cons(signer)]
130            pub f: &'a T,
131            #[cons(writable, signer)]
132            pub g: &'a [T],
133            #[cons(signer)]
134            pub h: &'a [T],
135            #[cons(signer)]
136            pub i: Vec<&'a T>,
137        }
138    }
139
140    #[test]
141    #[allow(clippy::bool_assert_comparison)]
142    fn functional_0() {
143        let i = Pubkey::new_unique();
144        let a = accounts::Accounts {
145            a: &Pubkey::new_unique(),
146            b: &Pubkey::new_unique(),
147            c: &[Pubkey::new_unique()],
148            d: &[Pubkey::new_unique()],
149            e: &Pubkey::new_unique(),
150            f: &Pubkey::new_unique(),
151            g: &[Pubkey::new_unique()],
152            h: &[Pubkey::new_unique()],
153            i: Some(&i),
154        };
155        let params = Params { match_limit: 46 };
156        let instruction = a.get_instruction(crate::ID, 0, params);
157        assert_eq!(instruction.accounts[0].is_writable, true);
158        assert_eq!(instruction.accounts[0].is_signer, false);
159        assert_eq!(instruction.accounts[0].pubkey, *a.a);
160        assert_eq!(instruction.accounts[1].is_writable, false);
161        assert_eq!(instruction.accounts[1].is_signer, false);
162        assert_eq!(instruction.accounts[1].pubkey, *a.b);
163        assert_eq!(instruction.accounts[2].is_writable, true);
164        assert_eq!(instruction.accounts[2].is_signer, false);
165        assert_eq!(instruction.accounts[2].pubkey, a.c[0]);
166        assert_eq!(instruction.accounts[3].is_writable, false);
167        assert_eq!(instruction.accounts[3].is_signer, false);
168        assert_eq!(instruction.accounts[3].pubkey, a.d[0]);
169
170        assert_eq!(instruction.accounts[4].is_writable, true);
171        assert_eq!(instruction.accounts[4].is_signer, true);
172        assert_eq!(instruction.accounts[4].pubkey, *a.e);
173        assert_eq!(instruction.accounts[5].is_writable, false);
174        assert_eq!(instruction.accounts[5].is_signer, true);
175        assert_eq!(instruction.accounts[5].pubkey, *a.f);
176        assert_eq!(instruction.accounts[6].is_writable, true);
177        assert_eq!(instruction.accounts[6].is_signer, true);
178        assert_eq!(instruction.accounts[6].pubkey, a.g[0]);
179        assert_eq!(instruction.accounts[7].is_writable, false);
180        assert_eq!(instruction.accounts[7].is_signer, true);
181        assert_eq!(instruction.accounts[7].pubkey, a.h[0]);
182        assert_eq!(instruction.accounts[8].is_writable, false);
183        assert_eq!(instruction.accounts[8].is_signer, true);
184        assert_eq!(instruction.accounts[8].pubkey, *a.i.unwrap());
185        let mut instruction_data = vec![0];
186        instruction_data.extend(&params.try_to_vec().unwrap());
187
188        assert_eq!(instruction_data, instruction.data);
189
190        // #[cfg(feature = "instruction_params_casting")]
191        // {
192        //     let instruction = a.get_instruction_cast(crate::ID, 0, params);
193        //     let mut instruction_data = [0; 8].to_vec();
194        //     instruction_data.extend(bytemuck::bytes_of(&params));
195
196        //     assert_eq!(instruction_data, instruction.data);
197        // }
198    }
199
200    #[test]
201    #[allow(clippy::bool_assert_comparison)]
202    fn functional_1() {
203        let i = (0..4).map(|_| Pubkey::new_unique()).collect::<Vec<_>>();
204        let a = accounts_with_vec::Accounts {
205            a: &Pubkey::new_unique(),
206            b: &Pubkey::new_unique(),
207            c: &[Pubkey::new_unique()],
208            d: &[Pubkey::new_unique()],
209            e: &Pubkey::new_unique(),
210            f: &Pubkey::new_unique(),
211            g: &[Pubkey::new_unique()],
212            h: &[Pubkey::new_unique()],
213            i: vec![&i[0], &i[1], &i[2], &i[3]],
214        };
215        let params = Params { match_limit: 46 };
216        let instruction = a.get_instruction(crate::ID, 0, params);
217        assert_eq!(instruction.accounts[0].is_writable, true);
218        assert_eq!(instruction.accounts[0].is_signer, false);
219        assert_eq!(instruction.accounts[0].pubkey, *a.a);
220        assert_eq!(instruction.accounts[1].is_writable, false);
221        assert_eq!(instruction.accounts[1].is_signer, false);
222        assert_eq!(instruction.accounts[1].pubkey, *a.b);
223        assert_eq!(instruction.accounts[2].is_writable, true);
224        assert_eq!(instruction.accounts[2].is_signer, false);
225        assert_eq!(instruction.accounts[2].pubkey, a.c[0]);
226        assert_eq!(instruction.accounts[3].is_writable, false);
227        assert_eq!(instruction.accounts[3].is_signer, false);
228        assert_eq!(instruction.accounts[3].pubkey, a.d[0]);
229
230        assert_eq!(instruction.accounts[4].is_writable, true);
231        assert_eq!(instruction.accounts[4].is_signer, true);
232        assert_eq!(instruction.accounts[4].pubkey, *a.e);
233        assert_eq!(instruction.accounts[5].is_writable, false);
234        assert_eq!(instruction.accounts[5].is_signer, true);
235        assert_eq!(instruction.accounts[5].pubkey, *a.f);
236        assert_eq!(instruction.accounts[6].is_writable, true);
237        assert_eq!(instruction.accounts[6].is_signer, true);
238        assert_eq!(instruction.accounts[6].pubkey, a.g[0]);
239        assert_eq!(instruction.accounts[7].is_writable, false);
240        assert_eq!(instruction.accounts[7].is_signer, true);
241        assert_eq!(instruction.accounts[7].pubkey, a.h[0]);
242        assert_eq!(instruction.accounts[8].is_writable, false);
243        assert_eq!(instruction.accounts[8].is_signer, true);
244        assert_eq!(instruction.accounts[8].pubkey, *a.i[0]);
245        assert_eq!(instruction.accounts[9].is_writable, false);
246        assert_eq!(instruction.accounts[9].is_signer, true);
247        assert_eq!(instruction.accounts[9].pubkey, *a.i[1]);
248        assert_eq!(instruction.accounts[10].is_writable, false);
249        assert_eq!(instruction.accounts[10].is_signer, true);
250        assert_eq!(instruction.accounts[10].pubkey, *a.i[2]);
251        let mut instruction_data = vec![0];
252        instruction_data.extend(&params.try_to_vec().unwrap());
253
254        assert_eq!(instruction_data, instruction.data);
255
256        // #[cfg(feature = "instruction_params_casting")]
257        // {
258        //     let instruction = a.get_instruction_cast(crate::ID, 0, params);
259        //     let mut instruction_data = [0; 8].to_vec();
260        //     instruction_data.extend(bytemuck::bytes_of(&params));
261
262        //     assert_eq!(instruction_data, instruction.data);
263        // }
264    }
265}