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