pinocchio_token/instructions/
approve.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 Approve<'account, 'multisig, MultisigSigner: AsRef<AccountView>> {
33 pub source: &'account AccountView,
35
36 pub delegate: &'account AccountView,
38
39 pub authority: &'account AccountView,
41
42 pub multisig_signers: &'multisig [MultisigSigner],
44
45 pub amount: u64,
47}
48
49impl<'account> Approve<'account, '_, &'account AccountView> {
50 pub const DISCRIMINATOR: u8 = 4;
52
53 pub const MAX_ACCOUNTS_LEN: usize = 3 + MAX_MULTISIG_SIGNERS;
59
60 pub const DATA_LEN: usize = 9;
64
65 #[inline(always)]
67 pub fn new(
68 source: &'account AccountView,
69 delegate: &'account AccountView,
70 authority: &'account AccountView,
71 amount: u64,
72 ) -> Self {
73 Self::with_multisig_signers(source, delegate, authority, amount, &[])
74 }
75}
76
77impl<'account, 'multisig, MultisigSigner: AsRef<AccountView>>
78 Approve<'account, 'multisig, MultisigSigner>
79{
80 #[inline(always)]
83 pub fn with_multisig_signers(
84 source: &'account AccountView,
85 delegate: &'account AccountView,
86 authority: &'account AccountView,
87 amount: u64,
88 multisig_signers: &'multisig [MultisigSigner],
89 ) -> Self {
90 Self {
91 source,
92 delegate,
93 authority,
94 multisig_signers,
95 amount,
96 }
97 }
98
99 #[inline(always)]
100 pub fn invoke(&self) -> ProgramResult {
101 self.invoke_signed(&[])
102 }
103
104 #[inline(always)]
105 pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
106 if self.multisig_signers.len() > MAX_MULTISIG_SIGNERS {
107 Err(ProgramError::InvalidArgument)?;
108 }
109
110 let mut instruction_accounts = [UNINIT_INSTRUCTION_ACCOUNT; Approve::MAX_ACCOUNTS_LEN];
111 let written_instruction_accounts =
112 self.write_instruction_accounts(&mut instruction_accounts)?;
113
114 let mut accounts = [UNINIT_CPI_ACCOUNT; Approve::MAX_ACCOUNTS_LEN];
115 let written_accounts = self.write_accounts(&mut accounts)?;
116
117 let mut instruction_data = [UNINIT_BYTE; Approve::DATA_LEN];
118 let written_instruction_data = self.write_instruction_data(&mut instruction_data)?;
119
120 unsafe {
121 invoke_signed_unchecked(
122 &InstructionView {
123 program_id: &crate::ID,
124 accounts: from_raw_parts(
125 instruction_accounts.as_ptr() as _,
126 written_instruction_accounts,
127 ),
128 data: from_raw_parts(instruction_data.as_ptr() as _, written_instruction_data),
129 },
130 from_raw_parts(accounts.as_ptr() as _, written_accounts),
131 signers,
132 );
133 }
134
135 Ok(())
136 }
137}
138
139impl<MultisigSigner: AsRef<AccountView>> CpiWriter for Approve<'_, '_, MultisigSigner> {
140 #[inline(always)]
141 fn write_accounts<'cpi>(
142 &self,
143 accounts: &mut [MaybeUninit<CpiAccount<'cpi>>],
144 ) -> Result<usize, ProgramError>
145 where
146 Self: 'cpi,
147 {
148 write_accounts(
149 self.source,
150 self.delegate,
151 self.authority,
152 self.multisig_signers,
153 accounts,
154 )
155 }
156
157 #[inline(always)]
158 fn write_instruction_accounts<'cpi>(
159 &self,
160 accounts: &mut [MaybeUninit<InstructionAccount<'cpi>>],
161 ) -> Result<usize, ProgramError>
162 where
163 Self: 'cpi,
164 {
165 write_instruction_accounts(
166 self.source,
167 self.delegate,
168 self.authority,
169 self.multisig_signers,
170 accounts,
171 )
172 }
173
174 #[inline(always)]
175 fn write_instruction_data(&self, data: &mut [MaybeUninit<u8>]) -> Result<usize, ProgramError> {
176 write_instruction_data(self.amount, data)
177 }
178}
179
180impl<MultisigSigner: AsRef<AccountView>> super::IntoBatch for Approve<'_, '_, MultisigSigner> {
181 #[inline(always)]
182 fn into_batch<'account, 'state>(
183 self,
184 batch: &mut super::Batch<'account, 'state>,
185 ) -> ProgramResult
186 where
187 Self: 'account + 'state,
188 {
189 let Self {
190 source,
191 delegate,
192 authority,
193 multisig_signers,
194 amount,
195 } = self;
196
197 batch.push(
198 |accounts| write_accounts(source, delegate, authority, multisig_signers, accounts),
199 |accounts| {
200 write_instruction_accounts(source, delegate, authority, multisig_signers, accounts)
201 },
202 |data| write_instruction_data(amount, data),
203 )
204 }
205}
206
207#[inline(always)]
208fn write_accounts<'account, 'multisig, 'out, MultisigSigner: AsRef<AccountView>>(
209 source: &'account AccountView,
210 delegate: &'account AccountView,
211 authority: &'account AccountView,
212 multisig_signers: &'multisig [MultisigSigner],
213 accounts: &mut [MaybeUninit<CpiAccount<'out>>],
214) -> Result<usize, ProgramError>
215where
216 'account: 'out,
217 'multisig: 'out,
218{
219 let expected_accounts = 3 + multisig_signers.len();
220
221 if expected_accounts > accounts.len() {
222 return Err(invalid_argument_error());
223 }
224
225 if source.is_borrowed() {
226 return Err(account_borrow_failed_error());
227 }
228
229 CpiAccount::init_from_account_view(source, &mut accounts[0]);
230
231 CpiAccount::init_from_account_view(delegate, &mut accounts[1]);
232
233 CpiAccount::init_from_account_view(authority, &mut accounts[2]);
234
235 for (account, signer) in accounts[3..expected_accounts]
236 .iter_mut()
237 .zip(multisig_signers.iter())
238 {
239 CpiAccount::init_from_account_view(signer.as_ref(), account);
240 }
241
242 Ok(expected_accounts)
243}
244
245#[inline(always)]
246fn write_instruction_accounts<'account, 'multisig, 'out, MultisigSigner: AsRef<AccountView>>(
247 source: &'account AccountView,
248 delegate: &'account AccountView,
249 authority: &'account AccountView,
250 multisig_signers: &'multisig [MultisigSigner],
251 accounts: &mut [MaybeUninit<InstructionAccount<'out>>],
252) -> Result<usize, ProgramError>
253where
254 'account: 'out,
255 'multisig: 'out,
256{
257 let expected_accounts = 3 + multisig_signers.len();
258
259 if expected_accounts > accounts.len() {
260 return Err(invalid_argument_error());
261 }
262
263 accounts[0].write(InstructionAccount::writable(source.address()));
264
265 accounts[1].write(InstructionAccount::readonly(delegate.address()));
266
267 accounts[2].write(InstructionAccount::new(
268 authority.address(),
269 false,
270 multisig_signers.is_empty(),
271 ));
272
273 for (account, signer) in accounts[3..expected_accounts]
274 .iter_mut()
275 .zip(multisig_signers.iter())
276 {
277 account.write(InstructionAccount::readonly_signer(
278 signer.as_ref().address(),
279 ));
280 }
281
282 Ok(expected_accounts)
283}
284
285#[inline(always)]
286fn write_instruction_data(
287 amount: u64,
288 data: &mut [MaybeUninit<u8>],
289) -> Result<usize, ProgramError> {
290 if data.len() < Approve::DATA_LEN {
291 return Err(invalid_argument_error());
292 }
293
294 data[0].write(Approve::DISCRIMINATOR);
295
296 write_bytes(&mut data[1..Approve::DATA_LEN], &amount.to_le_bytes());
297
298 Ok(Approve::DATA_LEN)
299}