From 2ab5f821f886675d794bc96ed70d92671e10d1b9 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Wed, 27 Jul 2022 15:34:16 -0600 Subject: [PATCH] Parse ConfidentialTransfer instructions --- transaction-status/src/parse_token.rs | 10 +- .../extension/confidential_transfer.rs | 399 ++++++++++++++++++ .../src/parse_token/extension/mod.rs | 1 + 3 files changed, 406 insertions(+), 4 deletions(-) create mode 100644 transaction-status/src/parse_token/extension/confidential_transfer.rs diff --git a/transaction-status/src/parse_token.rs b/transaction-status/src/parse_token.rs index 3c43cfbf426a28..f820883c74e1c8 100644 --- a/transaction-status/src/parse_token.rs +++ b/transaction-status/src/parse_token.rs @@ -3,8 +3,8 @@ use { check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum, }, extension::{ - default_account_state::*, interest_bearing_mint::*, memo_transfer::*, - mint_close_authority::*, reallocate::*, transfer_fee::*, + confidential_transfer::*, default_account_state::*, interest_bearing_mint::*, + memo_transfer::*, mint_close_authority::*, reallocate::*, transfer_fee::*, }, serde_json::{json, Map, Value}, solana_account_decoder::parse_token::{ @@ -510,8 +510,10 @@ pub fn parse_token( account_keys, ) } - TokenInstruction::ConfidentialTransferExtension => Err( - ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken), + TokenInstruction::ConfidentialTransferExtension => parse_confidential_transfer_instruction( + &instruction.data[1..], + &instruction.accounts, + account_keys, ), TokenInstruction::DefaultAccountStateExtension => { if instruction.data.len() <= 2 { diff --git a/transaction-status/src/parse_token/extension/confidential_transfer.rs b/transaction-status/src/parse_token/extension/confidential_transfer.rs new file mode 100644 index 00000000000000..867f90e97be133 --- /dev/null +++ b/transaction-status/src/parse_token/extension/confidential_transfer.rs @@ -0,0 +1,399 @@ +use { + super::*, + solana_account_decoder::parse_token_extension::UiConfidentialTransferMint, + spl_token_2022::{ + extension::confidential_transfer::{instruction::*, ConfidentialTransferMint}, + instruction::{decode_instruction_data, decode_instruction_type}, + }, +}; + +pub(in crate::parse_token) fn parse_confidential_transfer_instruction( + instruction_data: &[u8], + account_indexes: &[u8], + account_keys: &AccountKeys, +) -> Result { + match decode_instruction_type(instruction_data) + .map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken))? + { + ConfidentialTransferInstruction::InitializeMint => { + check_num_token_accounts(account_indexes, 1)?; + let confidential_transfer_mint: ConfidentialTransferMint = + *decode_instruction_data(instruction_data).map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let confidential_transfer_mint: UiConfidentialTransferMint = + confidential_transfer_mint.into(); + let mut value = json!({ + "mint": account_keys[account_indexes[0] as usize].to_string(), + }); + let map = value.as_object_mut().unwrap(); + map.append(json!(confidential_transfer_mint).as_object_mut().unwrap()); + Ok(ParsedInstructionEnum { + instruction_type: "initializeConfidentialTransferMint".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::UpdateMint => { + check_num_token_accounts(account_indexes, 3)?; + let confidential_transfer_mint: ConfidentialTransferMint = + *decode_instruction_data(instruction_data).map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let confidential_transfer_mint: UiConfidentialTransferMint = + confidential_transfer_mint.into(); + let mut value = json!({ + "mint": account_keys[account_indexes[0] as usize].to_string(), + "confidentialTransferMintAuthority": account_keys[account_indexes[1] as usize].to_string(), + "newConfidentialTransferMintAuthority": account_keys[account_indexes[2] as usize].to_string(), + }); + let map = value.as_object_mut().unwrap(); + map.append(json!(confidential_transfer_mint).as_object_mut().unwrap()); + Ok(ParsedInstructionEnum { + instruction_type: "updateConfidentialTransferMint".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::ConfigureAccount => { + check_num_token_accounts(account_indexes, 3)?; + let configure_account_data: ConfigureAccountInstructionData = + *decode_instruction_data(instruction_data).map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let maximum_pending_balance_credit_counter: u64 = configure_account_data + .maximum_pending_balance_credit_counter + .into(); + let mut value = json!({ + "account": account_keys[account_indexes[0] as usize].to_string(), + "mint": account_keys[account_indexes[1] as usize].to_string(), + "encryptionPubkey": format!("{}", configure_account_data.encryption_pubkey), + "decryptableZeroBalance": format!("{}", configure_account_data.decryptable_zero_balance), + "maximumPendingBalanceCreditCounter": maximum_pending_balance_credit_counter, + + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 2, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "configureConfidentialTransferAccount".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::ApproveAccount => { + check_num_token_accounts(account_indexes, 3)?; + Ok(ParsedInstructionEnum { + instruction_type: "approveConfidentialTransferAccount".to_string(), + info: json!({ + "account": account_keys[account_indexes[0] as usize].to_string(), + "mint": account_keys[account_indexes[1] as usize].to_string(), + "confidentialTransferAuditorAuthority": account_keys[account_indexes[2] as usize].to_string(), + }), + }) + } + ConfidentialTransferInstruction::EmptyAccount => { + check_num_token_accounts(account_indexes, 3)?; + let empty_account_data: EmptyAccountInstructionData = + *decode_instruction_data(instruction_data).map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let proof_instruction_offset: i8 = empty_account_data.proof_instruction_offset; + let mut value = json!({ + "account": account_keys[account_indexes[0] as usize].to_string(), + "instructionsSysvar": account_keys[account_indexes[1] as usize].to_string(), + "proofInstructionOffset": proof_instruction_offset, + + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 2, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "emptyConfidentialTransferAccount".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::Deposit => { + check_num_token_accounts(account_indexes, 4)?; + let deposit_data: DepositInstructionData = *decode_instruction_data(instruction_data) + .map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let amount: u64 = deposit_data.amount.into(); + let mut value = json!({ + "source": account_keys[account_indexes[0] as usize].to_string(), + "destination": account_keys[account_indexes[1] as usize].to_string(), + "mint": account_keys[account_indexes[2] as usize].to_string(), + "amount": amount, + "decimals": deposit_data.decimals, + + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 3, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "depositConfidentialTransfer".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::Withdraw => { + check_num_token_accounts(account_indexes, 5)?; + let withdrawal_data: WithdrawInstructionData = + *decode_instruction_data(instruction_data).map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let amount: u64 = withdrawal_data.amount.into(); + let proof_instruction_offset: i8 = withdrawal_data.proof_instruction_offset; + let mut value = json!({ + "source": account_keys[account_indexes[0] as usize].to_string(), + "destination": account_keys[account_indexes[1] as usize].to_string(), + "mint": account_keys[account_indexes[2] as usize].to_string(), + "instructionsSysvar": account_keys[account_indexes[3] as usize].to_string(), + "amount": amount, + "decimals": withdrawal_data.decimals, + "newDecryptableAvailableBalance": format!("{}", withdrawal_data.new_decryptable_available_balance), + "proofInstructionOffset": proof_instruction_offset, + + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 4, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "withdrawConfidentialTransfer".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::Transfer => { + check_num_token_accounts(account_indexes, 5)?; + let transfer_data: TransferInstructionData = *decode_instruction_data(instruction_data) + .map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let proof_instruction_offset: i8 = transfer_data.proof_instruction_offset; + let mut value = json!({ + "source": account_keys[account_indexes[0] as usize].to_string(), + "destination": account_keys[account_indexes[1] as usize].to_string(), + "mint": account_keys[account_indexes[2] as usize].to_string(), + "instructionsSysvar": account_keys[account_indexes[3] as usize].to_string(), + "newSourceDecryptableAvailableBalance": format!("{}", transfer_data.new_source_decryptable_available_balance), + "proofInstructionOffset": proof_instruction_offset, + + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 4, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "confidentialTransfer".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::TransferWithFee => { + check_num_token_accounts(account_indexes, 5)?; + let transfer_data: TransferInstructionData = *decode_instruction_data(instruction_data) + .map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let proof_instruction_offset: i8 = transfer_data.proof_instruction_offset; + let mut value = json!({ + "source": account_keys[account_indexes[0] as usize].to_string(), + "destination": account_keys[account_indexes[1] as usize].to_string(), + "mint": account_keys[account_indexes[2] as usize].to_string(), + "instructionsSysvar": account_keys[account_indexes[3] as usize].to_string(), + "newSourceDecryptableAvailableBalance": format!("{}", transfer_data.new_source_decryptable_available_balance), + "proofInstructionOffset": proof_instruction_offset, + + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 4, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "confidentialTransferWithFee".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::ApplyPendingBalance => { + check_num_token_accounts(account_indexes, 2)?; + let apply_pending_balance_data: ApplyPendingBalanceData = + *decode_instruction_data(instruction_data).map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let expected_pending_balance_credit_counter: u64 = apply_pending_balance_data + .expected_pending_balance_credit_counter + .into(); + let mut value = json!({ + "account": account_keys[account_indexes[0] as usize].to_string(), + "newDecryptableAvailableBalance": format!("{}", apply_pending_balance_data.new_decryptable_available_balance), + "expectedPendingBalanceCreditCounter": expected_pending_balance_credit_counter, + + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 1, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "applyPendingConfidentialTransferBalance".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::EnableBalanceCredits => { + check_num_token_accounts(account_indexes, 2)?; + let mut value = json!({ + "account": account_keys[account_indexes[0] as usize].to_string(), + + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 1, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "enableConfidentialTransferBalanceCredits".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::DisableBalanceCredits => { + check_num_token_accounts(account_indexes, 2)?; + let mut value = json!({ + "account": account_keys[account_indexes[0] as usize].to_string(), + + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 1, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "disableConfidentialTransferBalanceCredits".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::WithdrawWithheldTokensFromMint => { + check_num_token_accounts(account_indexes, 4)?; + let withdraw_withheld_data: WithdrawWithheldTokensFromMintData = + *decode_instruction_data(instruction_data).map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let proof_instruction_offset: i8 = withdraw_withheld_data.proof_instruction_offset; + let mut value = json!({ + "mint": account_keys[account_indexes[0] as usize].to_string(), + "feeRecipient": account_keys[account_indexes[1] as usize].to_string(), + "instructionsSysvar": account_keys[account_indexes[2] as usize].to_string(), + "proofInstructionOffset": proof_instruction_offset, + + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 3, + account_keys, + account_indexes, + "withdrawWithheldAuthority", + "multisigWithdrawWithheldAuthority", + ); + Ok(ParsedInstructionEnum { + instruction_type: "withdrawWithheldConfidentialTransferTokensFromMint".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::WithdrawWithheldTokensFromAccounts => { + let withdraw_withheld_data: WithdrawWithheldTokensFromAccountsData = + *decode_instruction_data(instruction_data).map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let num_token_accounts = withdraw_withheld_data.num_token_accounts; + check_num_token_accounts(account_indexes, 4 + num_token_accounts as usize)?; + let proof_instruction_offset: i8 = withdraw_withheld_data.proof_instruction_offset; + let mut value = json!({ + "mint": account_keys[account_indexes[0] as usize].to_string(), + "feeRecipient": account_keys[account_indexes[1] as usize].to_string(), + "instructionsSysvar": account_keys[account_indexes[2] as usize].to_string(), + "proofInstructionOffset": proof_instruction_offset, + }); + let map = value.as_object_mut().unwrap(); + let mut source_accounts: Vec = vec![]; + let first_source_account_index = account_indexes + .len() + .saturating_sub(num_token_accounts as usize); + for i in account_indexes[first_source_account_index..].iter() { + source_accounts.push(account_keys[*i as usize].to_string()); + } + map.insert("sourceAccounts".to_string(), json!(source_accounts)); + parse_signers( + map, + 3, + account_keys, + &account_indexes[..first_source_account_index], + "withdrawWithheldAuthority", + "multisigWithdrawWithheldAuthority", + ); + Ok(ParsedInstructionEnum { + instruction_type: "withdrawWithheldConfidentialTransferTokensFromAccounts" + .to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::HarvestWithheldTokensToMint => { + check_num_token_accounts(account_indexes, 1)?; + let mut value = json!({ + "mint": account_keys[account_indexes[0] as usize].to_string(), + + }); + let map = value.as_object_mut().unwrap(); + let mut source_accounts: Vec = vec![]; + for i in account_indexes.iter().skip(1) { + source_accounts.push(account_keys[*i as usize].to_string()); + } + map.insert("sourceAccounts".to_string(), json!(source_accounts)); + Ok(ParsedInstructionEnum { + instruction_type: "harvestWithheldConfidentialTransferTokensToMint".to_string(), + info: value, + }) + } + } +} diff --git a/transaction-status/src/parse_token/extension/mod.rs b/transaction-status/src/parse_token/extension/mod.rs index 3c84942651ab79..f5d8e41f4a94d5 100644 --- a/transaction-status/src/parse_token/extension/mod.rs +++ b/transaction-status/src/parse_token/extension/mod.rs @@ -1,5 +1,6 @@ use super::*; +pub(super) mod confidential_transfer; pub(super) mod default_account_state; pub(super) mod interest_bearing_mint; pub(super) mod memo_transfer;