solana_transaction_status/
parse_token.rs

1use {
2    crate::parse_instruction::{
3        check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum,
4    },
5    extension::{
6        confidential_mint_burn::*, confidential_transfer::*, confidential_transfer_fee::*,
7        cpi_guard::*, default_account_state::*, group_member_pointer::*, group_pointer::*,
8        interest_bearing_mint::*, memo_transfer::*, metadata_pointer::*, mint_close_authority::*,
9        pausable::*, permanent_delegate::*, reallocate::*, scaled_ui_amount::*, token_group::*,
10        token_metadata::*, transfer_fee::*, transfer_hook::*,
11    },
12    serde::{Deserialize, Serialize},
13    serde_json::{json, Map, Value},
14    solana_account_decoder::{
15        parse_account_data::SplTokenAdditionalDataV2, parse_token::token_amount_to_ui_amount_v3,
16    },
17    solana_message::{compiled_instruction::CompiledInstruction, AccountKeys},
18    solana_program_option::COption,
19    solana_pubkey::Pubkey,
20    spl_token_2022_interface::{
21        extension::ExtensionType,
22        instruction::{AuthorityType, TokenInstruction},
23    },
24    spl_token_group_interface::instruction::TokenGroupInstruction,
25    spl_token_metadata_interface::instruction::TokenMetadataInstruction,
26};
27
28mod extension;
29
30pub fn parse_token(
31    instruction: &CompiledInstruction,
32    account_keys: &AccountKeys,
33) -> Result<ParsedInstructionEnum, ParseInstructionError> {
34    match instruction.accounts.iter().max() {
35        Some(index) if (*index as usize) < account_keys.len() => {}
36        _ => {
37            // Runtime should prevent this from ever happening
38            return Err(ParseInstructionError::InstructionKeyMismatch(
39                ParsableProgram::SplToken,
40            ));
41        }
42    }
43    if let Ok(token_instruction) = TokenInstruction::unpack(&instruction.data) {
44        match token_instruction {
45            TokenInstruction::InitializeMint {
46                decimals,
47                mint_authority,
48                freeze_authority,
49            } => {
50                check_num_token_accounts(&instruction.accounts, 2)?;
51                let mut value = json!({
52                    "mint": account_keys[instruction.accounts[0] as usize].to_string(),
53                    "decimals": decimals,
54                    "mintAuthority": mint_authority.to_string(),
55                    "rentSysvar": account_keys[instruction.accounts[1] as usize].to_string(),
56                });
57                let map = value.as_object_mut().unwrap();
58                if let COption::Some(freeze_authority) = freeze_authority {
59                    map.insert(
60                        "freezeAuthority".to_string(),
61                        json!(freeze_authority.to_string()),
62                    );
63                }
64                Ok(ParsedInstructionEnum {
65                    instruction_type: "initializeMint".to_string(),
66                    info: value,
67                })
68            }
69            TokenInstruction::InitializeMint2 {
70                decimals,
71                mint_authority,
72                freeze_authority,
73            } => {
74                check_num_token_accounts(&instruction.accounts, 1)?;
75                let mut value = json!({
76                    "mint": account_keys[instruction.accounts[0] as usize].to_string(),
77                    "decimals": decimals,
78                    "mintAuthority": mint_authority.to_string(),
79                });
80                let map = value.as_object_mut().unwrap();
81                if let COption::Some(freeze_authority) = freeze_authority {
82                    map.insert(
83                        "freezeAuthority".to_string(),
84                        json!(freeze_authority.to_string()),
85                    );
86                }
87                Ok(ParsedInstructionEnum {
88                    instruction_type: "initializeMint2".to_string(),
89                    info: value,
90                })
91            }
92            TokenInstruction::InitializeAccount => {
93                check_num_token_accounts(&instruction.accounts, 4)?;
94                Ok(ParsedInstructionEnum {
95                    instruction_type: "initializeAccount".to_string(),
96                    info: json!({
97                        "account": account_keys[instruction.accounts[0] as usize].to_string(),
98                        "mint": account_keys[instruction.accounts[1] as usize].to_string(),
99                        "owner": account_keys[instruction.accounts[2] as usize].to_string(),
100                        "rentSysvar": account_keys[instruction.accounts[3] as usize].to_string(),
101                    }),
102                })
103            }
104            TokenInstruction::InitializeAccount2 { owner } => {
105                check_num_token_accounts(&instruction.accounts, 3)?;
106                Ok(ParsedInstructionEnum {
107                    instruction_type: "initializeAccount2".to_string(),
108                    info: json!({
109                        "account": account_keys[instruction.accounts[0] as usize].to_string(),
110                        "mint": account_keys[instruction.accounts[1] as usize].to_string(),
111                        "owner": owner.to_string(),
112                        "rentSysvar": account_keys[instruction.accounts[2] as usize].to_string(),
113                    }),
114                })
115            }
116            TokenInstruction::InitializeAccount3 { owner } => {
117                check_num_token_accounts(&instruction.accounts, 2)?;
118                Ok(ParsedInstructionEnum {
119                    instruction_type: "initializeAccount3".to_string(),
120                    info: json!({
121                        "account": account_keys[instruction.accounts[0] as usize].to_string(),
122                        "mint": account_keys[instruction.accounts[1] as usize].to_string(),
123                        "owner": owner.to_string(),
124                    }),
125                })
126            }
127            TokenInstruction::InitializeMultisig { m } => {
128                check_num_token_accounts(&instruction.accounts, 3)?;
129                let mut signers: Vec<String> = vec![];
130                for i in instruction.accounts[2..].iter() {
131                    signers.push(account_keys[*i as usize].to_string());
132                }
133                Ok(ParsedInstructionEnum {
134                    instruction_type: "initializeMultisig".to_string(),
135                    info: json!({
136                        "multisig": account_keys[instruction.accounts[0] as usize].to_string(),
137                        "rentSysvar": account_keys[instruction.accounts[1] as usize].to_string(),
138                        "signers": signers,
139                        "m": m,
140                    }),
141                })
142            }
143            TokenInstruction::InitializeMultisig2 { m } => {
144                check_num_token_accounts(&instruction.accounts, 2)?;
145                let mut signers: Vec<String> = vec![];
146                for i in instruction.accounts[1..].iter() {
147                    signers.push(account_keys[*i as usize].to_string());
148                }
149                Ok(ParsedInstructionEnum {
150                    instruction_type: "initializeMultisig2".to_string(),
151                    info: json!({
152                        "multisig": account_keys[instruction.accounts[0] as usize].to_string(),
153                        "signers": signers,
154                        "m": m,
155                    }),
156                })
157            }
158            #[allow(deprecated)]
159            TokenInstruction::Transfer { amount } => {
160                check_num_token_accounts(&instruction.accounts, 3)?;
161                let mut value = json!({
162                    "source": account_keys[instruction.accounts[0] as usize].to_string(),
163                    "destination": account_keys[instruction.accounts[1] as usize].to_string(),
164                    "amount": amount.to_string(),
165                });
166                let map = value.as_object_mut().unwrap();
167                parse_signers(
168                    map,
169                    2,
170                    account_keys,
171                    &instruction.accounts,
172                    "authority",
173                    "multisigAuthority",
174                );
175                Ok(ParsedInstructionEnum {
176                    instruction_type: "transfer".to_string(),
177                    info: value,
178                })
179            }
180            TokenInstruction::Approve { amount } => {
181                check_num_token_accounts(&instruction.accounts, 3)?;
182                let mut value = json!({
183                    "source": account_keys[instruction.accounts[0] as usize].to_string(),
184                    "delegate": account_keys[instruction.accounts[1] as usize].to_string(),
185                    "amount": amount.to_string(),
186                });
187                let map = value.as_object_mut().unwrap();
188                parse_signers(
189                    map,
190                    2,
191                    account_keys,
192                    &instruction.accounts,
193                    "owner",
194                    "multisigOwner",
195                );
196                Ok(ParsedInstructionEnum {
197                    instruction_type: "approve".to_string(),
198                    info: value,
199                })
200            }
201            TokenInstruction::Revoke => {
202                check_num_token_accounts(&instruction.accounts, 2)?;
203                let mut value = json!({
204                    "source": account_keys[instruction.accounts[0] as usize].to_string(),
205                });
206                let map = value.as_object_mut().unwrap();
207                parse_signers(
208                    map,
209                    1,
210                    account_keys,
211                    &instruction.accounts,
212                    "owner",
213                    "multisigOwner",
214                );
215                Ok(ParsedInstructionEnum {
216                    instruction_type: "revoke".to_string(),
217                    info: value,
218                })
219            }
220            TokenInstruction::SetAuthority {
221                authority_type,
222                new_authority,
223            } => {
224                check_num_token_accounts(&instruction.accounts, 2)?;
225                let owned = match authority_type {
226                    AuthorityType::MintTokens
227                    | AuthorityType::FreezeAccount
228                    | AuthorityType::TransferFeeConfig
229                    | AuthorityType::WithheldWithdraw
230                    | AuthorityType::CloseMint
231                    | AuthorityType::InterestRate
232                    | AuthorityType::PermanentDelegate
233                    | AuthorityType::ConfidentialTransferMint
234                    | AuthorityType::TransferHookProgramId
235                    | AuthorityType::ConfidentialTransferFeeConfig
236                    | AuthorityType::MetadataPointer
237                    | AuthorityType::GroupPointer
238                    | AuthorityType::GroupMemberPointer
239                    | AuthorityType::ScaledUiAmount
240                    | AuthorityType::Pause => "mint",
241                    AuthorityType::AccountOwner | AuthorityType::CloseAccount => "account",
242                };
243                let mut value = json!({
244                    owned: account_keys[instruction.accounts[0] as usize].to_string(),
245                    "authorityType": Into::<UiAuthorityType>::into(authority_type),
246                    "newAuthority": map_coption_pubkey(new_authority),
247                });
248                let map = value.as_object_mut().unwrap();
249                parse_signers(
250                    map,
251                    1,
252                    account_keys,
253                    &instruction.accounts,
254                    "authority",
255                    "multisigAuthority",
256                );
257                Ok(ParsedInstructionEnum {
258                    instruction_type: "setAuthority".to_string(),
259                    info: value,
260                })
261            }
262            TokenInstruction::MintTo { amount } => {
263                check_num_token_accounts(&instruction.accounts, 3)?;
264                let mut value = json!({
265                    "mint": account_keys[instruction.accounts[0] as usize].to_string(),
266                    "account": account_keys[instruction.accounts[1] as usize].to_string(),
267                    "amount": amount.to_string(),
268                });
269                let map = value.as_object_mut().unwrap();
270                parse_signers(
271                    map,
272                    2,
273                    account_keys,
274                    &instruction.accounts,
275                    "mintAuthority",
276                    "multisigMintAuthority",
277                );
278                Ok(ParsedInstructionEnum {
279                    instruction_type: "mintTo".to_string(),
280                    info: value,
281                })
282            }
283            TokenInstruction::Burn { amount } => {
284                check_num_token_accounts(&instruction.accounts, 3)?;
285                let mut value = json!({
286                    "account": account_keys[instruction.accounts[0] as usize].to_string(),
287                    "mint": account_keys[instruction.accounts[1] as usize].to_string(),
288                    "amount": amount.to_string(),
289                });
290                let map = value.as_object_mut().unwrap();
291                parse_signers(
292                    map,
293                    2,
294                    account_keys,
295                    &instruction.accounts,
296                    "authority",
297                    "multisigAuthority",
298                );
299                Ok(ParsedInstructionEnum {
300                    instruction_type: "burn".to_string(),
301                    info: value,
302                })
303            }
304            TokenInstruction::CloseAccount => {
305                check_num_token_accounts(&instruction.accounts, 3)?;
306                let mut value = json!({
307                    "account": account_keys[instruction.accounts[0] as usize].to_string(),
308                    "destination": account_keys[instruction.accounts[1] as usize].to_string(),
309                });
310                let map = value.as_object_mut().unwrap();
311                parse_signers(
312                    map,
313                    2,
314                    account_keys,
315                    &instruction.accounts,
316                    "owner",
317                    "multisigOwner",
318                );
319                Ok(ParsedInstructionEnum {
320                    instruction_type: "closeAccount".to_string(),
321                    info: value,
322                })
323            }
324            TokenInstruction::FreezeAccount => {
325                check_num_token_accounts(&instruction.accounts, 3)?;
326                let mut value = json!({
327                    "account": account_keys[instruction.accounts[0] as usize].to_string(),
328                    "mint": account_keys[instruction.accounts[1] as usize].to_string(),
329                });
330                let map = value.as_object_mut().unwrap();
331                parse_signers(
332                    map,
333                    2,
334                    account_keys,
335                    &instruction.accounts,
336                    "freezeAuthority",
337                    "multisigFreezeAuthority",
338                );
339                Ok(ParsedInstructionEnum {
340                    instruction_type: "freezeAccount".to_string(),
341                    info: value,
342                })
343            }
344            TokenInstruction::ThawAccount => {
345                check_num_token_accounts(&instruction.accounts, 3)?;
346                let mut value = json!({
347                    "account": account_keys[instruction.accounts[0] as usize].to_string(),
348                    "mint": account_keys[instruction.accounts[1] as usize].to_string(),
349                });
350                let map = value.as_object_mut().unwrap();
351                parse_signers(
352                    map,
353                    2,
354                    account_keys,
355                    &instruction.accounts,
356                    "freezeAuthority",
357                    "multisigFreezeAuthority",
358                );
359                Ok(ParsedInstructionEnum {
360                    instruction_type: "thawAccount".to_string(),
361                    info: value,
362                })
363            }
364            TokenInstruction::TransferChecked { amount, decimals } => {
365                check_num_token_accounts(&instruction.accounts, 4)?;
366                let additional_data = SplTokenAdditionalDataV2::with_decimals(decimals);
367                let mut value = json!({
368                    "source": account_keys[instruction.accounts[0] as usize].to_string(),
369                    "mint": account_keys[instruction.accounts[1] as usize].to_string(),
370                    "destination": account_keys[instruction.accounts[2] as usize].to_string(),
371                    "tokenAmount": token_amount_to_ui_amount_v3(amount, &additional_data),
372                });
373                let map = value.as_object_mut().unwrap();
374                parse_signers(
375                    map,
376                    3,
377                    account_keys,
378                    &instruction.accounts,
379                    "authority",
380                    "multisigAuthority",
381                );
382                Ok(ParsedInstructionEnum {
383                    instruction_type: "transferChecked".to_string(),
384                    info: value,
385                })
386            }
387            TokenInstruction::ApproveChecked { amount, decimals } => {
388                check_num_token_accounts(&instruction.accounts, 4)?;
389                let additional_data = SplTokenAdditionalDataV2::with_decimals(decimals);
390                let mut value = json!({
391                    "source": account_keys[instruction.accounts[0] as usize].to_string(),
392                    "mint": account_keys[instruction.accounts[1] as usize].to_string(),
393                    "delegate": account_keys[instruction.accounts[2] as usize].to_string(),
394                    "tokenAmount": token_amount_to_ui_amount_v3(amount, &additional_data),
395                });
396                let map = value.as_object_mut().unwrap();
397                parse_signers(
398                    map,
399                    3,
400                    account_keys,
401                    &instruction.accounts,
402                    "owner",
403                    "multisigOwner",
404                );
405                Ok(ParsedInstructionEnum {
406                    instruction_type: "approveChecked".to_string(),
407                    info: value,
408                })
409            }
410            TokenInstruction::MintToChecked { amount, decimals } => {
411                check_num_token_accounts(&instruction.accounts, 3)?;
412                let additional_data = SplTokenAdditionalDataV2::with_decimals(decimals);
413                let mut value = json!({
414                    "mint": account_keys[instruction.accounts[0] as usize].to_string(),
415                    "account": account_keys[instruction.accounts[1] as usize].to_string(),
416                    "tokenAmount": token_amount_to_ui_amount_v3(amount, &additional_data),
417                });
418                let map = value.as_object_mut().unwrap();
419                parse_signers(
420                    map,
421                    2,
422                    account_keys,
423                    &instruction.accounts,
424                    "mintAuthority",
425                    "multisigMintAuthority",
426                );
427                Ok(ParsedInstructionEnum {
428                    instruction_type: "mintToChecked".to_string(),
429                    info: value,
430                })
431            }
432            TokenInstruction::BurnChecked { amount, decimals } => {
433                check_num_token_accounts(&instruction.accounts, 3)?;
434                let additional_data = SplTokenAdditionalDataV2::with_decimals(decimals);
435                let mut value = json!({
436                    "account": account_keys[instruction.accounts[0] as usize].to_string(),
437                    "mint": account_keys[instruction.accounts[1] as usize].to_string(),
438                    "tokenAmount": token_amount_to_ui_amount_v3(amount, &additional_data),
439                });
440                let map = value.as_object_mut().unwrap();
441                parse_signers(
442                    map,
443                    2,
444                    account_keys,
445                    &instruction.accounts,
446                    "authority",
447                    "multisigAuthority",
448                );
449                Ok(ParsedInstructionEnum {
450                    instruction_type: "burnChecked".to_string(),
451                    info: value,
452                })
453            }
454            TokenInstruction::SyncNative => {
455                check_num_token_accounts(&instruction.accounts, 1)?;
456                Ok(ParsedInstructionEnum {
457                    instruction_type: "syncNative".to_string(),
458                    info: json!({
459                        "account": account_keys[instruction.accounts[0] as usize].to_string(),
460                    }),
461                })
462            }
463            TokenInstruction::GetAccountDataSize { extension_types } => {
464                check_num_token_accounts(&instruction.accounts, 1)?;
465                let mut value = json!({
466                    "mint": account_keys[instruction.accounts[0] as usize].to_string(),
467                });
468                let map = value.as_object_mut().unwrap();
469                if !extension_types.is_empty() {
470                    map.insert(
471                        "extensionTypes".to_string(),
472                        json!(extension_types
473                            .into_iter()
474                            .map(UiExtensionType::from)
475                            .collect::<Vec<_>>()),
476                    );
477                }
478                Ok(ParsedInstructionEnum {
479                    instruction_type: "getAccountDataSize".to_string(),
480                    info: value,
481                })
482            }
483            TokenInstruction::InitializeImmutableOwner => {
484                check_num_token_accounts(&instruction.accounts, 1)?;
485                Ok(ParsedInstructionEnum {
486                    instruction_type: "initializeImmutableOwner".to_string(),
487                    info: json!({
488                        "account": account_keys[instruction.accounts[0] as usize].to_string(),
489                    }),
490                })
491            }
492            TokenInstruction::AmountToUiAmount { amount } => {
493                check_num_token_accounts(&instruction.accounts, 1)?;
494                Ok(ParsedInstructionEnum {
495                    instruction_type: "amountToUiAmount".to_string(),
496                    info: json!({
497                        "mint": account_keys[instruction.accounts[0] as usize].to_string(),
498                        "amount": amount.to_string(),
499                    }),
500                })
501            }
502            TokenInstruction::UiAmountToAmount { ui_amount } => {
503                check_num_token_accounts(&instruction.accounts, 1)?;
504                Ok(ParsedInstructionEnum {
505                    instruction_type: "uiAmountToAmount".to_string(),
506                    info: json!({
507                        "mint": account_keys[instruction.accounts[0] as usize].to_string(),
508                        "uiAmount": ui_amount,
509                    }),
510                })
511            }
512            TokenInstruction::InitializeMintCloseAuthority { close_authority } => {
513                parse_initialize_mint_close_authority_instruction(
514                    close_authority,
515                    &instruction.accounts,
516                    account_keys,
517                )
518            }
519            TokenInstruction::TransferFeeExtension => parse_transfer_fee_instruction(
520                &instruction.data[1..],
521                &instruction.accounts,
522                account_keys,
523            ),
524            TokenInstruction::ConfidentialTransferExtension => {
525                parse_confidential_transfer_instruction(
526                    &instruction.data[1..],
527                    &instruction.accounts,
528                    account_keys,
529                )
530            }
531            TokenInstruction::DefaultAccountStateExtension => {
532                if instruction.data.len() <= 2 {
533                    return Err(ParseInstructionError::InstructionNotParsable(
534                        ParsableProgram::SplToken,
535                    ));
536                }
537                parse_default_account_state_instruction(
538                    &instruction.data[1..],
539                    &instruction.accounts,
540                    account_keys,
541                )
542            }
543            TokenInstruction::Reallocate { extension_types } => {
544                parse_reallocate_instruction(extension_types, &instruction.accounts, account_keys)
545            }
546            TokenInstruction::MemoTransferExtension => {
547                if instruction.data.len() < 2 {
548                    return Err(ParseInstructionError::InstructionNotParsable(
549                        ParsableProgram::SplToken,
550                    ));
551                }
552                parse_memo_transfer_instruction(
553                    &instruction.data[1..],
554                    &instruction.accounts,
555                    account_keys,
556                )
557            }
558            TokenInstruction::CreateNativeMint => {
559                check_num_token_accounts(&instruction.accounts, 3)?;
560                Ok(ParsedInstructionEnum {
561                    instruction_type: "createNativeMint".to_string(),
562                    info: json!({
563                        "payer": account_keys[instruction.accounts[0] as usize].to_string(),
564                        "nativeMint": account_keys[instruction.accounts[1] as usize].to_string(),
565                        "systemProgram": account_keys[instruction.accounts[2] as usize].to_string(),
566                    }),
567                })
568            }
569            TokenInstruction::InitializeNonTransferableMint => {
570                check_num_token_accounts(&instruction.accounts, 1)?;
571                Ok(ParsedInstructionEnum {
572                    instruction_type: "initializeNonTransferableMint".to_string(),
573                    info: json!({
574                        "mint": account_keys[instruction.accounts[0] as usize].to_string(),
575                    }),
576                })
577            }
578            TokenInstruction::InterestBearingMintExtension => {
579                if instruction.data.len() < 2 {
580                    return Err(ParseInstructionError::InstructionNotParsable(
581                        ParsableProgram::SplToken,
582                    ));
583                }
584                parse_interest_bearing_mint_instruction(
585                    &instruction.data[1..],
586                    &instruction.accounts,
587                    account_keys,
588                )
589            }
590            TokenInstruction::CpiGuardExtension => {
591                if instruction.data.len() < 2 {
592                    return Err(ParseInstructionError::InstructionNotParsable(
593                        ParsableProgram::SplToken,
594                    ));
595                }
596                parse_cpi_guard_instruction(
597                    &instruction.data[1..],
598                    &instruction.accounts,
599                    account_keys,
600                )
601            }
602            TokenInstruction::InitializePermanentDelegate { delegate } => {
603                parse_initialize_permanent_delegate_instruction(
604                    delegate,
605                    &instruction.accounts,
606                    account_keys,
607                )
608            }
609            TokenInstruction::TransferHookExtension => {
610                if instruction.data.len() < 2 {
611                    return Err(ParseInstructionError::InstructionNotParsable(
612                        ParsableProgram::SplToken,
613                    ));
614                }
615                parse_transfer_hook_instruction(
616                    &instruction.data[1..],
617                    &instruction.accounts,
618                    account_keys,
619                )
620            }
621            TokenInstruction::ConfidentialTransferFeeExtension => {
622                if instruction.data.len() < 2 {
623                    return Err(ParseInstructionError::InstructionNotParsable(
624                        ParsableProgram::SplToken,
625                    ));
626                }
627                parse_confidential_transfer_fee_instruction(
628                    &instruction.data[1..],
629                    &instruction.accounts,
630                    account_keys,
631                )
632            }
633            TokenInstruction::WithdrawExcessLamports => {
634                check_num_token_accounts(&instruction.accounts, 3)?;
635                let mut value = json!({
636                    "source": account_keys[instruction.accounts[0] as usize].to_string(),
637                    "destination": account_keys[instruction.accounts[1] as usize].to_string(),
638                });
639                let map = value.as_object_mut().unwrap();
640                parse_signers(
641                    map,
642                    2,
643                    account_keys,
644                    &instruction.accounts,
645                    "authority",
646                    "multisigAuthority",
647                );
648                Ok(ParsedInstructionEnum {
649                    instruction_type: "withdrawExcessLamports".to_string(),
650                    info: value,
651                })
652            }
653            TokenInstruction::MetadataPointerExtension => {
654                if instruction.data.len() < 2 {
655                    return Err(ParseInstructionError::InstructionNotParsable(
656                        ParsableProgram::SplToken,
657                    ));
658                }
659                parse_metadata_pointer_instruction(
660                    &instruction.data[1..],
661                    &instruction.accounts,
662                    account_keys,
663                )
664            }
665            TokenInstruction::GroupPointerExtension => {
666                if instruction.data.len() < 2 {
667                    return Err(ParseInstructionError::InstructionNotParsable(
668                        ParsableProgram::SplToken,
669                    ));
670                }
671                parse_group_pointer_instruction(
672                    &instruction.data[1..],
673                    &instruction.accounts,
674                    account_keys,
675                )
676            }
677            TokenInstruction::GroupMemberPointerExtension => {
678                if instruction.data.len() < 2 {
679                    return Err(ParseInstructionError::InstructionNotParsable(
680                        ParsableProgram::SplToken,
681                    ));
682                }
683                parse_group_member_pointer_instruction(
684                    &instruction.data[1..],
685                    &instruction.accounts,
686                    account_keys,
687                )
688            }
689            TokenInstruction::ConfidentialMintBurnExtension => {
690                parse_confidential_mint_burn_instruction(
691                    &instruction.data[1..],
692                    &instruction.accounts,
693                    account_keys,
694                )
695            }
696            TokenInstruction::ScaledUiAmountExtension => parse_scaled_ui_amount_instruction(
697                &instruction.data[1..],
698                &instruction.accounts,
699                account_keys,
700            ),
701            TokenInstruction::PausableExtension => parse_pausable_instruction(
702                &instruction.data[1..],
703                &instruction.accounts,
704                account_keys,
705            ),
706        }
707    } else if let Ok(token_group_instruction) = TokenGroupInstruction::unpack(&instruction.data) {
708        parse_token_group_instruction(
709            &token_group_instruction,
710            &instruction.accounts,
711            account_keys,
712        )
713    } else if let Ok(token_metadata_instruction) =
714        TokenMetadataInstruction::unpack(&instruction.data)
715    {
716        parse_token_metadata_instruction(
717            &token_metadata_instruction,
718            &instruction.accounts,
719            account_keys,
720        )
721    } else {
722        Err(ParseInstructionError::InstructionNotParsable(
723            ParsableProgram::SplToken,
724        ))
725    }
726}
727
728#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
729#[serde(rename_all = "camelCase")]
730pub enum UiAuthorityType {
731    MintTokens,
732    FreezeAccount,
733    AccountOwner,
734    CloseAccount,
735    TransferFeeConfig,
736    WithheldWithdraw,
737    CloseMint,
738    InterestRate,
739    PermanentDelegate,
740    ConfidentialTransferMint,
741    TransferHookProgramId,
742    ConfidentialTransferFeeConfig,
743    MetadataPointer,
744    GroupPointer,
745    GroupMemberPointer,
746    ScaledUiAmount,
747    Pause,
748}
749
750impl From<AuthorityType> for UiAuthorityType {
751    fn from(authority_type: AuthorityType) -> Self {
752        match authority_type {
753            AuthorityType::MintTokens => UiAuthorityType::MintTokens,
754            AuthorityType::FreezeAccount => UiAuthorityType::FreezeAccount,
755            AuthorityType::AccountOwner => UiAuthorityType::AccountOwner,
756            AuthorityType::CloseAccount => UiAuthorityType::CloseAccount,
757            AuthorityType::TransferFeeConfig => UiAuthorityType::TransferFeeConfig,
758            AuthorityType::WithheldWithdraw => UiAuthorityType::WithheldWithdraw,
759            AuthorityType::CloseMint => UiAuthorityType::CloseMint,
760            AuthorityType::InterestRate => UiAuthorityType::InterestRate,
761            AuthorityType::PermanentDelegate => UiAuthorityType::PermanentDelegate,
762            AuthorityType::ConfidentialTransferMint => UiAuthorityType::ConfidentialTransferMint,
763            AuthorityType::TransferHookProgramId => UiAuthorityType::TransferHookProgramId,
764            AuthorityType::ConfidentialTransferFeeConfig => {
765                UiAuthorityType::ConfidentialTransferFeeConfig
766            }
767            AuthorityType::MetadataPointer => UiAuthorityType::MetadataPointer,
768            AuthorityType::GroupPointer => UiAuthorityType::GroupPointer,
769            AuthorityType::GroupMemberPointer => UiAuthorityType::GroupMemberPointer,
770            AuthorityType::ScaledUiAmount => UiAuthorityType::ScaledUiAmount,
771            AuthorityType::Pause => UiAuthorityType::Pause,
772        }
773    }
774}
775
776#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
777#[serde(rename_all = "camelCase")]
778pub enum UiExtensionType {
779    Uninitialized,
780    TransferFeeConfig,
781    TransferFeeAmount,
782    MintCloseAuthority,
783    ConfidentialTransferMint,
784    ConfidentialTransferAccount,
785    DefaultAccountState,
786    ImmutableOwner,
787    MemoTransfer,
788    NonTransferable,
789    InterestBearingConfig,
790    CpiGuard,
791    PermanentDelegate,
792    NonTransferableAccount,
793    TransferHook,
794    TransferHookAccount,
795    ConfidentialTransferFeeConfig,
796    ConfidentialTransferFeeAmount,
797    MetadataPointer,
798    TokenMetadata,
799    GroupPointer,
800    GroupMemberPointer,
801    TokenGroup,
802    TokenGroupMember,
803    ConfidentialMintBurn,
804    ScaledUiAmount,
805    Pausable,
806    PausableAccount,
807}
808
809impl From<ExtensionType> for UiExtensionType {
810    fn from(extension_type: ExtensionType) -> Self {
811        match extension_type {
812            ExtensionType::Uninitialized => UiExtensionType::Uninitialized,
813            ExtensionType::TransferFeeConfig => UiExtensionType::TransferFeeConfig,
814            ExtensionType::TransferFeeAmount => UiExtensionType::TransferFeeAmount,
815            ExtensionType::MintCloseAuthority => UiExtensionType::MintCloseAuthority,
816            ExtensionType::ConfidentialTransferMint => UiExtensionType::ConfidentialTransferMint,
817            ExtensionType::ConfidentialTransferAccount => {
818                UiExtensionType::ConfidentialTransferAccount
819            }
820            ExtensionType::DefaultAccountState => UiExtensionType::DefaultAccountState,
821            ExtensionType::ImmutableOwner => UiExtensionType::ImmutableOwner,
822            ExtensionType::MemoTransfer => UiExtensionType::MemoTransfer,
823            ExtensionType::NonTransferable => UiExtensionType::NonTransferable,
824            ExtensionType::InterestBearingConfig => UiExtensionType::InterestBearingConfig,
825            ExtensionType::CpiGuard => UiExtensionType::CpiGuard,
826            ExtensionType::PermanentDelegate => UiExtensionType::PermanentDelegate,
827            ExtensionType::NonTransferableAccount => UiExtensionType::NonTransferableAccount,
828            ExtensionType::TransferHook => UiExtensionType::TransferHook,
829            ExtensionType::TransferHookAccount => UiExtensionType::TransferHookAccount,
830            ExtensionType::ConfidentialTransferFeeConfig => {
831                UiExtensionType::ConfidentialTransferFeeConfig
832            }
833            ExtensionType::ConfidentialTransferFeeAmount => {
834                UiExtensionType::ConfidentialTransferFeeAmount
835            }
836            ExtensionType::MetadataPointer => UiExtensionType::MetadataPointer,
837            ExtensionType::TokenMetadata => UiExtensionType::TokenMetadata,
838            ExtensionType::GroupPointer => UiExtensionType::GroupPointer,
839            ExtensionType::GroupMemberPointer => UiExtensionType::GroupMemberPointer,
840            ExtensionType::TokenGroup => UiExtensionType::TokenGroup,
841            ExtensionType::TokenGroupMember => UiExtensionType::TokenGroupMember,
842            ExtensionType::ConfidentialMintBurn => UiExtensionType::ConfidentialMintBurn,
843            ExtensionType::ScaledUiAmount => UiExtensionType::ScaledUiAmount,
844            ExtensionType::Pausable => UiExtensionType::Pausable,
845            ExtensionType::PausableAccount => UiExtensionType::PausableAccount,
846        }
847    }
848}
849
850fn parse_signers(
851    map: &mut Map<String, Value>,
852    last_nonsigner_index: usize,
853    account_keys: &AccountKeys,
854    accounts: &[u8],
855    owner_field_name: &str,
856    multisig_field_name: &str,
857) {
858    if accounts.len() > last_nonsigner_index + 1 {
859        let mut signers: Vec<String> = vec![];
860        for i in accounts[last_nonsigner_index + 1..].iter() {
861            signers.push(account_keys[*i as usize].to_string());
862        }
863        map.insert(
864            multisig_field_name.to_string(),
865            json!(account_keys[accounts[last_nonsigner_index] as usize].to_string()),
866        );
867        map.insert("signers".to_string(), json!(signers));
868    } else {
869        map.insert(
870            owner_field_name.to_string(),
871            json!(account_keys[accounts[last_nonsigner_index] as usize].to_string()),
872        );
873    }
874}
875
876fn check_num_token_accounts(accounts: &[u8], num: usize) -> Result<(), ParseInstructionError> {
877    check_num_accounts(accounts, num, ParsableProgram::SplToken)
878}
879
880fn map_coption_pubkey(pubkey: COption<Pubkey>) -> Option<String> {
881    match pubkey {
882        COption::Some(pubkey) => Some(pubkey.to_string()),
883        COption::None => None,
884    }
885}
886
887#[cfg(test)]
888mod test {
889    use {
890        super::*, solana_message::Message, solana_pubkey::Pubkey,
891        spl_token_2022_interface::instruction::*, std::iter::repeat_with,
892    };
893
894    fn test_parse_token(program_id: &Pubkey) {
895        let mint_pubkey = Pubkey::new_unique();
896        let mint_authority = Pubkey::new_unique();
897        let freeze_authority = Pubkey::new_unique();
898        let rent_sysvar = solana_sdk_ids::sysvar::rent::id();
899
900        // Test InitializeMint variations
901        let initialize_mint_ix = initialize_mint(
902            program_id,
903            &mint_pubkey,
904            &mint_authority,
905            Some(&freeze_authority),
906            2,
907        )
908        .unwrap();
909        let message = Message::new(&[initialize_mint_ix], None);
910        let compiled_instruction = &message.instructions[0];
911        assert_eq!(
912            parse_token(
913                compiled_instruction,
914                &AccountKeys::new(&message.account_keys, None)
915            )
916            .unwrap(),
917            ParsedInstructionEnum {
918                instruction_type: "initializeMint".to_string(),
919                info: json!({
920                    "mint": mint_pubkey.to_string(),
921                    "decimals": 2,
922                    "mintAuthority": mint_authority.to_string(),
923                    "freezeAuthority": freeze_authority.to_string(),
924                    "rentSysvar": rent_sysvar.to_string(),
925                })
926            }
927        );
928
929        let initialize_mint_ix =
930            initialize_mint(program_id, &mint_pubkey, &mint_authority, None, 2).unwrap();
931        let message = Message::new(&[initialize_mint_ix], None);
932        let compiled_instruction = &message.instructions[0];
933        assert_eq!(
934            parse_token(
935                compiled_instruction,
936                &AccountKeys::new(&message.account_keys, None)
937            )
938            .unwrap(),
939            ParsedInstructionEnum {
940                instruction_type: "initializeMint".to_string(),
941                info: json!({
942                    "mint": mint_pubkey.to_string(),
943                    "decimals": 2,
944                    "mintAuthority": mint_authority.to_string(),
945                    "rentSysvar": rent_sysvar.to_string(),
946                })
947            }
948        );
949
950        // Test InitializeMint2
951        let initialize_mint_ix = initialize_mint2(
952            program_id,
953            &mint_pubkey,
954            &mint_authority,
955            Some(&freeze_authority),
956            2,
957        )
958        .unwrap();
959        let message = Message::new(&[initialize_mint_ix], None);
960        let compiled_instruction = &message.instructions[0];
961        assert_eq!(
962            parse_token(
963                compiled_instruction,
964                &AccountKeys::new(&message.account_keys, None)
965            )
966            .unwrap(),
967            ParsedInstructionEnum {
968                instruction_type: "initializeMint2".to_string(),
969                info: json!({
970                    "mint": mint_pubkey.to_string(),
971                    "decimals": 2,
972                    "mintAuthority": mint_authority.to_string(),
973                    "freezeAuthority": freeze_authority.to_string(),
974                })
975            }
976        );
977
978        // Test InitializeAccount
979        let account_pubkey = Pubkey::new_unique();
980        let owner = Pubkey::new_unique();
981        let initialize_account_ix =
982            initialize_account(program_id, &account_pubkey, &mint_pubkey, &owner).unwrap();
983        let message = Message::new(&[initialize_account_ix], None);
984        let compiled_instruction = &message.instructions[0];
985        assert_eq!(
986            parse_token(
987                compiled_instruction,
988                &AccountKeys::new(&message.account_keys, None)
989            )
990            .unwrap(),
991            ParsedInstructionEnum {
992                instruction_type: "initializeAccount".to_string(),
993                info: json!({
994                    "account": account_pubkey.to_string(),
995                    "mint": mint_pubkey.to_string(),
996                    "owner": owner.to_string(),
997                    "rentSysvar": rent_sysvar.to_string(),
998                })
999            }
1000        );
1001
1002        // Test InitializeAccount2
1003        let initialize_account_ix =
1004            initialize_account2(program_id, &account_pubkey, &mint_pubkey, &owner).unwrap();
1005        let message = Message::new(&[initialize_account_ix], None);
1006        let compiled_instruction = &message.instructions[0];
1007        assert_eq!(
1008            parse_token(
1009                compiled_instruction,
1010                &AccountKeys::new(&message.account_keys, None)
1011            )
1012            .unwrap(),
1013            ParsedInstructionEnum {
1014                instruction_type: "initializeAccount2".to_string(),
1015                info: json!({
1016                   "account": account_pubkey.to_string(),
1017                   "mint": mint_pubkey.to_string(),
1018                   "owner": owner.to_string(),
1019                   "rentSysvar": rent_sysvar.to_string(),
1020                })
1021            }
1022        );
1023
1024        // Test InitializeAccount3
1025        let initialize_account_ix =
1026            initialize_account3(program_id, &account_pubkey, &mint_pubkey, &owner).unwrap();
1027        let message = Message::new(&[initialize_account_ix], None);
1028        let compiled_instruction = &message.instructions[0];
1029        assert_eq!(
1030            parse_token(
1031                compiled_instruction,
1032                &AccountKeys::new(&message.account_keys, None)
1033            )
1034            .unwrap(),
1035            ParsedInstructionEnum {
1036                instruction_type: "initializeAccount3".to_string(),
1037                info: json!({
1038                   "account": account_pubkey.to_string(),
1039                   "mint": mint_pubkey.to_string(),
1040                   "owner": owner.to_string(),
1041                })
1042            }
1043        );
1044
1045        // Test InitializeMultisig
1046        let multisig_pubkey = Pubkey::new_unique();
1047        let multisig_signer0 = Pubkey::new_unique();
1048        let multisig_signer1 = Pubkey::new_unique();
1049        let multisig_signer2 = Pubkey::new_unique();
1050        let initialize_multisig_ix = initialize_multisig(
1051            program_id,
1052            &multisig_pubkey,
1053            &[&multisig_signer0, &multisig_signer1, &multisig_signer2],
1054            2,
1055        )
1056        .unwrap();
1057        let message = Message::new(&[initialize_multisig_ix], None);
1058        let compiled_instruction = &message.instructions[0];
1059        assert_eq!(
1060            parse_token(
1061                compiled_instruction,
1062                &AccountKeys::new(&message.account_keys, None)
1063            )
1064            .unwrap(),
1065            ParsedInstructionEnum {
1066                instruction_type: "initializeMultisig".to_string(),
1067                info: json!({
1068                    "multisig": multisig_pubkey.to_string(),
1069                    "m": 2,
1070                    "rentSysvar": rent_sysvar.to_string(),
1071                    "signers": vec![
1072                        multisig_signer0.to_string(),
1073                        multisig_signer1.to_string(),
1074                        multisig_signer2.to_string(),
1075                    ],
1076                })
1077            }
1078        );
1079
1080        // Test InitializeMultisig2
1081        let initialize_multisig_ix = initialize_multisig2(
1082            program_id,
1083            &multisig_pubkey,
1084            &[&multisig_signer0, &multisig_signer1, &multisig_signer2],
1085            2,
1086        )
1087        .unwrap();
1088        let message = Message::new(&[initialize_multisig_ix], None);
1089        let compiled_instruction = &message.instructions[0];
1090        assert_eq!(
1091            parse_token(
1092                compiled_instruction,
1093                &AccountKeys::new(&message.account_keys, None)
1094            )
1095            .unwrap(),
1096            ParsedInstructionEnum {
1097                instruction_type: "initializeMultisig2".to_string(),
1098                info: json!({
1099                    "multisig": multisig_pubkey.to_string(),
1100                    "m": 2,
1101                    "signers": vec![
1102                        multisig_signer0.to_string(),
1103                        multisig_signer1.to_string(),
1104                        multisig_signer2.to_string(),
1105                    ],
1106                })
1107            }
1108        );
1109
1110        // Test Transfer, incl multisig
1111        let recipient = Pubkey::new_unique();
1112        #[allow(deprecated)]
1113        let transfer_ix =
1114            transfer(program_id, &account_pubkey, &recipient, &owner, &[], 42).unwrap();
1115        let message = Message::new(&[transfer_ix], None);
1116        let compiled_instruction = &message.instructions[0];
1117        assert_eq!(
1118            parse_token(
1119                compiled_instruction,
1120                &AccountKeys::new(&message.account_keys, None)
1121            )
1122            .unwrap(),
1123            ParsedInstructionEnum {
1124                instruction_type: "transfer".to_string(),
1125                info: json!({
1126                    "source": account_pubkey.to_string(),
1127                    "destination": recipient.to_string(),
1128                    "authority": owner.to_string(),
1129                    "amount": "42",
1130                })
1131            }
1132        );
1133
1134        #[allow(deprecated)]
1135        let transfer_ix = transfer(
1136            program_id,
1137            &account_pubkey,
1138            &recipient,
1139            &multisig_pubkey,
1140            &[&multisig_signer0, &multisig_signer1],
1141            42,
1142        )
1143        .unwrap();
1144        let message = Message::new(&[transfer_ix], None);
1145        let compiled_instruction = &message.instructions[0];
1146        assert_eq!(
1147            parse_token(
1148                compiled_instruction,
1149                &AccountKeys::new(&message.account_keys, None)
1150            )
1151            .unwrap(),
1152            ParsedInstructionEnum {
1153                instruction_type: "transfer".to_string(),
1154                info: json!({
1155                    "source": account_pubkey.to_string(),
1156                    "destination": recipient.to_string(),
1157                    "multisigAuthority": multisig_pubkey.to_string(),
1158                    "signers": vec![
1159                        multisig_signer0.to_string(),
1160                        multisig_signer1.to_string(),
1161                    ],
1162                    "amount": "42",
1163                })
1164            }
1165        );
1166
1167        // Test Approve, incl multisig
1168        let approve_ix = approve(program_id, &account_pubkey, &recipient, &owner, &[], 42).unwrap();
1169        let message = Message::new(&[approve_ix], None);
1170        let compiled_instruction = &message.instructions[0];
1171        assert_eq!(
1172            parse_token(
1173                compiled_instruction,
1174                &AccountKeys::new(&message.account_keys, None)
1175            )
1176            .unwrap(),
1177            ParsedInstructionEnum {
1178                instruction_type: "approve".to_string(),
1179                info: json!({
1180                    "source": account_pubkey.to_string(),
1181                    "delegate": recipient.to_string(),
1182                    "owner": owner.to_string(),
1183                    "amount": "42",
1184                })
1185            }
1186        );
1187
1188        let approve_ix = approve(
1189            program_id,
1190            &account_pubkey,
1191            &recipient,
1192            &multisig_pubkey,
1193            &[&multisig_signer0, &multisig_signer1],
1194            42,
1195        )
1196        .unwrap();
1197        let message = Message::new(&[approve_ix], None);
1198        let compiled_instruction = &message.instructions[0];
1199        assert_eq!(
1200            parse_token(
1201                compiled_instruction,
1202                &AccountKeys::new(&message.account_keys, None)
1203            )
1204            .unwrap(),
1205            ParsedInstructionEnum {
1206                instruction_type: "approve".to_string(),
1207                info: json!({
1208                    "source": account_pubkey.to_string(),
1209                    "delegate": recipient.to_string(),
1210                    "multisigOwner": multisig_pubkey.to_string(),
1211                    "signers": vec![
1212                        multisig_signer0.to_string(),
1213                        multisig_signer1.to_string(),
1214                    ],
1215                    "amount": "42",
1216                })
1217            }
1218        );
1219
1220        // Test Revoke
1221        let revoke_ix = revoke(program_id, &account_pubkey, &owner, &[]).unwrap();
1222        let message = Message::new(&[revoke_ix], None);
1223        let compiled_instruction = &message.instructions[0];
1224        assert_eq!(
1225            parse_token(
1226                compiled_instruction,
1227                &AccountKeys::new(&message.account_keys, None)
1228            )
1229            .unwrap(),
1230            ParsedInstructionEnum {
1231                instruction_type: "revoke".to_string(),
1232                info: json!({
1233                    "source": account_pubkey.to_string(),
1234                    "owner": owner.to_string(),
1235                })
1236            }
1237        );
1238
1239        // Test SetOwner
1240        let new_freeze_authority = Pubkey::new_unique();
1241        let set_authority_ix = set_authority(
1242            program_id,
1243            &mint_pubkey,
1244            Some(&new_freeze_authority),
1245            AuthorityType::FreezeAccount,
1246            &freeze_authority,
1247            &[],
1248        )
1249        .unwrap();
1250        let message = Message::new(&[set_authority_ix], None);
1251        let compiled_instruction = &message.instructions[0];
1252        assert_eq!(
1253            parse_token(
1254                compiled_instruction,
1255                &AccountKeys::new(&message.account_keys, None)
1256            )
1257            .unwrap(),
1258            ParsedInstructionEnum {
1259                instruction_type: "setAuthority".to_string(),
1260                info: json!({
1261                    "mint": mint_pubkey.to_string(),
1262                    "newAuthority": new_freeze_authority.to_string(),
1263                    "authority": freeze_authority.to_string(),
1264                    "authorityType": "freezeAccount".to_string(),
1265                })
1266            }
1267        );
1268
1269        let set_authority_ix = set_authority(
1270            program_id,
1271            &account_pubkey,
1272            None,
1273            AuthorityType::CloseAccount,
1274            &owner,
1275            &[],
1276        )
1277        .unwrap();
1278        let message = Message::new(&[set_authority_ix], None);
1279        let compiled_instruction = &message.instructions[0];
1280        let new_authority: Option<String> = None;
1281        assert_eq!(
1282            parse_token(
1283                compiled_instruction,
1284                &AccountKeys::new(&message.account_keys, None)
1285            )
1286            .unwrap(),
1287            ParsedInstructionEnum {
1288                instruction_type: "setAuthority".to_string(),
1289                info: json!({
1290                    "account": account_pubkey.to_string(),
1291                    "newAuthority": new_authority,
1292                    "authority": owner.to_string(),
1293                    "authorityType": "closeAccount".to_string(),
1294                })
1295            }
1296        );
1297
1298        // Test MintTo
1299        let mint_to_ix = mint_to(
1300            program_id,
1301            &mint_pubkey,
1302            &account_pubkey,
1303            &mint_authority,
1304            &[],
1305            42,
1306        )
1307        .unwrap();
1308        let message = Message::new(&[mint_to_ix], None);
1309        let compiled_instruction = &message.instructions[0];
1310        assert_eq!(
1311            parse_token(
1312                compiled_instruction,
1313                &AccountKeys::new(&message.account_keys, None)
1314            )
1315            .unwrap(),
1316            ParsedInstructionEnum {
1317                instruction_type: "mintTo".to_string(),
1318                info: json!({
1319                    "mint": mint_pubkey.to_string(),
1320                    "account": account_pubkey.to_string(),
1321                    "mintAuthority": mint_authority.to_string(),
1322                    "amount": "42",
1323                })
1324            }
1325        );
1326
1327        // Test Burn
1328        let burn_ix = burn(program_id, &account_pubkey, &mint_pubkey, &owner, &[], 42).unwrap();
1329        let message = Message::new(&[burn_ix], None);
1330        let compiled_instruction = &message.instructions[0];
1331        assert_eq!(
1332            parse_token(
1333                compiled_instruction,
1334                &AccountKeys::new(&message.account_keys, None)
1335            )
1336            .unwrap(),
1337            ParsedInstructionEnum {
1338                instruction_type: "burn".to_string(),
1339                info: json!({
1340                    "account": account_pubkey.to_string(),
1341                    "mint": mint_pubkey.to_string(),
1342                    "authority": owner.to_string(),
1343                    "amount": "42",
1344                })
1345            }
1346        );
1347
1348        // Test CloseAccount
1349        let close_account_ix =
1350            close_account(program_id, &account_pubkey, &recipient, &owner, &[]).unwrap();
1351        let message = Message::new(&[close_account_ix], None);
1352        let compiled_instruction = &message.instructions[0];
1353        assert_eq!(
1354            parse_token(
1355                compiled_instruction,
1356                &AccountKeys::new(&message.account_keys, None)
1357            )
1358            .unwrap(),
1359            ParsedInstructionEnum {
1360                instruction_type: "closeAccount".to_string(),
1361                info: json!({
1362                    "account": account_pubkey.to_string(),
1363                    "destination": recipient.to_string(),
1364                    "owner": owner.to_string(),
1365                })
1366            }
1367        );
1368
1369        // Test FreezeAccount
1370        let freeze_account_ix = freeze_account(
1371            program_id,
1372            &account_pubkey,
1373            &mint_pubkey,
1374            &freeze_authority,
1375            &[],
1376        )
1377        .unwrap();
1378        let message = Message::new(&[freeze_account_ix], None);
1379        let compiled_instruction = &message.instructions[0];
1380        assert_eq!(
1381            parse_token(
1382                compiled_instruction,
1383                &AccountKeys::new(&message.account_keys, None)
1384            )
1385            .unwrap(),
1386            ParsedInstructionEnum {
1387                instruction_type: "freezeAccount".to_string(),
1388                info: json!({
1389                    "account": account_pubkey.to_string(),
1390                    "mint": mint_pubkey.to_string(),
1391                    "freezeAuthority": freeze_authority.to_string(),
1392                })
1393            }
1394        );
1395
1396        // Test ThawAccount
1397        let thaw_account_ix = thaw_account(
1398            program_id,
1399            &account_pubkey,
1400            &mint_pubkey,
1401            &freeze_authority,
1402            &[],
1403        )
1404        .unwrap();
1405        let message = Message::new(&[thaw_account_ix], None);
1406        let compiled_instruction = &message.instructions[0];
1407        assert_eq!(
1408            parse_token(
1409                compiled_instruction,
1410                &AccountKeys::new(&message.account_keys, None)
1411            )
1412            .unwrap(),
1413            ParsedInstructionEnum {
1414                instruction_type: "thawAccount".to_string(),
1415                info: json!({
1416                    "account": account_pubkey.to_string(),
1417                    "mint": mint_pubkey.to_string(),
1418                    "freezeAuthority": freeze_authority.to_string(),
1419                })
1420            }
1421        );
1422
1423        // Test TransferChecked, incl multisig
1424        let transfer_ix = transfer_checked(
1425            program_id,
1426            &account_pubkey,
1427            &mint_pubkey,
1428            &recipient,
1429            &owner,
1430            &[],
1431            42,
1432            2,
1433        )
1434        .unwrap();
1435        let message = Message::new(&[transfer_ix], None);
1436        let compiled_instruction = &message.instructions[0];
1437        assert_eq!(
1438            parse_token(
1439                compiled_instruction,
1440                &AccountKeys::new(&message.account_keys, None)
1441            )
1442            .unwrap(),
1443            ParsedInstructionEnum {
1444                instruction_type: "transferChecked".to_string(),
1445                info: json!({
1446                    "source": account_pubkey.to_string(),
1447                    "destination": recipient.to_string(),
1448                    "mint": mint_pubkey.to_string(),
1449                    "authority": owner.to_string(),
1450                    "tokenAmount": {
1451                        "uiAmount": 0.42,
1452                        "decimals": 2,
1453                        "amount": "42",
1454                        "uiAmountString": "0.42",
1455                   }
1456                })
1457            }
1458        );
1459
1460        let transfer_ix = transfer_checked(
1461            program_id,
1462            &account_pubkey,
1463            &mint_pubkey,
1464            &recipient,
1465            &multisig_pubkey,
1466            &[&multisig_signer0, &multisig_signer1],
1467            42,
1468            2,
1469        )
1470        .unwrap();
1471        let message = Message::new(&[transfer_ix], None);
1472        let compiled_instruction = &message.instructions[0];
1473        assert_eq!(
1474            parse_token(
1475                compiled_instruction,
1476                &AccountKeys::new(&message.account_keys, None)
1477            )
1478            .unwrap(),
1479            ParsedInstructionEnum {
1480                instruction_type: "transferChecked".to_string(),
1481                info: json!({
1482                    "source": account_pubkey.to_string(),
1483                    "destination": recipient.to_string(),
1484                    "mint": mint_pubkey.to_string(),
1485                    "multisigAuthority": multisig_pubkey.to_string(),
1486                    "signers": vec![
1487                        multisig_signer0.to_string(),
1488                        multisig_signer1.to_string(),
1489                    ],
1490                    "tokenAmount": {
1491                        "uiAmount": 0.42,
1492                        "decimals": 2,
1493                        "amount": "42",
1494                        "uiAmountString": "0.42",
1495                   }
1496                })
1497            }
1498        );
1499
1500        // Test ApproveChecked, incl multisig
1501        let approve_ix = approve_checked(
1502            program_id,
1503            &account_pubkey,
1504            &mint_pubkey,
1505            &recipient,
1506            &owner,
1507            &[],
1508            42,
1509            2,
1510        )
1511        .unwrap();
1512        let message = Message::new(&[approve_ix], None);
1513        let compiled_instruction = &message.instructions[0];
1514        assert_eq!(
1515            parse_token(
1516                compiled_instruction,
1517                &AccountKeys::new(&message.account_keys, None)
1518            )
1519            .unwrap(),
1520            ParsedInstructionEnum {
1521                instruction_type: "approveChecked".to_string(),
1522                info: json!({
1523                    "source": account_pubkey.to_string(),
1524                    "mint": mint_pubkey.to_string(),
1525                    "delegate": recipient.to_string(),
1526                    "owner": owner.to_string(),
1527                    "tokenAmount": {
1528                        "uiAmount": 0.42,
1529                        "decimals": 2,
1530                        "amount": "42",
1531                        "uiAmountString": "0.42",
1532                    }
1533                })
1534            }
1535        );
1536
1537        let approve_ix = approve_checked(
1538            program_id,
1539            &account_pubkey,
1540            &mint_pubkey,
1541            &recipient,
1542            &multisig_pubkey,
1543            &[&multisig_signer0, &multisig_signer1],
1544            42,
1545            2,
1546        )
1547        .unwrap();
1548        let message = Message::new(&[approve_ix], None);
1549        let compiled_instruction = &message.instructions[0];
1550        assert_eq!(
1551            parse_token(
1552                compiled_instruction,
1553                &AccountKeys::new(&message.account_keys, None)
1554            )
1555            .unwrap(),
1556            ParsedInstructionEnum {
1557                instruction_type: "approveChecked".to_string(),
1558                info: json!({
1559                    "source": account_pubkey.to_string(),
1560                    "mint": mint_pubkey.to_string(),
1561                    "delegate": recipient.to_string(),
1562                    "multisigOwner": multisig_pubkey.to_string(),
1563                    "signers": vec![
1564                        multisig_signer0.to_string(),
1565                        multisig_signer1.to_string(),
1566                    ],
1567                    "tokenAmount": {
1568                        "uiAmount": 0.42,
1569                        "decimals": 2,
1570                        "amount": "42",
1571                        "uiAmountString": "0.42",
1572                    }
1573                })
1574            }
1575        );
1576
1577        // Test MintToChecked
1578        let mint_to_ix = mint_to_checked(
1579            program_id,
1580            &mint_pubkey,
1581            &account_pubkey,
1582            &mint_authority,
1583            &[],
1584            42,
1585            2,
1586        )
1587        .unwrap();
1588        let message = Message::new(&[mint_to_ix], None);
1589        let compiled_instruction = &message.instructions[0];
1590        assert_eq!(
1591            parse_token(
1592                compiled_instruction,
1593                &AccountKeys::new(&message.account_keys, None)
1594            )
1595            .unwrap(),
1596            ParsedInstructionEnum {
1597                instruction_type: "mintToChecked".to_string(),
1598                info: json!({
1599                    "mint": mint_pubkey.to_string(),
1600                    "account": account_pubkey.to_string(),
1601                    "mintAuthority": mint_authority.to_string(),
1602                    "tokenAmount": {
1603                        "uiAmount": 0.42,
1604                        "decimals": 2,
1605                        "amount": "42",
1606                        "uiAmountString": "0.42",
1607                    }
1608                })
1609            }
1610        );
1611
1612        // Test BurnChecked
1613        let burn_ix = burn_checked(
1614            program_id,
1615            &account_pubkey,
1616            &mint_pubkey,
1617            &owner,
1618            &[],
1619            42,
1620            2,
1621        )
1622        .unwrap();
1623        let message = Message::new(&[burn_ix], None);
1624        let compiled_instruction = &message.instructions[0];
1625        assert_eq!(
1626            parse_token(
1627                compiled_instruction,
1628                &AccountKeys::new(&message.account_keys, None)
1629            )
1630            .unwrap(),
1631            ParsedInstructionEnum {
1632                instruction_type: "burnChecked".to_string(),
1633                info: json!({
1634                    "account": account_pubkey.to_string(),
1635                    "mint": mint_pubkey.to_string(),
1636                    "authority": owner.to_string(),
1637                    "tokenAmount": {
1638                        "uiAmount": 0.42,
1639                        "decimals": 2,
1640                        "amount": "42",
1641                        "uiAmountString": "0.42",
1642                    }
1643                })
1644            }
1645        );
1646
1647        // Test SyncNative
1648        let sync_native_ix = sync_native(program_id, &account_pubkey).unwrap();
1649        let message = Message::new(&[sync_native_ix], None);
1650        let compiled_instruction = &message.instructions[0];
1651        assert_eq!(
1652            parse_token(
1653                compiled_instruction,
1654                &AccountKeys::new(&message.account_keys, None)
1655            )
1656            .unwrap(),
1657            ParsedInstructionEnum {
1658                instruction_type: "syncNative".to_string(),
1659                info: json!({
1660                   "account": account_pubkey.to_string(),
1661                })
1662            }
1663        );
1664
1665        // Test InitializeImmutableOwner
1666        let init_immutable_owner_ix =
1667            initialize_immutable_owner(program_id, &account_pubkey).unwrap();
1668        let message = Message::new(&[init_immutable_owner_ix], None);
1669        let compiled_instruction = &message.instructions[0];
1670        assert_eq!(
1671            parse_token(
1672                compiled_instruction,
1673                &AccountKeys::new(&message.account_keys, None)
1674            )
1675            .unwrap(),
1676            ParsedInstructionEnum {
1677                instruction_type: "initializeImmutableOwner".to_string(),
1678                info: json!({
1679                   "account": account_pubkey.to_string(),
1680                })
1681            }
1682        );
1683
1684        // Test GetAccountDataSize
1685        let get_account_data_size_ix = get_account_data_size(
1686            program_id,
1687            &mint_pubkey,
1688            &[], // This emulates the packed data of spl_token_interface::instruction::get_account_data_size
1689        )
1690        .unwrap();
1691        let message = Message::new(&[get_account_data_size_ix], None);
1692        let compiled_instruction = &message.instructions[0];
1693        assert_eq!(
1694            parse_token(
1695                compiled_instruction,
1696                &AccountKeys::new(&message.account_keys, None)
1697            )
1698            .unwrap(),
1699            ParsedInstructionEnum {
1700                instruction_type: "getAccountDataSize".to_string(),
1701                info: json!({
1702                   "mint": mint_pubkey.to_string(),
1703                })
1704            }
1705        );
1706
1707        let get_account_data_size_ix = get_account_data_size(
1708            program_id,
1709            &mint_pubkey,
1710            &[ExtensionType::ImmutableOwner, ExtensionType::MemoTransfer],
1711        )
1712        .unwrap();
1713        let message = Message::new(&[get_account_data_size_ix], None);
1714        let compiled_instruction = &message.instructions[0];
1715        assert_eq!(
1716            parse_token(
1717                compiled_instruction,
1718                &AccountKeys::new(&message.account_keys, None)
1719            )
1720            .unwrap(),
1721            ParsedInstructionEnum {
1722                instruction_type: "getAccountDataSize".to_string(),
1723                info: json!({
1724                    "mint": mint_pubkey.to_string(),
1725                    "extensionTypes": [
1726                        "immutableOwner",
1727                        "memoTransfer"
1728                    ]
1729                })
1730            }
1731        );
1732
1733        // Test AmountToUiAmount
1734        let amount_to_ui_amount_ix = amount_to_ui_amount(program_id, &mint_pubkey, 4242).unwrap();
1735        let message = Message::new(&[amount_to_ui_amount_ix], None);
1736        let compiled_instruction = &message.instructions[0];
1737        assert_eq!(
1738            parse_token(
1739                compiled_instruction,
1740                &AccountKeys::new(&message.account_keys, None)
1741            )
1742            .unwrap(),
1743            ParsedInstructionEnum {
1744                instruction_type: "amountToUiAmount".to_string(),
1745                info: json!({
1746                   "mint": mint_pubkey.to_string(),
1747                   "amount": "4242",
1748                })
1749            }
1750        );
1751
1752        // Test UiAmountToAmount
1753        let ui_amount_to_amount_ix =
1754            ui_amount_to_amount(program_id, &mint_pubkey, "42.42").unwrap();
1755        let message = Message::new(&[ui_amount_to_amount_ix], None);
1756        let compiled_instruction = &message.instructions[0];
1757        assert_eq!(
1758            parse_token(
1759                compiled_instruction,
1760                &AccountKeys::new(&message.account_keys, None)
1761            )
1762            .unwrap(),
1763            ParsedInstructionEnum {
1764                instruction_type: "uiAmountToAmount".to_string(),
1765                info: json!({
1766                   "mint": mint_pubkey.to_string(),
1767                   "uiAmount": "42.42",
1768                })
1769            }
1770        );
1771    }
1772
1773    #[test]
1774    fn test_parse_token_v3() {
1775        test_parse_token(&spl_token_interface::id());
1776    }
1777
1778    #[test]
1779    fn test_parse_token_2022() {
1780        test_parse_token(&spl_token_2022_interface::id());
1781    }
1782
1783    #[test]
1784    fn test_create_native_mint() {
1785        let payer = Pubkey::new_unique();
1786        let create_native_mint_ix =
1787            create_native_mint(&spl_token_2022_interface::id(), &payer).unwrap();
1788        let message = Message::new(&[create_native_mint_ix], None);
1789        let compiled_instruction = &message.instructions[0];
1790        assert_eq!(
1791            parse_token(
1792                compiled_instruction,
1793                &AccountKeys::new(&message.account_keys, None)
1794            )
1795            .unwrap(),
1796            ParsedInstructionEnum {
1797                instruction_type: "createNativeMint".to_string(),
1798                info: json!({
1799                   "payer": payer.to_string(),
1800                   "nativeMint": spl_token_2022_interface::native_mint::id().to_string(),
1801                   "systemProgram": solana_sdk_ids::system_program::id().to_string(),
1802                })
1803            }
1804        );
1805    }
1806
1807    fn test_token_ix_not_enough_keys(program_id: &Pubkey) {
1808        let keys: Vec<Pubkey> = repeat_with(solana_pubkey::new_rand).take(10).collect();
1809
1810        // Test InitializeMint variations
1811        let initialize_mint_ix =
1812            initialize_mint(program_id, &keys[0], &keys[1], Some(&keys[2]), 2).unwrap();
1813        let mut message = Message::new(&[initialize_mint_ix], None);
1814        let compiled_instruction = &mut message.instructions[0];
1815        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..1], None)).is_err());
1816        compiled_instruction.accounts =
1817            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
1818        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
1819
1820        let initialize_mint_ix = initialize_mint(program_id, &keys[0], &keys[1], None, 2).unwrap();
1821        let mut message = Message::new(&[initialize_mint_ix], None);
1822        let compiled_instruction = &mut message.instructions[0];
1823        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..1], None)).is_err());
1824        compiled_instruction.accounts =
1825            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
1826        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
1827
1828        // Test InitializeMint2
1829        let initialize_mint_ix =
1830            initialize_mint2(program_id, &keys[0], &keys[1], Some(&keys[2]), 2).unwrap();
1831        let mut message = Message::new(&[initialize_mint_ix], None);
1832        let compiled_instruction = &mut message.instructions[0];
1833        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..0], None)).is_err());
1834        compiled_instruction.accounts =
1835            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
1836        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
1837
1838        // Test InitializeAccount
1839        let initialize_account_ix =
1840            initialize_account(program_id, &keys[0], &keys[1], &keys[2]).unwrap();
1841        let mut message = Message::new(&[initialize_account_ix], None);
1842        let compiled_instruction = &mut message.instructions[0];
1843        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..3], None)).is_err());
1844        compiled_instruction.accounts =
1845            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
1846        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
1847
1848        // Test InitializeAccount2
1849        let initialize_account_ix =
1850            initialize_account2(program_id, &keys[0], &keys[1], &keys[3]).unwrap();
1851        let mut message = Message::new(&[initialize_account_ix], None);
1852        let compiled_instruction = &mut message.instructions[0];
1853        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..2], None)).is_err());
1854        compiled_instruction.accounts =
1855            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
1856        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
1857
1858        // Test InitializeAccount3
1859        let initialize_account_ix =
1860            initialize_account3(program_id, &keys[0], &keys[1], &keys[2]).unwrap();
1861        let mut message = Message::new(&[initialize_account_ix], None);
1862        let compiled_instruction = &mut message.instructions[0];
1863        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..1], None)).is_err());
1864        compiled_instruction.accounts =
1865            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
1866        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
1867
1868        // Test InitializeMultisig
1869        let initialize_multisig_ix =
1870            initialize_multisig(program_id, &keys[0], &[&keys[1], &keys[2], &keys[3]], 2).unwrap();
1871        let mut message = Message::new(&[initialize_multisig_ix], None);
1872        let compiled_instruction = &mut message.instructions[0];
1873        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..4], None)).is_err());
1874        compiled_instruction.accounts =
1875            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 3].to_vec();
1876        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
1877
1878        // Test InitializeMultisig2
1879        let initialize_multisig_ix =
1880            initialize_multisig2(program_id, &keys[0], &[&keys[1], &keys[2], &keys[3]], 2).unwrap();
1881        let mut message = Message::new(&[initialize_multisig_ix], None);
1882        let compiled_instruction = &mut message.instructions[0];
1883        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..3], None)).is_err());
1884        compiled_instruction.accounts =
1885            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 3].to_vec();
1886        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
1887
1888        // Test Transfer, incl multisig
1889        #[allow(deprecated)]
1890        let transfer_ix = transfer(program_id, &keys[1], &keys[2], &keys[0], &[], 42).unwrap();
1891        let mut message = Message::new(&[transfer_ix], None);
1892        let compiled_instruction = &mut message.instructions[0];
1893        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..2], None)).is_err());
1894        compiled_instruction.accounts =
1895            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
1896        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
1897
1898        #[allow(deprecated)]
1899        let transfer_ix = transfer(
1900            program_id,
1901            &keys[2],
1902            &keys[3],
1903            &keys[4],
1904            &[&keys[0], &keys[1]],
1905            42,
1906        )
1907        .unwrap();
1908        let mut message = Message::new(&[transfer_ix], None);
1909        let compiled_instruction = &mut message.instructions[0];
1910        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..4], None)).is_err());
1911        compiled_instruction.accounts =
1912            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 3].to_vec();
1913        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
1914
1915        // Test Approve, incl multisig
1916        let approve_ix = approve(program_id, &keys[1], &keys[2], &keys[0], &[], 42).unwrap();
1917        let mut message = Message::new(&[approve_ix], None);
1918        let compiled_instruction = &mut message.instructions[0];
1919        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..2], None)).is_err());
1920        compiled_instruction.accounts =
1921            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
1922        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
1923
1924        let approve_ix = approve(
1925            program_id,
1926            &keys[2],
1927            &keys[3],
1928            &keys[4],
1929            &[&keys[0], &keys[1]],
1930            42,
1931        )
1932        .unwrap();
1933        let mut message = Message::new(&[approve_ix], None);
1934        let compiled_instruction = &mut message.instructions[0];
1935        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..4], None)).is_err());
1936        compiled_instruction.accounts =
1937            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 3].to_vec();
1938        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
1939
1940        // Test Revoke
1941        let revoke_ix = revoke(program_id, &keys[1], &keys[0], &[]).unwrap();
1942        let mut message = Message::new(&[revoke_ix], None);
1943        let compiled_instruction = &mut message.instructions[0];
1944        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..1], None)).is_err());
1945        compiled_instruction.accounts =
1946            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
1947        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
1948
1949        // Test SetAuthority
1950        let set_authority_ix = set_authority(
1951            program_id,
1952            &keys[1],
1953            Some(&keys[2]),
1954            AuthorityType::FreezeAccount,
1955            &keys[0],
1956            &[],
1957        )
1958        .unwrap();
1959        let mut message = Message::new(&[set_authority_ix], None);
1960        let compiled_instruction = &mut message.instructions[0];
1961        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..1], None)).is_err());
1962        compiled_instruction.accounts =
1963            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
1964        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
1965
1966        // Test MintTo
1967        let mint_to_ix = mint_to(program_id, &keys[1], &keys[2], &keys[0], &[], 42).unwrap();
1968        let mut message = Message::new(&[mint_to_ix], None);
1969        let compiled_instruction = &mut message.instructions[0];
1970        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..2], None)).is_err());
1971        compiled_instruction.accounts =
1972            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
1973        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
1974
1975        // Test Burn
1976        let burn_ix = burn(program_id, &keys[1], &keys[2], &keys[0], &[], 42).unwrap();
1977        let mut message = Message::new(&[burn_ix], None);
1978        let compiled_instruction = &mut message.instructions[0];
1979        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..2], None)).is_err());
1980        compiled_instruction.accounts =
1981            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
1982        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
1983
1984        // Test CloseAccount
1985        let close_account_ix =
1986            close_account(program_id, &keys[1], &keys[2], &keys[0], &[]).unwrap();
1987        let mut message = Message::new(&[close_account_ix], None);
1988        let compiled_instruction = &mut message.instructions[0];
1989        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..2], None)).is_err());
1990        compiled_instruction.accounts =
1991            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
1992        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
1993
1994        // Test FreezeAccount
1995        let freeze_account_ix =
1996            freeze_account(program_id, &keys[1], &keys[2], &keys[0], &[]).unwrap();
1997        let mut message = Message::new(&[freeze_account_ix], None);
1998        let compiled_instruction = &mut message.instructions[0];
1999        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..2], None)).is_err());
2000        compiled_instruction.accounts =
2001            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
2002        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
2003
2004        // Test ThawAccount
2005        let thaw_account_ix = thaw_account(program_id, &keys[1], &keys[2], &keys[0], &[]).unwrap();
2006        let mut message = Message::new(&[thaw_account_ix], None);
2007        let compiled_instruction = &mut message.instructions[0];
2008        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..2], None)).is_err());
2009        compiled_instruction.accounts =
2010            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
2011        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
2012
2013        // Test TransferChecked, incl multisig
2014        let transfer_ix = transfer_checked(
2015            program_id,
2016            &keys[1],
2017            &keys[2],
2018            &keys[3],
2019            &keys[0],
2020            &[],
2021            42,
2022            2,
2023        )
2024        .unwrap();
2025        let mut message = Message::new(&[transfer_ix], None);
2026        let compiled_instruction = &mut message.instructions[0];
2027        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..3], None)).is_err());
2028        compiled_instruction.accounts =
2029            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
2030        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
2031
2032        let transfer_ix = transfer_checked(
2033            program_id,
2034            &keys[2],
2035            &keys[3],
2036            &keys[4],
2037            &keys[5],
2038            &[&keys[0], &keys[1]],
2039            42,
2040            2,
2041        )
2042        .unwrap();
2043        let mut message = Message::new(&[transfer_ix], None);
2044        let compiled_instruction = &mut message.instructions[0];
2045        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..5], None)).is_err());
2046        compiled_instruction.accounts =
2047            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 3].to_vec();
2048        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
2049
2050        // Test ApproveChecked, incl multisig
2051        let approve_ix = approve_checked(
2052            program_id,
2053            &keys[1],
2054            &keys[2],
2055            &keys[3],
2056            &keys[0],
2057            &[],
2058            42,
2059            2,
2060        )
2061        .unwrap();
2062        let mut message = Message::new(&[approve_ix], None);
2063        let compiled_instruction = &mut message.instructions[0];
2064        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..3], None)).is_err());
2065        compiled_instruction.accounts =
2066            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
2067        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
2068
2069        let approve_ix = approve_checked(
2070            program_id,
2071            &keys[2],
2072            &keys[3],
2073            &keys[4],
2074            &keys[5],
2075            &[&keys[0], &keys[1]],
2076            42,
2077            2,
2078        )
2079        .unwrap();
2080        let mut message = Message::new(&[approve_ix], None);
2081        let compiled_instruction = &mut message.instructions[0];
2082        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..5], None)).is_err());
2083        compiled_instruction.accounts =
2084            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 3].to_vec();
2085        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
2086
2087        // Test MintToChecked
2088        let mint_to_ix =
2089            mint_to_checked(program_id, &keys[1], &keys[2], &keys[0], &[], 42, 2).unwrap();
2090        let mut message = Message::new(&[mint_to_ix], None);
2091        let compiled_instruction = &mut message.instructions[0];
2092        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..2], None)).is_err());
2093        compiled_instruction.accounts =
2094            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
2095        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
2096
2097        // Test BurnChecked
2098        let burn_ix = burn_checked(program_id, &keys[1], &keys[2], &keys[0], &[], 42, 2).unwrap();
2099        let mut message = Message::new(&[burn_ix], None);
2100        let compiled_instruction = &mut message.instructions[0];
2101        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys[0..2], None)).is_err());
2102        compiled_instruction.accounts =
2103            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
2104        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
2105
2106        // Test SyncNative
2107        let sync_native_ix = sync_native(program_id, &keys[0]).unwrap();
2108        let mut message = Message::new(&[sync_native_ix], None);
2109        let compiled_instruction = &mut message.instructions[0];
2110        assert!(parse_token(compiled_instruction, &AccountKeys::new(&[], None)).is_err());
2111        compiled_instruction.accounts =
2112            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
2113        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
2114
2115        // Test InitializeImmutableOwner
2116        let init_immutable_owner_ix = initialize_immutable_owner(program_id, &keys[0]).unwrap();
2117        let mut message = Message::new(&[init_immutable_owner_ix], None);
2118        let compiled_instruction = &mut message.instructions[0];
2119        assert!(parse_token(compiled_instruction, &AccountKeys::new(&[], None)).is_err());
2120        compiled_instruction.accounts =
2121            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
2122        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
2123
2124        // Test GetAccountDataSize
2125        let get_account_data_size_ix = get_account_data_size(program_id, &keys[0], &[]).unwrap();
2126        let mut message = Message::new(&[get_account_data_size_ix], None);
2127        let compiled_instruction = &mut message.instructions[0];
2128        assert!(parse_token(compiled_instruction, &AccountKeys::new(&[], None)).is_err());
2129        compiled_instruction.accounts =
2130            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
2131        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
2132
2133        // Test AmountToUiAmount
2134        let amount_to_ui_amount_ix = amount_to_ui_amount(program_id, &keys[0], 4242).unwrap();
2135        let mut message = Message::new(&[amount_to_ui_amount_ix], None);
2136        let compiled_instruction = &mut message.instructions[0];
2137        assert!(parse_token(compiled_instruction, &AccountKeys::new(&[], None)).is_err());
2138        compiled_instruction.accounts =
2139            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
2140        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
2141
2142        // Test UiAmountToAmount
2143        let ui_amount_to_amount_ix = ui_amount_to_amount(program_id, &keys[0], "42.42").unwrap();
2144        let mut message = Message::new(&[ui_amount_to_amount_ix], None);
2145        let compiled_instruction = &mut message.instructions[0];
2146        assert!(parse_token(compiled_instruction, &AccountKeys::new(&[], None)).is_err());
2147        compiled_instruction.accounts =
2148            compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
2149        assert!(parse_token(compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
2150    }
2151
2152    #[test]
2153    fn test_not_enough_keys_token_v3() {
2154        test_token_ix_not_enough_keys(&spl_token_interface::id());
2155    }
2156
2157    #[test]
2158    fn test_not_enough_keys_token_2022() {
2159        test_token_ix_not_enough_keys(&spl_token_2022_interface::id());
2160    }
2161}