1use crate::account::AccountView;
13use crate::address::Address;
14use crate::error::ProgramError;
15use crate::instruction::{InstructionAccount, Signer};
16use crate::token::{
17 authority_meta, encoders, require_authority_signed_direct, require_multisig_signers_direct,
18 token_program_methods, Invoke, TokenInstruction, TokenProgram, TokenSink, Trailing,
19 MAX_TOKEN_MULTISIG_SIGNERS,
20};
21use crate::ProgramResult;
22
23pub use crate::token::encoders::{MAX_EXTENSION_TYPES, MAX_UI_AMOUNT_LEN};
24
25pub struct InitializeMint<'a> {
33 pub mint: &'a AccountView<'a>,
34 pub rent_sysvar: &'a AccountView<'a>,
35 pub decimals: u8,
36 pub mint_authority: &'a Address,
37 pub freeze_authority: Option<&'a Address>,
38}
39
40impl InitializeMint<'_> {
41 #[inline]
42 pub fn invoke(&self) -> ProgramResult {
43 self.emit(&[], &mut Invoke::legacy(&[]))
44 }
45}
46
47impl<'a, 'x: 'a> TokenInstruction<'a> for InitializeMint<'x> {
48 #[inline(always)]
49 fn emit(
50 &self,
51 multisig_signers: &[&'a AccountView<'a>],
52 sink: &mut impl TokenSink<'a>,
53 ) -> ProgramResult {
54 let (data, len) = encoders::encode_initialize_mint(
55 self.decimals,
56 self.mint_authority.as_array(),
57 self.freeze_authority.map(|a| a.as_array()),
58 );
59 let accounts = [
60 InstructionAccount::writable(self.mint.address()),
61 InstructionAccount::readonly(self.rent_sysvar.address()),
62 ];
63 let views = [self.mint, self.rent_sysvar];
64 sink.emit(
65 &data[..len],
66 accounts,
67 views,
68 &[Trailing::signers(multisig_signers)],
69 )
70 }
71}
72
73token_program_methods!(InitializeMint, owner = mint);
74
75#[inline(always)]
80fn check_multisig_shape(m: u8, signers: usize) -> ProgramResult {
81 if m == 0 || signers == 0 || signers > MAX_TOKEN_MULTISIG_SIGNERS || usize::from(m) > signers {
82 return Err(ProgramError::InvalidArgument);
83 }
84 Ok(())
85}
86
87pub struct InitializeMultisig<'a> {
92 pub multisig: &'a AccountView<'a>,
93 pub rent_sysvar: &'a AccountView<'a>,
94 pub signers: &'a [&'a AccountView<'a>],
96 pub m: u8,
98}
99
100impl InitializeMultisig<'_> {
101 #[inline]
102 pub fn invoke(&self) -> ProgramResult {
103 self.emit(&[], &mut Invoke::legacy(&[]))
104 }
105}
106
107impl<'a, 'x: 'a> TokenInstruction<'a> for InitializeMultisig<'x> {
108 #[inline(always)]
109 fn emit(
110 &self,
111 _multisig_signers: &[&'a AccountView<'a>],
112 sink: &mut impl TokenSink<'a>,
113 ) -> ProgramResult {
114 check_multisig_shape(self.m, self.signers.len())?;
115 let data = encoders::encode_initialize_multisig(self.m);
116 let accounts = [
117 InstructionAccount::writable(self.multisig.address()),
118 InstructionAccount::readonly(self.rent_sysvar.address()),
119 ];
120 let views = [self.multisig, self.rent_sysvar];
121 sink.emit(&data, accounts, views, &[Trailing::readonly(self.signers)])
122 }
123}
124
125token_program_methods!(InitializeMultisig, owner = multisig);
126
127pub struct InitializeMultisig2<'a> {
130 pub multisig: &'a AccountView<'a>,
131 pub signers: &'a [&'a AccountView<'a>],
133 pub m: u8,
135}
136
137impl InitializeMultisig2<'_> {
138 #[inline]
139 pub fn invoke(&self) -> ProgramResult {
140 self.emit(&[], &mut Invoke::legacy(&[]))
141 }
142}
143
144impl<'a, 'x: 'a> TokenInstruction<'a> for InitializeMultisig2<'x> {
145 #[inline(always)]
146 fn emit(
147 &self,
148 _multisig_signers: &[&'a AccountView<'a>],
149 sink: &mut impl TokenSink<'a>,
150 ) -> ProgramResult {
151 check_multisig_shape(self.m, self.signers.len())?;
152 let data = encoders::encode_initialize_multisig2(self.m);
153 let accounts = [InstructionAccount::writable(self.multisig.address())];
154 let views = [self.multisig];
155 sink.emit(&data, accounts, views, &[Trailing::readonly(self.signers)])
156 }
157}
158
159token_program_methods!(InitializeMultisig2, owner = multisig);
160
161pub struct InitializeImmutableOwner<'a> {
169 pub account: &'a AccountView<'a>,
170}
171
172impl InitializeImmutableOwner<'_> {
173 #[inline]
174 pub fn invoke(&self) -> ProgramResult {
175 self.emit(&[], &mut Invoke::legacy(&[]))
176 }
177}
178
179impl<'a, 'x: 'a> TokenInstruction<'a> for InitializeImmutableOwner<'x> {
180 #[inline(always)]
181 fn emit(
182 &self,
183 multisig_signers: &[&'a AccountView<'a>],
184 sink: &mut impl TokenSink<'a>,
185 ) -> ProgramResult {
186 let data = encoders::encode_initialize_immutable_owner();
187 let accounts = [InstructionAccount::writable(self.account.address())];
188 let views = [self.account];
189 sink.emit(
190 &data,
191 accounts,
192 views,
193 &[Trailing::signers(multisig_signers)],
194 )
195 }
196}
197
198token_program_methods!(InitializeImmutableOwner, owner = account);
199
200pub struct GetAccountDataSize<'a> {
209 pub mint: &'a AccountView<'a>,
210 pub extension_types: &'a [u16],
211}
212
213impl GetAccountDataSize<'_> {
214 #[inline]
215 pub fn invoke(&self) -> ProgramResult {
216 self.emit(&[], &mut Invoke::legacy(&[]))
217 }
218
219 #[inline]
221 pub fn query(&self, program: TokenProgram) -> Result<u64, ProgramError> {
222 self.invoke_on(program, &[], &[])?;
223 return_data_u64(program.address())
224 }
225
226 #[inline]
228 pub fn query_for_owner(&self) -> Result<u64, ProgramError> {
229 self.query(TokenProgram::owning(self.mint)?)
230 }
231}
232
233impl<'a, 'x: 'a> TokenInstruction<'a> for GetAccountDataSize<'x> {
234 #[inline(always)]
235 fn emit(
236 &self,
237 multisig_signers: &[&'a AccountView<'a>],
238 sink: &mut impl TokenSink<'a>,
239 ) -> ProgramResult {
240 let (data, len) = encoders::encode_extension_types(21, self.extension_types)
241 .ok_or(ProgramError::InvalidArgument)?;
242 let accounts = [InstructionAccount::readonly(self.mint.address())];
243 let views = [self.mint];
244 sink.emit(
245 &data[..len],
246 accounts,
247 views,
248 &[Trailing::signers(multisig_signers)],
249 )
250 }
251}
252
253token_program_methods!(GetAccountDataSize, owner = mint);
254
255pub struct AmountToUiAmount<'a> {
261 pub mint: &'a AccountView<'a>,
262 pub amount: u64,
263}
264
265impl AmountToUiAmount<'_> {
266 #[inline]
267 pub fn invoke(&self) -> ProgramResult {
268 self.emit(&[], &mut Invoke::legacy(&[]))
269 }
270
271 #[inline]
274 pub fn query(
275 &self,
276 program: TokenProgram,
277 out: &mut [u8; MAX_UI_AMOUNT_LEN],
278 ) -> Result<usize, ProgramError> {
279 self.invoke_on(program, &[], &[])?;
280 return_data_string(program.address(), out)
281 }
282}
283
284impl<'a, 'x: 'a> TokenInstruction<'a> for AmountToUiAmount<'x> {
285 #[inline(always)]
286 fn emit(
287 &self,
288 multisig_signers: &[&'a AccountView<'a>],
289 sink: &mut impl TokenSink<'a>,
290 ) -> ProgramResult {
291 let data = encoders::encode_amount_to_ui_amount(self.amount);
292 let accounts = [InstructionAccount::readonly(self.mint.address())];
293 let views = [self.mint];
294 sink.emit(
295 &data,
296 accounts,
297 views,
298 &[Trailing::signers(multisig_signers)],
299 )
300 }
301}
302
303token_program_methods!(AmountToUiAmount, owner = mint);
304
305pub struct UiAmountToAmount<'a> {
310 pub mint: &'a AccountView<'a>,
311 pub ui_amount: &'a str,
312}
313
314impl UiAmountToAmount<'_> {
315 #[inline]
316 pub fn invoke(&self) -> ProgramResult {
317 self.emit(&[], &mut Invoke::legacy(&[]))
318 }
319
320 #[inline]
322 pub fn query(&self, program: TokenProgram) -> Result<u64, ProgramError> {
323 self.invoke_on(program, &[], &[])?;
324 return_data_u64(program.address())
325 }
326}
327
328impl<'a, 'x: 'a> TokenInstruction<'a> for UiAmountToAmount<'x> {
329 #[inline(always)]
330 fn emit(
331 &self,
332 multisig_signers: &[&'a AccountView<'a>],
333 sink: &mut impl TokenSink<'a>,
334 ) -> ProgramResult {
335 let (data, len) = encoders::encode_ui_amount_to_amount(self.ui_amount)
336 .ok_or(ProgramError::InvalidArgument)?;
337 let accounts = [InstructionAccount::readonly(self.mint.address())];
338 let views = [self.mint];
339 sink.emit(
340 &data[..len],
341 accounts,
342 views,
343 &[Trailing::signers(multisig_signers)],
344 )
345 }
346}
347
348token_program_methods!(UiAmountToAmount, owner = mint);
349
350pub struct WithdrawExcessLamports<'a> {
358 pub source: &'a AccountView<'a>,
359 pub destination: &'a AccountView<'a>,
360 pub authority: &'a AccountView<'a>,
361}
362
363impl WithdrawExcessLamports<'_> {
364 #[inline]
365 pub fn invoke(&self) -> ProgramResult {
366 require_authority_signed_direct(self.authority)?;
367 self.emit(&[], &mut Invoke::legacy(&[]))
368 }
369
370 #[inline]
371 pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
372 self.emit(&[], &mut Invoke::legacy(signers))
373 }
374
375 #[inline]
376 pub fn invoke_multisig(&self, multisig_signers: &[&AccountView<'_>]) -> ProgramResult {
377 require_multisig_signers_direct(multisig_signers)?;
378 self.emit(multisig_signers, &mut Invoke::legacy(&[]))
379 }
380}
381
382impl<'a, 'x: 'a> TokenInstruction<'a> for WithdrawExcessLamports<'x> {
383 #[inline(always)]
384 fn emit(
385 &self,
386 multisig_signers: &[&'a AccountView<'a>],
387 sink: &mut impl TokenSink<'a>,
388 ) -> ProgramResult {
389 let data = encoders::encode_withdraw_excess_lamports();
390 let accounts = [
391 InstructionAccount::writable(self.source.address()),
392 InstructionAccount::writable(self.destination.address()),
393 authority_meta(self.authority, multisig_signers),
394 ];
395 let views = [self.source, self.destination, self.authority];
396 sink.emit(
397 &data,
398 accounts,
399 views,
400 &[Trailing::signers(multisig_signers)],
401 )
402 }
403}
404
405token_program_methods!(
406 WithdrawExcessLamports,
407 owner = source,
408 authority = authority
409);
410
411pub struct UnwrapLamports<'a> {
417 pub source: &'a AccountView<'a>,
418 pub destination: &'a AccountView<'a>,
419 pub authority: &'a AccountView<'a>,
420 pub amount: Option<u64>,
421}
422
423impl UnwrapLamports<'_> {
424 #[inline]
425 pub fn invoke(&self) -> ProgramResult {
426 require_authority_signed_direct(self.authority)?;
427 self.emit(&[], &mut Invoke::legacy(&[]))
428 }
429
430 #[inline]
431 pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
432 self.emit(&[], &mut Invoke::legacy(signers))
433 }
434
435 #[inline]
436 pub fn invoke_multisig(&self, multisig_signers: &[&AccountView<'_>]) -> ProgramResult {
437 require_multisig_signers_direct(multisig_signers)?;
438 self.emit(multisig_signers, &mut Invoke::legacy(&[]))
439 }
440}
441
442impl<'a, 'x: 'a> TokenInstruction<'a> for UnwrapLamports<'x> {
443 #[inline(always)]
444 fn emit(
445 &self,
446 multisig_signers: &[&'a AccountView<'a>],
447 sink: &mut impl TokenSink<'a>,
448 ) -> ProgramResult {
449 let (data, len) = encoders::encode_unwrap_lamports(self.amount);
450 let accounts = [
451 InstructionAccount::writable(self.source.address()),
452 InstructionAccount::writable(self.destination.address()),
453 authority_meta(self.authority, multisig_signers),
454 ];
455 let views = [self.source, self.destination, self.authority];
456 sink.emit(
457 &data[..len],
458 accounts,
459 views,
460 &[Trailing::signers(multisig_signers)],
461 )
462 }
463}
464
465token_program_methods!(UnwrapLamports, owner = source, authority = authority);
466
467#[inline]
472pub fn return_data_u64(program: &Address) -> Result<u64, ProgramError> {
473 let returned =
474 crate::return_data::get_return_data().ok_or(ProgramError::InvalidInstructionData)?;
475 if returned.program_id() != program {
476 return Err(ProgramError::IncorrectProgramId);
477 }
478 let bytes: [u8; 8] = returned
479 .data()
480 .get(..8)
481 .and_then(|b| b.try_into().ok())
482 .ok_or(ProgramError::InvalidInstructionData)?;
483 Ok(u64::from_le_bytes(bytes))
484}
485
486#[inline]
490pub fn return_data_string(
491 program: &Address,
492 out: &mut [u8; MAX_UI_AMOUNT_LEN],
493) -> Result<usize, ProgramError> {
494 let returned =
495 crate::return_data::get_return_data().ok_or(ProgramError::InvalidInstructionData)?;
496 if returned.program_id() != program {
497 return Err(ProgramError::IncorrectProgramId);
498 }
499 let data = returned.data();
500 if data.len() > out.len() {
501 return Err(ProgramError::InvalidInstructionData);
502 }
503 out[..data.len()].copy_from_slice(data);
504 Ok(data.len())
505}
506
507#[cfg(test)]
508mod tests {
509 use super::*;
510
511 #[test]
512 fn multisig_shape_is_checked_before_the_cpi() {
513 assert!(check_multisig_shape(1, 1).is_ok());
514 assert!(check_multisig_shape(11, 11).is_ok());
515 assert!(check_multisig_shape(0, 1).is_err());
516 assert!(check_multisig_shape(2, 1).is_err());
517 assert!(check_multisig_shape(1, 0).is_err());
518 assert!(check_multisig_shape(1, 12).is_err());
519 }
520
521 #[test]
522 fn admin_encoders_match_the_token_wire_format() {
523 let authority = [7u8; 32];
524 let freeze = [9u8; 32];
525 let (data, len) = encoders::encode_initialize_mint(6, &authority, Some(&freeze));
526 assert_eq!(len, 67);
527 assert_eq!(&data[..2], &[0, 6]);
528 assert_eq!(&data[2..34], &authority);
529 assert_eq!(data[34], 1);
530 assert_eq!(&data[35..67], &freeze);
531 let (data, len) = encoders::encode_initialize_mint(0, &authority, None);
532 assert_eq!(len, 35);
533 assert_eq!(data[34], 0);
534
535 assert_eq!(encoders::encode_initialize_multisig(2), [2, 2]);
536 assert_eq!(encoders::encode_initialize_multisig2(3), [19, 3]);
537 assert_eq!(encoders::encode_initialize_immutable_owner(), [22]);
538 assert_eq!(encoders::encode_withdraw_excess_lamports(), [38]);
539 assert_eq!(
540 encoders::encode_amount_to_ui_amount(258),
541 [23, 2, 1, 0, 0, 0, 0, 0, 0]
542 );
543
544 let (data, len) = encoders::encode_extension_types(21, &[7, 0x0102]).unwrap();
545 assert_eq!(len, 5);
546 assert_eq!(&data[..5], &[21, 7, 0, 2, 1]);
547 let (data, len) = encoders::encode_extension_types(29, &[]).unwrap();
548 assert_eq!((data[0], len), (29, 1));
549 assert!(encoders::encode_extension_types(21, &[0; MAX_EXTENSION_TYPES + 1]).is_none());
550
551 let (data, len) = encoders::encode_ui_amount_to_amount("1.5").unwrap();
552 assert_eq!(&data[..len], b"\x181.5");
553 let long = [b'1'; MAX_UI_AMOUNT_LEN];
554 let (_, len) =
555 encoders::encode_ui_amount_to_amount(core::str::from_utf8(&long).unwrap()).unwrap();
556 assert_eq!(len, 1 + MAX_UI_AMOUNT_LEN);
557 let too_long = [b'1'; MAX_UI_AMOUNT_LEN + 1];
558 assert!(
559 encoders::encode_ui_amount_to_amount(core::str::from_utf8(&too_long).unwrap())
560 .is_none()
561 );
562
563 let (data, len) = encoders::encode_unwrap_lamports(None);
564 assert_eq!(&data[..len], &[45, 0]);
565 let (data, len) = encoders::encode_unwrap_lamports(Some(1));
566 assert_eq!(&data[..len], &[45, 1, 1, 0, 0, 0, 0, 0, 0, 0]);
567 }
568}