pinocchio_token/instructions/
approve_checked.rs1use {
2 crate::{
3 instructions::{
4 account_borrow_failed_error, invalid_argument_error, CpiWriter, MAX_MULTISIG_SIGNERS,
5 },
6 write_bytes, UNINIT_BYTE, UNINIT_CPI_ACCOUNT, UNINIT_INSTRUCTION_ACCOUNT,
7 },
8 core::{mem::MaybeUninit, slice::from_raw_parts},
9 solana_account_view::AccountView,
10 solana_instruction_view::{
11 cpi::{invoke_signed_unchecked, CpiAccount, Signer},
12 InstructionAccount, InstructionView,
13 },
14 solana_program_error::{ProgramError, ProgramResult},
15};
16
17pub struct ApproveChecked<'account, 'multisig, MultisigSigner: AsRef<AccountView>> {
39 pub source: &'account AccountView,
41
42 pub mint: &'account AccountView,
44
45 pub delegate: &'account AccountView,
47
48 pub authority: &'account AccountView,
50
51 pub multisig_signers: &'multisig [MultisigSigner],
53
54 pub amount: u64,
56
57 pub decimals: u8,
59}
60
61impl<'account> ApproveChecked<'account, '_, &'account AccountView> {
62 pub const DISCRIMINATOR: u8 = 13;
64
65 pub const MAX_ACCOUNTS_LEN: usize = 4 + MAX_MULTISIG_SIGNERS;
71
72 pub const DATA_LEN: usize = 10;
77
78 #[inline(always)]
81 pub fn new(
82 source: &'account AccountView,
83 mint: &'account AccountView,
84 delegate: &'account AccountView,
85 authority: &'account AccountView,
86 amount: u64,
87 decimals: u8,
88 ) -> Self {
89 Self::with_multisig_signers(source, mint, delegate, authority, amount, decimals, &[])
90 }
91}
92
93impl<'account, 'multisig, MultisigSigner: AsRef<AccountView>>
94 ApproveChecked<'account, 'multisig, MultisigSigner>
95{
96 #[inline(always)]
99 pub fn with_multisig_signers(
100 source: &'account AccountView,
101 mint: &'account AccountView,
102 delegate: &'account AccountView,
103 authority: &'account AccountView,
104 amount: u64,
105 decimals: u8,
106 multisig_signers: &'multisig [MultisigSigner],
107 ) -> Self {
108 Self {
109 source,
110 mint,
111 delegate,
112 authority,
113 multisig_signers,
114 amount,
115 decimals,
116 }
117 }
118
119 #[inline(always)]
120 pub fn invoke(&self) -> ProgramResult {
121 self.invoke_signed(&[])
122 }
123
124 #[inline(always)]
125 pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
126 if self.multisig_signers.len() > MAX_MULTISIG_SIGNERS {
127 Err(ProgramError::InvalidArgument)?;
128 }
129
130 let mut instruction_accounts =
131 [UNINIT_INSTRUCTION_ACCOUNT; ApproveChecked::MAX_ACCOUNTS_LEN];
132 let written_instruction_accounts =
133 self.write_instruction_accounts(&mut instruction_accounts)?;
134
135 let mut accounts = [UNINIT_CPI_ACCOUNT; ApproveChecked::MAX_ACCOUNTS_LEN];
136 let written_accounts = self.write_accounts(&mut accounts)?;
137
138 let mut instruction_data = [UNINIT_BYTE; ApproveChecked::DATA_LEN];
139 let written_instruction_data = self.write_instruction_data(&mut instruction_data)?;
140
141 unsafe {
142 invoke_signed_unchecked(
143 &InstructionView {
144 program_id: &crate::ID,
145 accounts: from_raw_parts(
146 instruction_accounts.as_ptr() as _,
147 written_instruction_accounts,
148 ),
149 data: from_raw_parts(instruction_data.as_ptr() as _, written_instruction_data),
150 },
151 from_raw_parts(accounts.as_ptr() as _, written_accounts),
152 signers,
153 );
154 }
155
156 Ok(())
157 }
158}
159
160impl<MultisigSigner: AsRef<AccountView>> CpiWriter for ApproveChecked<'_, '_, MultisigSigner> {
161 #[inline(always)]
162 fn write_accounts<'cpi>(
163 &self,
164 accounts: &mut [MaybeUninit<CpiAccount<'cpi>>],
165 ) -> Result<usize, ProgramError>
166 where
167 Self: 'cpi,
168 {
169 write_accounts(
170 self.source,
171 self.mint,
172 self.delegate,
173 self.authority,
174 self.multisig_signers,
175 accounts,
176 )
177 }
178
179 #[inline(always)]
180 fn write_instruction_accounts<'cpi>(
181 &self,
182 accounts: &mut [MaybeUninit<InstructionAccount<'cpi>>],
183 ) -> Result<usize, ProgramError>
184 where
185 Self: 'cpi,
186 {
187 write_instruction_accounts(
188 self.source,
189 self.mint,
190 self.delegate,
191 self.authority,
192 self.multisig_signers,
193 accounts,
194 )
195 }
196
197 #[inline(always)]
198 fn write_instruction_data(&self, data: &mut [MaybeUninit<u8>]) -> Result<usize, ProgramError> {
199 write_instruction_data(self.amount, self.decimals, data)
200 }
201}
202
203impl<MultisigSigner: AsRef<AccountView>> super::IntoBatch
204 for ApproveChecked<'_, '_, MultisigSigner>
205{
206 #[inline(always)]
207 fn into_batch<'account, 'state>(
208 self,
209 batch: &mut super::Batch<'account, 'state>,
210 ) -> ProgramResult
211 where
212 Self: 'account + 'state,
213 {
214 batch.push(
215 |accounts| {
216 write_accounts(
217 self.source,
218 self.mint,
219 self.delegate,
220 self.authority,
221 self.multisig_signers,
222 accounts,
223 )
224 },
225 |accounts| {
226 write_instruction_accounts(
227 self.source,
228 self.mint,
229 self.delegate,
230 self.authority,
231 self.multisig_signers,
232 accounts,
233 )
234 },
235 |data| write_instruction_data(self.amount, self.decimals, data),
236 )
237 }
238}
239
240#[inline(always)]
241fn write_accounts<'account, 'multisig, 'out, MultisigSigner: AsRef<AccountView>>(
242 source: &'account AccountView,
243 mint: &'account AccountView,
244 delegate: &'account AccountView,
245 authority: &'account AccountView,
246 multisig_signers: &'multisig [MultisigSigner],
247 accounts: &mut [MaybeUninit<CpiAccount<'out>>],
248) -> Result<usize, ProgramError>
249where
250 'account: 'out,
251 'multisig: 'out,
252{
253 let expected_accounts = 4 + multisig_signers.len();
254
255 if expected_accounts > accounts.len() {
256 return Err(invalid_argument_error());
257 }
258
259 if source.is_borrowed() {
260 return Err(account_borrow_failed_error());
261 }
262
263 CpiAccount::init_from_account_view(source, &mut accounts[0]);
264
265 CpiAccount::init_from_account_view(mint, &mut accounts[1]);
266
267 CpiAccount::init_from_account_view(delegate, &mut accounts[2]);
268
269 CpiAccount::init_from_account_view(authority, &mut accounts[3]);
270
271 for (account, signer) in accounts[4..expected_accounts]
272 .iter_mut()
273 .zip(multisig_signers.iter())
274 {
275 CpiAccount::init_from_account_view(signer.as_ref(), account);
276 }
277
278 Ok(expected_accounts)
279}
280
281#[inline(always)]
282fn write_instruction_accounts<'account, 'multisig, 'out, MultisigSigner: AsRef<AccountView>>(
283 source: &'account AccountView,
284 mint: &'account AccountView,
285 delegate: &'account AccountView,
286 authority: &'account AccountView,
287 multisig_signers: &'multisig [MultisigSigner],
288 accounts: &mut [MaybeUninit<InstructionAccount<'out>>],
289) -> Result<usize, ProgramError>
290where
291 'account: 'out,
292 'multisig: 'out,
293{
294 let expected_accounts = 4 + multisig_signers.len();
295
296 if expected_accounts > accounts.len() {
297 return Err(invalid_argument_error());
298 }
299
300 accounts[0].write(InstructionAccount::writable(source.address()));
301
302 accounts[1].write(InstructionAccount::readonly(mint.address()));
303
304 accounts[2].write(InstructionAccount::readonly(delegate.address()));
305
306 accounts[3].write(InstructionAccount::new(
307 authority.address(),
308 false,
309 multisig_signers.is_empty(),
310 ));
311
312 for (account, signer) in accounts[4..expected_accounts]
313 .iter_mut()
314 .zip(multisig_signers.iter())
315 {
316 account.write(InstructionAccount::readonly_signer(
317 signer.as_ref().address(),
318 ));
319 }
320
321 Ok(expected_accounts)
322}
323
324#[inline(always)]
325fn write_instruction_data(
326 amount: u64,
327 decimals: u8,
328 data: &mut [MaybeUninit<u8>],
329) -> Result<usize, ProgramError> {
330 if data.len() < ApproveChecked::DATA_LEN {
331 return Err(invalid_argument_error());
332 }
333
334 data[0].write(ApproveChecked::DISCRIMINATOR);
335
336 write_bytes(&mut data[1..9], &amount.to_le_bytes());
337
338 data[9].write(decimals);
339
340 Ok(ApproveChecked::DATA_LEN)
341}