solana_transaction_status_wasm/
parse_token.rs

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