solana_transaction_status/
parse_token.rs

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