1use crate::account::AccountView;
28use crate::address::Address;
29use crate::error::ProgramError;
30use crate::instruction::{InstructionAccount, Signer};
31use crate::token::{
32 authority_meta, encoders as token_encoders, require_authority_signed_direct,
33 require_multisig_signers_direct, Invoke, TokenInstruction, TokenSink, Trailing,
34};
35use crate::ProgramResult;
36
37#[derive(Clone, Copy)]
39pub struct Bytes<const N: usize> {
40 buf: [u8; N],
41 len: usize,
42}
43
44impl<const N: usize> Bytes<N> {
45 #[inline(always)]
46 const fn new() -> Self {
47 Self {
48 buf: [0; N],
49 len: 0,
50 }
51 }
52
53 #[inline(always)]
54 fn push(&mut self, bytes: &[u8]) {
55 self.buf[self.len..self.len + bytes.len()].copy_from_slice(bytes);
56 self.len += bytes.len();
57 }
58
59 #[inline(always)]
61 fn coption(&mut self, address: Option<&Address>) {
62 match address {
63 Some(address) => {
64 self.push(&[1]);
65 self.push(address.as_array());
66 }
67 None => self.push(&[0]),
68 }
69 }
70
71 #[inline(always)]
73 fn nullable(&mut self, address: Option<&Address>) {
74 match address {
75 Some(address) => self.push(address.as_array()),
76 None => self.push(&[0; 32]),
77 }
78 }
79
80 #[inline(always)]
82 pub fn as_slice(&self) -> &[u8] {
83 &self.buf[..self.len]
84 }
85}
86
87#[inline(always)]
91fn non_zero(address: Option<&Address>) -> ProgramResult {
92 if address.is_some_and(|a| a.as_array() == &[0; 32]) {
93 return Err(ProgramError::InvalidArgument);
94 }
95 Ok(())
96}
97
98pub mod encoders {
105 use super::{non_zero, Bytes};
106 use crate::address::Address;
107 use crate::error::ProgramError;
108 use crate::ProgramResult;
109
110 pub const IX_INITIALIZE_MINT_CLOSE_AUTHORITY: u8 = 25;
111 pub const IX_TRANSFER_FEE: u8 = 26;
112 pub const IX_DEFAULT_ACCOUNT_STATE: u8 = 28;
113 pub const IX_REALLOCATE: u8 = 29;
114 pub const IX_MEMO_TRANSFER: u8 = 30;
115 pub const IX_CREATE_NATIVE_MINT: u8 = 31;
116 pub const IX_INITIALIZE_NON_TRANSFERABLE_MINT: u8 = 32;
117 pub const IX_INTEREST_BEARING_MINT: u8 = 33;
118 pub const IX_CPI_GUARD: u8 = 34;
119 pub const IX_INITIALIZE_PERMANENT_DELEGATE: u8 = 35;
120 pub const IX_TRANSFER_HOOK: u8 = 36;
121 pub const IX_METADATA_POINTER: u8 = 39;
122 pub const IX_GROUP_POINTER: u8 = 40;
123 pub const IX_GROUP_MEMBER_POINTER: u8 = 41;
124 pub const IX_SCALED_UI_AMOUNT: u8 = 43;
125 pub const IX_PAUSABLE: u8 = 44;
126 pub const IX_PERMISSIONED_BURN: u8 = 46;
127
128 pub const ACCOUNT_STATE_INITIALIZED: u8 = 1;
130 pub const ACCOUNT_STATE_FROZEN: u8 = 2;
132
133 pub const MAX_FEE_BASIS_POINTS: u16 = 10_000;
135
136 #[inline(always)]
138 pub fn encode_create_native_mint() -> [u8; 1] {
139 [IX_CREATE_NATIVE_MINT]
140 }
141
142 #[inline(always)]
144 pub fn encode_initialize_non_transferable_mint() -> [u8; 1] {
145 [IX_INITIALIZE_NON_TRANSFERABLE_MINT]
146 }
147
148 #[inline(always)]
150 pub fn encode_initialize_mint_close_authority(close_authority: Option<&Address>) -> Bytes<34> {
151 let mut out = Bytes::new();
152 out.push(&[IX_INITIALIZE_MINT_CLOSE_AUTHORITY]);
153 out.coption(close_authority);
154 out
155 }
156
157 #[inline(always)]
160 pub fn encode_initialize_transfer_fee_config(
161 transfer_fee_config_authority: Option<&Address>,
162 withdraw_withheld_authority: Option<&Address>,
163 transfer_fee_basis_points: u16,
164 maximum_fee: u64,
165 ) -> Result<Bytes<78>, ProgramError> {
166 if transfer_fee_basis_points > MAX_FEE_BASIS_POINTS {
167 return Err(ProgramError::InvalidArgument);
168 }
169 let mut out = Bytes::new();
170 out.push(&[IX_TRANSFER_FEE, 0]);
171 out.coption(transfer_fee_config_authority);
172 out.coption(withdraw_withheld_authority);
173 out.push(&transfer_fee_basis_points.to_le_bytes());
174 out.push(&maximum_fee.to_le_bytes());
175 Ok(out)
176 }
177
178 #[inline(always)]
180 pub fn encode_transfer_checked_with_fee(amount: u64, decimals: u8, fee: u64) -> [u8; 19] {
181 let mut data = [0u8; 19];
182 data[0] = IX_TRANSFER_FEE;
183 data[1] = 1;
184 data[2..10].copy_from_slice(&amount.to_le_bytes());
185 data[10] = decimals;
186 data[11..19].copy_from_slice(&fee.to_le_bytes());
187 data
188 }
189
190 #[inline(always)]
192 pub fn encode_withdraw_withheld_tokens_from_mint() -> [u8; 2] {
193 [IX_TRANSFER_FEE, 2]
194 }
195
196 #[inline(always)]
198 pub fn encode_withdraw_withheld_tokens_from_accounts(num_token_accounts: u8) -> [u8; 3] {
199 [IX_TRANSFER_FEE, 3, num_token_accounts]
200 }
201
202 #[inline(always)]
204 pub fn encode_harvest_withheld_tokens_to_mint() -> [u8; 2] {
205 [IX_TRANSFER_FEE, 4]
206 }
207
208 #[inline(always)]
211 pub fn encode_set_transfer_fee(
212 transfer_fee_basis_points: u16,
213 maximum_fee: u64,
214 ) -> Result<[u8; 12], ProgramError> {
215 if transfer_fee_basis_points > MAX_FEE_BASIS_POINTS {
216 return Err(ProgramError::InvalidArgument);
217 }
218 let mut data = [0u8; 12];
219 data[0] = IX_TRANSFER_FEE;
220 data[1] = 5;
221 data[2..4].copy_from_slice(&transfer_fee_basis_points.to_le_bytes());
222 data[4..12].copy_from_slice(&maximum_fee.to_le_bytes());
223 Ok(data)
224 }
225
226 #[inline(always)]
227 fn check_account_state(state: u8) -> ProgramResult {
228 if state == ACCOUNT_STATE_INITIALIZED || state == ACCOUNT_STATE_FROZEN {
229 Ok(())
230 } else {
231 Err(ProgramError::InvalidArgument)
232 }
233 }
234
235 #[inline(always)]
237 pub fn encode_initialize_default_account_state(state: u8) -> Result<[u8; 3], ProgramError> {
238 check_account_state(state)?;
239 Ok([IX_DEFAULT_ACCOUNT_STATE, 0, state])
240 }
241
242 #[inline(always)]
244 pub fn encode_update_default_account_state(state: u8) -> Result<[u8; 3], ProgramError> {
245 check_account_state(state)?;
246 Ok([IX_DEFAULT_ACCOUNT_STATE, 1, state])
247 }
248
249 #[inline(always)]
251 pub fn encode_memo_transfer(enable: bool) -> [u8; 2] {
252 [IX_MEMO_TRANSFER, u8::from(!enable)]
253 }
254
255 #[inline(always)]
257 pub fn encode_initialize_interest_bearing_mint(
258 rate_authority: Option<&Address>,
259 rate: i16,
260 ) -> Result<Bytes<36>, ProgramError> {
261 non_zero(rate_authority)?;
262 let mut out = Bytes::new();
263 out.push(&[IX_INTEREST_BEARING_MINT, 0]);
264 out.nullable(rate_authority);
265 out.push(&rate.to_le_bytes());
266 Ok(out)
267 }
268
269 #[inline(always)]
271 pub fn encode_update_interest_rate(rate: i16) -> [u8; 4] {
272 let rate = rate.to_le_bytes();
273 [IX_INTEREST_BEARING_MINT, 1, rate[0], rate[1]]
274 }
275
276 #[inline(always)]
278 pub fn encode_cpi_guard(enable: bool) -> [u8; 2] {
279 [IX_CPI_GUARD, u8::from(!enable)]
280 }
281
282 #[inline(always)]
284 pub fn encode_initialize_permanent_delegate(delegate: &Address) -> Bytes<33> {
285 let mut out = Bytes::new();
286 out.push(&[IX_INITIALIZE_PERMANENT_DELEGATE]);
287 out.push(delegate.as_array());
288 out
289 }
290
291 #[inline(always)]
295 pub fn encode_initialize_pointer(
296 family: u8,
297 authority: Option<&Address>,
298 target: Option<&Address>,
299 ) -> Result<Bytes<66>, ProgramError> {
300 non_zero(authority)?;
301 non_zero(target)?;
302 let mut out = Bytes::new();
303 out.push(&[family, 0]);
304 out.nullable(authority);
305 out.nullable(target);
306 Ok(out)
307 }
308
309 #[inline(always)]
313 pub fn encode_update_pointer(
314 family: u8,
315 target: Option<&Address>,
316 ) -> Result<Bytes<34>, ProgramError> {
317 non_zero(target)?;
318 let mut out = Bytes::new();
319 out.push(&[family, 1]);
320 out.nullable(target);
321 Ok(out)
322 }
323
324 #[inline(always)]
327 pub const fn multiplier_is_valid(multiplier: f64) -> bool {
328 let bits = multiplier.to_bits();
329 let exponent = (bits >> 52) & 0x7ff;
330 bits >> 63 == 0 && exponent != 0x7ff && bits != 0
331 }
332
333 #[inline(always)]
336 pub fn encode_initialize_scaled_ui_amount(
337 authority: Option<&Address>,
338 multiplier: f64,
339 ) -> Result<Bytes<42>, ProgramError> {
340 non_zero(authority)?;
341 if !multiplier_is_valid(multiplier) {
342 return Err(ProgramError::InvalidArgument);
343 }
344 let mut out = Bytes::new();
345 out.push(&[IX_SCALED_UI_AMOUNT, 0]);
346 out.nullable(authority);
347 out.push(&multiplier.to_le_bytes());
348 Ok(out)
349 }
350
351 #[inline(always)]
353 pub fn encode_update_scaled_ui_amount_multiplier(
354 multiplier: f64,
355 effective_timestamp: i64,
356 ) -> Result<[u8; 18], ProgramError> {
357 if !multiplier_is_valid(multiplier) {
358 return Err(ProgramError::InvalidArgument);
359 }
360 let mut data = [0u8; 18];
361 data[0] = IX_SCALED_UI_AMOUNT;
362 data[1] = 1;
363 data[2..10].copy_from_slice(&multiplier.to_le_bytes());
364 data[10..18].copy_from_slice(&effective_timestamp.to_le_bytes());
365 Ok(data)
366 }
367
368 #[inline(always)]
372 pub fn encode_initialize_authority(
373 family: u8,
374 authority: &Address,
375 ) -> Result<Bytes<34>, ProgramError> {
376 non_zero(Some(authority))?;
377 let mut out = Bytes::new();
378 out.push(&[family, 0]);
379 out.push(authority.as_array());
380 Ok(out)
381 }
382
383 #[inline(always)]
385 pub fn encode_pausable(pause: bool) -> [u8; 2] {
386 [IX_PAUSABLE, if pause { 1 } else { 2 }]
387 }
388
389 #[inline(always)]
391 pub fn encode_permissioned_burn(amount: u64) -> [u8; 10] {
392 let mut data = [0u8; 10];
393 data[0] = IX_PERMISSIONED_BURN;
394 data[1] = 1;
395 data[2..10].copy_from_slice(&amount.to_le_bytes());
396 data
397 }
398
399 #[inline(always)]
401 pub fn encode_permissioned_burn_checked(amount: u64, decimals: u8) -> [u8; 11] {
402 let mut data = [0u8; 11];
403 data[0] = IX_PERMISSIONED_BURN;
404 data[1] = 2;
405 data[2..10].copy_from_slice(&amount.to_le_bytes());
406 data[10] = decimals;
407 data
408 }
409}
410
411use encoders::*;
412
413macro_rules! t22_methods {
417 ($name:ident) => {
418 impl $name<'_> {
419 #[inline]
421 pub fn invoke(&self) -> ProgramResult {
422 self.emit(&[], &mut Invoke::token_2022(&[]))
423 }
424
425 #[inline]
427 pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
428 self.emit(&[], &mut Invoke::token_2022(signers))
429 }
430 }
431 };
432 ($name:ident, authority = $auth:ident) => {
433 impl $name<'_> {
434 #[inline]
438 pub fn invoke(&self) -> ProgramResult {
439 require_authority_signed_direct(self.$auth)?;
440 self.emit(&[], &mut Invoke::token_2022(&[]))
441 }
442
443 #[inline]
445 pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
446 self.emit(&[], &mut Invoke::token_2022(signers))
447 }
448
449 #[inline]
452 pub fn invoke_multisig(&self, multisig_signers: &[&AccountView<'_>]) -> ProgramResult {
453 require_multisig_signers_direct(multisig_signers)?;
454 self.emit(multisig_signers, &mut Invoke::token_2022(&[]))
455 }
456
457 #[inline]
460 pub fn invoke_signed_multisig(
461 &self,
462 multisig_signers: &[&AccountView<'_>],
463 signers: &[Signer<'_, '_>],
464 ) -> ProgramResult {
465 self.emit(multisig_signers, &mut Invoke::token_2022(signers))
466 }
467 }
468 };
469}
470
471macro_rules! mint_initializer {
474 ($(#[$doc:meta])* $name:ident { $($field:ident : $ty:ty),* $(,)? } data = |$s:ident| $data:expr;) => {
475 $(#[$doc])*
476 pub struct $name<'a> {
477 pub mint: &'a AccountView<'a>,
478 $(pub $field: $ty,)*
479 }
480
481 impl<'a, 'x: 'a> TokenInstruction<'a> for $name<'x> {
482 #[inline(always)]
483 fn emit(
484 &self,
485 multisig_signers: &[&'a AccountView<'a>],
486 sink: &mut impl TokenSink<'a>,
487 ) -> ProgramResult {
488 let $s = self;
489 let data = $data;
490 let accounts = [InstructionAccount::writable(self.mint.address())];
491 let views = [self.mint];
492 sink.emit(data.as_ref(), accounts, views, &[Trailing::signers(multisig_signers)])
493 }
494 }
495
496 t22_methods!($name);
497 };
498}
499
500macro_rules! authority_update {
503 ($(#[$doc:meta])* $name:ident { target = $target:ident, authority = $auth:ident $(, $field:ident : $ty:ty)* $(,)? } data = |$s:ident| $data:expr;) => {
504 $(#[$doc])*
505 pub struct $name<'a> {
506 pub $target: &'a AccountView<'a>,
507 pub $auth: &'a AccountView<'a>,
508 $(pub $field: $ty,)*
509 }
510
511 impl<'a, 'x: 'a> TokenInstruction<'a> for $name<'x> {
512 #[inline(always)]
513 fn emit(
514 &self,
515 multisig_signers: &[&'a AccountView<'a>],
516 sink: &mut impl TokenSink<'a>,
517 ) -> ProgramResult {
518 let $s = self;
519 let data = $data;
520 let accounts = [
521 InstructionAccount::writable(self.$target.address()),
522 authority_meta(self.$auth, multisig_signers),
523 ];
524 let views = [self.$target, self.$auth];
525 sink.emit(data.as_ref(), accounts, views, &[Trailing::signers(multisig_signers)])
526 }
527 }
528
529 t22_methods!($name, authority = $auth);
530 };
531}
532
533impl<const N: usize> AsRef<[u8]> for Bytes<N> {
534 #[inline(always)]
535 fn as_ref(&self) -> &[u8] {
536 self.as_slice()
537 }
538}
539
540pub struct CreateNativeMint<'a> {
547 pub payer: &'a AccountView<'a>,
548 pub native_mint: &'a AccountView<'a>,
549 pub system_program: &'a AccountView<'a>,
550}
551
552impl<'a, 'x: 'a> TokenInstruction<'a> for CreateNativeMint<'x> {
553 #[inline(always)]
554 fn emit(
555 &self,
556 multisig_signers: &[&'a AccountView<'a>],
557 sink: &mut impl TokenSink<'a>,
558 ) -> ProgramResult {
559 let data = encode_create_native_mint();
560 let accounts = [
561 InstructionAccount::writable_signer(self.payer.address()),
562 InstructionAccount::writable(self.native_mint.address()),
563 InstructionAccount::readonly(self.system_program.address()),
564 ];
565 let views = [self.payer, self.native_mint, self.system_program];
566 sink.emit(
567 &data,
568 accounts,
569 views,
570 &[Trailing::signers(multisig_signers)],
571 )
572 }
573}
574
575t22_methods!(CreateNativeMint);
576
577mint_initializer! {
578 InitializeNonTransferableMint {}
582 data = |_s| encode_initialize_non_transferable_mint();
583}
584
585pub struct Reallocate<'a> {
590 pub account: &'a AccountView<'a>,
591 pub payer: &'a AccountView<'a>,
592 pub system_program: &'a AccountView<'a>,
593 pub owner: &'a AccountView<'a>,
594 pub extension_types: &'a [u16],
595}
596
597impl<'a, 'x: 'a> TokenInstruction<'a> for Reallocate<'x> {
598 #[inline(always)]
599 fn emit(
600 &self,
601 multisig_signers: &[&'a AccountView<'a>],
602 sink: &mut impl TokenSink<'a>,
603 ) -> ProgramResult {
604 let (data, len) =
605 token_encoders::encode_extension_types(IX_REALLOCATE, self.extension_types)
606 .ok_or(ProgramError::InvalidArgument)?;
607 let accounts = [
608 InstructionAccount::writable(self.account.address()),
609 InstructionAccount::writable_signer(self.payer.address()),
610 InstructionAccount::readonly(self.system_program.address()),
611 authority_meta(self.owner, multisig_signers),
612 ];
613 let views = [self.account, self.payer, self.system_program, self.owner];
614 sink.emit(
615 &data[..len],
616 accounts,
617 views,
618 &[Trailing::signers(multisig_signers)],
619 )
620 }
621}
622
623t22_methods!(Reallocate, authority = owner);
624
625mint_initializer! {
629 InitializeMintCloseAuthority { close_authority: Option<&'a Address> }
632 data = |s| encode_initialize_mint_close_authority(s.close_authority);
633}
634
635mint_initializer! {
636 InitializeTransferFeeConfig {
640 transfer_fee_config_authority: Option<&'a Address>,
641 withdraw_withheld_authority: Option<&'a Address>,
642 transfer_fee_basis_points: u16,
643 maximum_fee: u64,
644 }
645 data = |s| encode_initialize_transfer_fee_config(
646 s.transfer_fee_config_authority,
647 s.withdraw_withheld_authority,
648 s.transfer_fee_basis_points,
649 s.maximum_fee,
650 )?;
651}
652
653mint_initializer! {
654 InitializeDefaultAccountState { state: u8 }
657 data = |s| encode_initialize_default_account_state(s.state)?;
658}
659
660mint_initializer! {
661 InitializeInterestBearingMint { rate_authority: Option<&'a Address>, rate: i16 }
664 data = |s| encode_initialize_interest_bearing_mint(s.rate_authority, s.rate)?;
665}
666
667mint_initializer! {
668 InitializePermanentDelegate { delegate: &'a Address }
671 data = |s| encode_initialize_permanent_delegate(s.delegate);
672}
673
674mint_initializer! {
675 InitializeTransferHook { authority: Option<&'a Address>, program_id: Option<&'a Address> }
678 data = |s| encode_initialize_pointer(IX_TRANSFER_HOOK, s.authority, s.program_id)?;
679}
680
681mint_initializer! {
682 InitializeMetadataPointer { authority: Option<&'a Address>, metadata_address: Option<&'a Address> }
684 data = |s| encode_initialize_pointer(IX_METADATA_POINTER, s.authority, s.metadata_address)?;
685}
686
687mint_initializer! {
688 InitializeGroupPointer { authority: Option<&'a Address>, group_address: Option<&'a Address> }
690 data = |s| encode_initialize_pointer(IX_GROUP_POINTER, s.authority, s.group_address)?;
691}
692
693mint_initializer! {
694 InitializeGroupMemberPointer { authority: Option<&'a Address>, member_address: Option<&'a Address> }
696 data = |s| encode_initialize_pointer(IX_GROUP_MEMBER_POINTER, s.authority, s.member_address)?;
697}
698
699mint_initializer! {
700 InitializeScaledUiAmount { authority: Option<&'a Address>, multiplier: f64 }
703 data = |s| encode_initialize_scaled_ui_amount(s.authority, s.multiplier)?;
704}
705
706mint_initializer! {
707 InitializePausable { authority: &'a Address }
710 data = |s| encode_initialize_authority(IX_PAUSABLE, s.authority)?;
711}
712
713mint_initializer! {
714 InitializePermissionedBurn { authority: &'a Address }
717 data = |s| encode_initialize_authority(IX_PERMISSIONED_BURN, s.authority)?;
718}
719
720pub struct TransferCheckedWithFee<'a> {
727 pub source: &'a AccountView<'a>,
728 pub mint: &'a AccountView<'a>,
729 pub destination: &'a AccountView<'a>,
730 pub authority: &'a AccountView<'a>,
731 pub amount: u64,
732 pub decimals: u8,
733 pub fee: u64,
734}
735
736impl<'a, 'x: 'a> TokenInstruction<'a> for TransferCheckedWithFee<'x> {
737 #[inline(always)]
738 fn emit(
739 &self,
740 multisig_signers: &[&'a AccountView<'a>],
741 sink: &mut impl TokenSink<'a>,
742 ) -> ProgramResult {
743 let data = encode_transfer_checked_with_fee(self.amount, self.decimals, self.fee);
744 let accounts = [
745 InstructionAccount::writable(self.source.address()),
746 InstructionAccount::readonly(self.mint.address()),
747 InstructionAccount::writable(self.destination.address()),
748 authority_meta(self.authority, multisig_signers),
749 ];
750 let views = [self.source, self.mint, self.destination, self.authority];
751 sink.emit(
752 &data,
753 accounts,
754 views,
755 &[Trailing::signers(multisig_signers)],
756 )
757 }
758}
759
760t22_methods!(TransferCheckedWithFee, authority = authority);
761
762pub struct WithdrawWithheldTokensFromMint<'a> {
765 pub mint: &'a AccountView<'a>,
766 pub destination: &'a AccountView<'a>,
767 pub withdraw_withheld_authority: &'a AccountView<'a>,
768}
769
770impl<'a, 'x: 'a> TokenInstruction<'a> for WithdrawWithheldTokensFromMint<'x> {
771 #[inline(always)]
772 fn emit(
773 &self,
774 multisig_signers: &[&'a AccountView<'a>],
775 sink: &mut impl TokenSink<'a>,
776 ) -> ProgramResult {
777 let data = encode_withdraw_withheld_tokens_from_mint();
778 let accounts = [
779 InstructionAccount::writable(self.mint.address()),
780 InstructionAccount::writable(self.destination.address()),
781 authority_meta(self.withdraw_withheld_authority, multisig_signers),
782 ];
783 let views = [
784 self.mint,
785 self.destination,
786 self.withdraw_withheld_authority,
787 ];
788 sink.emit(
789 &data,
790 accounts,
791 views,
792 &[Trailing::signers(multisig_signers)],
793 )
794 }
795}
796
797t22_methods!(
798 WithdrawWithheldTokensFromMint,
799 authority = withdraw_withheld_authority
800);
801
802pub struct WithdrawWithheldTokensFromAccounts<'a> {
806 pub mint: &'a AccountView<'a>,
807 pub destination: &'a AccountView<'a>,
808 pub withdraw_withheld_authority: &'a AccountView<'a>,
809 pub sources: &'a [&'a AccountView<'a>],
810}
811
812impl<'a, 'x: 'a> TokenInstruction<'a> for WithdrawWithheldTokensFromAccounts<'x> {
813 #[inline(always)]
814 fn emit(
815 &self,
816 multisig_signers: &[&'a AccountView<'a>],
817 sink: &mut impl TokenSink<'a>,
818 ) -> ProgramResult {
819 let count = u8::try_from(self.sources.len()).map_err(|_| ProgramError::InvalidArgument)?;
820 let data = encode_withdraw_withheld_tokens_from_accounts(count);
821 let accounts = [
822 InstructionAccount::readonly(self.mint.address()),
823 InstructionAccount::writable(self.destination.address()),
824 authority_meta(self.withdraw_withheld_authority, multisig_signers),
825 ];
826 let views = [
827 self.mint,
828 self.destination,
829 self.withdraw_withheld_authority,
830 ];
831 sink.emit(
832 &data,
833 accounts,
834 views,
835 &[
836 Trailing::signers(multisig_signers),
837 Trailing::writable(self.sources),
838 ],
839 )
840 }
841}
842
843t22_methods!(
844 WithdrawWithheldTokensFromAccounts,
845 authority = withdraw_withheld_authority
846);
847
848pub struct HarvestWithheldTokensToMint<'a> {
851 pub mint: &'a AccountView<'a>,
852 pub sources: &'a [&'a AccountView<'a>],
853}
854
855impl<'a, 'x: 'a> TokenInstruction<'a> for HarvestWithheldTokensToMint<'x> {
856 #[inline(always)]
857 fn emit(
858 &self,
859 _multisig_signers: &[&'a AccountView<'a>],
860 sink: &mut impl TokenSink<'a>,
861 ) -> ProgramResult {
862 let data = encode_harvest_withheld_tokens_to_mint();
863 let accounts = [InstructionAccount::writable(self.mint.address())];
864 let views = [self.mint];
865 sink.emit(&data, accounts, views, &[Trailing::writable(self.sources)])
866 }
867}
868
869t22_methods!(HarvestWithheldTokensToMint);
870
871authority_update! {
872 SetTransferFee { target = mint, authority = transfer_fee_config_authority, transfer_fee_basis_points: u16, maximum_fee: u64 }
875 data = |s| encode_set_transfer_fee(s.transfer_fee_basis_points, s.maximum_fee)?;
876}
877
878authority_update! {
882 UpdateDefaultAccountState { target = mint, authority = freeze_authority, state: u8 }
885 data = |s| encode_update_default_account_state(s.state)?;
886}
887
888authority_update! {
889 EnableMemoTransfer { target = account, authority = owner }
892 data = |_s| encode_memo_transfer(true);
893}
894
895authority_update! {
896 DisableMemoTransfer { target = account, authority = owner }
898 data = |_s| encode_memo_transfer(false);
899}
900
901authority_update! {
902 UpdateInterestRate { target = mint, authority = rate_authority, rate: i16 }
904 data = |s| encode_update_interest_rate(s.rate);
905}
906
907authority_update! {
908 EnableCpiGuard { target = account, authority = owner }
912 data = |_s| encode_cpi_guard(true);
913}
914
915authority_update! {
916 DisableCpiGuard { target = account, authority = owner }
918 data = |_s| encode_cpi_guard(false);
919}
920
921authority_update! {
922 UpdateTransferHook { target = mint, authority = authority, program_id: Option<&'a Address> }
925 data = |s| encode_update_pointer(IX_TRANSFER_HOOK, s.program_id)?;
926}
927
928authority_update! {
929 UpdateMetadataPointer { target = mint, authority = authority, metadata_address: Option<&'a Address> }
931 data = |s| encode_update_pointer(IX_METADATA_POINTER, s.metadata_address)?;
932}
933
934authority_update! {
935 UpdateGroupPointer { target = mint, authority = authority, group_address: Option<&'a Address> }
937 data = |s| encode_update_pointer(IX_GROUP_POINTER, s.group_address)?;
938}
939
940authority_update! {
941 UpdateGroupMemberPointer { target = mint, authority = authority, member_address: Option<&'a Address> }
943 data = |s| encode_update_pointer(IX_GROUP_MEMBER_POINTER, s.member_address)?;
944}
945
946authority_update! {
947 UpdateScaledUiAmountMultiplier { target = mint, authority = authority, multiplier: f64, effective_timestamp: i64 }
950 data = |s| encode_update_scaled_ui_amount_multiplier(s.multiplier, s.effective_timestamp)?;
951}
952
953authority_update! {
954 Pause { target = mint, authority = authority }
957 data = |_s| encode_pausable(true);
958}
959
960authority_update! {
961 Resume { target = mint, authority = authority }
963 data = |_s| encode_pausable(false);
964}
965
966pub struct PermissionedBurn<'a> {
973 pub account: &'a AccountView<'a>,
974 pub mint: &'a AccountView<'a>,
975 pub permissioned_burn_authority: &'a AccountView<'a>,
976 pub authority: &'a AccountView<'a>,
977 pub amount: u64,
978}
979
980impl<'a, 'x: 'a> TokenInstruction<'a> for PermissionedBurn<'x> {
981 #[inline(always)]
982 fn emit(
983 &self,
984 multisig_signers: &[&'a AccountView<'a>],
985 sink: &mut impl TokenSink<'a>,
986 ) -> ProgramResult {
987 let data = encode_permissioned_burn(self.amount);
988 let accounts = [
989 InstructionAccount::writable(self.account.address()),
990 InstructionAccount::writable(self.mint.address()),
991 InstructionAccount::readonly_signer(self.permissioned_burn_authority.address()),
992 authority_meta(self.authority, multisig_signers),
993 ];
994 let views = [
995 self.account,
996 self.mint,
997 self.permissioned_burn_authority,
998 self.authority,
999 ];
1000 sink.emit(
1001 &data,
1002 accounts,
1003 views,
1004 &[Trailing::signers(multisig_signers)],
1005 )
1006 }
1007}
1008
1009t22_methods!(PermissionedBurn, authority = authority);
1010
1011pub struct PermissionedBurnChecked<'a> {
1014 pub account: &'a AccountView<'a>,
1015 pub mint: &'a AccountView<'a>,
1016 pub permissioned_burn_authority: &'a AccountView<'a>,
1017 pub authority: &'a AccountView<'a>,
1018 pub amount: u64,
1019 pub decimals: u8,
1020}
1021
1022impl<'a, 'x: 'a> TokenInstruction<'a> for PermissionedBurnChecked<'x> {
1023 #[inline(always)]
1024 fn emit(
1025 &self,
1026 multisig_signers: &[&'a AccountView<'a>],
1027 sink: &mut impl TokenSink<'a>,
1028 ) -> ProgramResult {
1029 let data = encode_permissioned_burn_checked(self.amount, self.decimals);
1030 let accounts = [
1031 InstructionAccount::writable(self.account.address()),
1032 InstructionAccount::writable(self.mint.address()),
1033 InstructionAccount::readonly_signer(self.permissioned_burn_authority.address()),
1034 authority_meta(self.authority, multisig_signers),
1035 ];
1036 let views = [
1037 self.account,
1038 self.mint,
1039 self.permissioned_burn_authority,
1040 self.authority,
1041 ];
1042 sink.emit(
1043 &data,
1044 accounts,
1045 views,
1046 &[Trailing::signers(multisig_signers)],
1047 )
1048 }
1049}
1050
1051t22_methods!(PermissionedBurnChecked, authority = authority);
1052
1053#[cfg(test)]
1054mod tests {
1055 use super::encoders::*;
1056 use crate::address::Address;
1057
1058 fn addr(byte: u8) -> Address {
1059 Address::new_from_array([byte; 32])
1060 }
1061
1062 #[test]
1063 fn fixed_encoders_match_the_token_2022_wire_format() {
1064 assert_eq!(encode_create_native_mint(), [31]);
1065 assert_eq!(encode_initialize_non_transferable_mint(), [32]);
1066 assert_eq!(encode_withdraw_withheld_tokens_from_mint(), [26, 2]);
1067 assert_eq!(encode_withdraw_withheld_tokens_from_accounts(3), [26, 3, 3]);
1068 assert_eq!(encode_harvest_withheld_tokens_to_mint(), [26, 4]);
1069 assert_eq!(encode_memo_transfer(true), [30, 0]);
1070 assert_eq!(encode_memo_transfer(false), [30, 1]);
1071 assert_eq!(encode_cpi_guard(true), [34, 0]);
1072 assert_eq!(encode_cpi_guard(false), [34, 1]);
1073 assert_eq!(encode_pausable(true), [44, 1]);
1074 assert_eq!(encode_pausable(false), [44, 2]);
1075 assert_eq!(encode_update_interest_rate(-2), [33, 1, 0xfe, 0xff]);
1076 assert_eq!(
1077 encode_transfer_checked_with_fee(1, 9, 2),
1078 [26, 1, 1, 0, 0, 0, 0, 0, 0, 0, 9, 2, 0, 0, 0, 0, 0, 0, 0]
1079 );
1080 assert_eq!(
1081 encode_set_transfer_fee(100, 5).unwrap(),
1082 [26, 5, 100, 0, 5, 0, 0, 0, 0, 0, 0, 0]
1083 );
1084 assert!(encode_set_transfer_fee(10_001, 5).is_err());
1085 assert_eq!(
1086 encode_initialize_default_account_state(2).unwrap(),
1087 [28, 0, 2]
1088 );
1089 assert_eq!(encode_update_default_account_state(1).unwrap(), [28, 1, 1]);
1090 assert!(encode_initialize_default_account_state(0).is_err());
1091 assert!(encode_update_default_account_state(3).is_err());
1092 assert_eq!(encode_permissioned_burn(1), [46, 1, 1, 0, 0, 0, 0, 0, 0, 0]);
1093 assert_eq!(
1094 encode_permissioned_burn_checked(1, 6),
1095 [46, 2, 1, 0, 0, 0, 0, 0, 0, 0, 6]
1096 );
1097 }
1098
1099 #[test]
1100 fn optional_address_encoders_distinguish_coption_from_nullable() {
1101 let a = addr(1);
1102 let b = addr(2);
1103
1104 let some = encode_initialize_mint_close_authority(Some(&a));
1105 assert_eq!(some.as_slice().len(), 34);
1106 assert_eq!(&some.as_slice()[..2], &[25, 1]);
1107 assert_eq!(&some.as_slice()[2..], a.as_bytes());
1108 let none = encode_initialize_mint_close_authority(None);
1109 assert_eq!(none.as_slice(), &[25, 0]);
1110
1111 let fee = encode_initialize_transfer_fee_config(Some(&a), None, 250, 7).unwrap();
1112 let bytes = fee.as_slice();
1113 assert_eq!(bytes.len(), 2 + 33 + 1 + 2 + 8);
1114 assert_eq!(&bytes[..3], &[26, 0, 1]);
1115 assert_eq!(&bytes[3..35], a.as_bytes());
1116 assert_eq!(bytes[35], 0);
1117 assert_eq!(&bytes[36..38], &250u16.to_le_bytes());
1118 assert_eq!(&bytes[38..46], &7u64.to_le_bytes());
1119 assert!(encode_initialize_transfer_fee_config(None, None, 10_001, 0).is_err());
1120
1121 let hook = encode_initialize_pointer(36, Some(&a), Some(&b)).unwrap();
1122 assert_eq!(hook.as_slice().len(), 66);
1123 assert_eq!(&hook.as_slice()[..2], &[36, 0]);
1124 assert_eq!(&hook.as_slice()[2..34], a.as_bytes());
1125 assert_eq!(&hook.as_slice()[34..66], b.as_bytes());
1126 let bare = encode_initialize_pointer(39, None, None).unwrap();
1127 assert_eq!(&bare.as_slice()[2..], &[0u8; 64]);
1128 assert!(encode_initialize_pointer(40, Some(&Address::default()), None).is_err());
1129
1130 let update = encode_update_pointer(41, None).unwrap();
1131 assert_eq!(update.as_slice().len(), 34);
1132 assert_eq!(&update.as_slice()[..2], &[41, 1]);
1133
1134 let interest = encode_initialize_interest_bearing_mint(None, 300).unwrap();
1135 assert_eq!(interest.as_slice().len(), 36);
1136 assert_eq!(&interest.as_slice()[34..], &300i16.to_le_bytes());
1137
1138 let delegate = encode_initialize_permanent_delegate(&a);
1139 assert_eq!(delegate.as_slice()[0], 35);
1140 assert_eq!(&delegate.as_slice()[1..], a.as_bytes());
1141
1142 let pausable = encode_initialize_authority(44, &a).unwrap();
1143 assert_eq!(&pausable.as_slice()[..2], &[44, 0]);
1144 assert_eq!(&pausable.as_slice()[2..], a.as_bytes());
1145 assert!(encode_initialize_authority(46, &Address::default()).is_err());
1146 }
1147
1148 #[test]
1149 fn scaled_ui_amount_multiplier_is_validated_on_bits() {
1150 assert!(multiplier_is_valid(1.0));
1151 assert!(multiplier_is_valid(0.5));
1152 assert!(multiplier_is_valid(f64::MIN_POSITIVE));
1153 assert!(!multiplier_is_valid(0.0));
1154 assert!(!multiplier_is_valid(-0.0));
1155 assert!(!multiplier_is_valid(-1.0));
1156 assert!(!multiplier_is_valid(f64::INFINITY));
1157 assert!(!multiplier_is_valid(f64::NAN));
1158
1159 let init = encode_initialize_scaled_ui_amount(None, 2.0).unwrap();
1160 assert_eq!(init.as_slice().len(), 42);
1161 assert_eq!(&init.as_slice()[..2], &[43, 0]);
1162 assert_eq!(&init.as_slice()[34..], &2.0f64.to_le_bytes());
1163 assert!(encode_initialize_scaled_ui_amount(None, 0.0).is_err());
1164
1165 let update = encode_update_scaled_ui_amount_multiplier(3.0, 17).unwrap();
1166 assert_eq!(&update[..2], &[43, 1]);
1167 assert_eq!(&update[2..10], &3.0f64.to_le_bytes());
1168 assert_eq!(&update[10..18], &17i64.to_le_bytes());
1169 }
1170}