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