diff --git a/Cargo.lock b/Cargo.lock index 0dd1d77c8189c4..bf88964cae5986 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9470,28 +9470,39 @@ dependencies = [ [[package]] name = "spl-associated-token-account" -version = "4.0.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68034596cf4804880d265f834af1ff2f821ad5293e41fa0f8f59086c181fc38e" +checksum = "76fee7d65013667032d499adc3c895e286197a35a0d3a4643c80e7fd3e9969e3" dependencies = [ - "assert_matches", "borsh 1.5.3", "num-derive", "num-traits", "solana-program", + "spl-associated-token-account-client", "spl-token", "spl-token-2022", "thiserror 1.0.69", ] +[[package]] +name = "spl-associated-token-account-client" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f8349dbcbe575f354f9a533a21f272f3eb3808a49e2fdc1c34393b88ba76cb" +dependencies = [ + "solana-instruction", + "solana-pubkey", +] + [[package]] name = "spl-discriminator" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a38ea8b6dedb7065887f12d62ed62c1743aa70749e8558f963609793f6fb12bc" +checksum = "0a20542d4c8264856d205c0090512f374dbf7b3124479a3d93ab6184ae3631aa" dependencies = [ "bytemuck", - "solana-program", + "solana-program-error", + "solana-sha256-hasher", "spl-discriminator-derive", ] @@ -9519,44 +9530,73 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "spl-elgamal-registry" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a157622a63a4d12fbd8b347fd75ee442cb913137fa98647824c992fb049a15b" +dependencies = [ + "bytemuck", + "solana-program", + "solana-zk-sdk", + "spl-pod", + "spl-token-confidential-transfer-proof-extraction", +] + [[package]] name = "spl-instruction-padding" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cdbcd2652240c5b04befd4807c2b0f9412c66b18db398ca955f236a8ff1c378" +checksum = "5352d4c4a6d455fc96320342aeab9f522f057e2666ebe40b2e079d054339ab8f" dependencies = [ "num_enum", - "solana-program", + "solana-account-info", + "solana-cpi", + "solana-instruction", + "solana-program-entrypoint", + "solana-program-error", + "solana-pubkey", ] [[package]] name = "spl-memo" -version = "5.0.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0dba2f2bb6419523405d21c301a32c9f9568354d4742552e7972af801f4bdb3" +checksum = "9f09647c0974e33366efeb83b8e2daebb329f0420149e74d3a4bd2c08cf9f7cb" dependencies = [ - "solana-program", + "solana-account-info", + "solana-instruction", + "solana-msg", + "solana-program-entrypoint", + "solana-program-error", + "solana-pubkey", ] [[package]] name = "spl-pod" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6166a591d93af33afd75bbd8573c5fd95fb1213f1bf254f0508c89fdb5ee156" +checksum = "41a7d5950993e1ff2680bd989df298eeb169367fb2f9deeef1f132de6e4e8016" dependencies = [ "borsh 1.5.3", "bytemuck", "bytemuck_derive", - "solana-program", - "solana-zk-token-sdk", - "spl-program-error", + "num-derive", + "num-traits", + "solana-decode-error", + "solana-msg", + "solana-program-error", + "solana-program-option", + "solana-pubkey", + "solana-zk-sdk", + "thiserror 1.0.69", ] [[package]] name = "spl-program-error" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7b28bed65356558133751cc32b48a7a5ddfc59ac4e941314630bbed1ac10532" +checksum = "9d39b5186f42b2b50168029d81e58e800b690877ef0b30580d107659250da1d1" dependencies = [ "num-derive", "num-traits", @@ -9579,23 +9619,31 @@ dependencies = [ [[package]] name = "spl-tlv-account-resolution" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a75a5f0fcc58126693ed78a17042e9dc53f07e357d6be91789f7d62aff61a4" +checksum = "cd99ff1e9ed2ab86e3fd582850d47a739fec1be9f4661cba1782d3a0f26805f3" dependencies = [ "bytemuck", - "solana-program", + "num-derive", + "num-traits", + "solana-account-info", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", "spl-discriminator", "spl-pod", "spl-program-error", "spl-type-length-value", + "thiserror 1.0.69", ] [[package]] name = "spl-token" -version = "6.0.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a0f06ac7f23dc0984931b1fe309468f14ea58e32660439c1cef19456f5d0e3" +checksum = "ed320a6c934128d4f7e54fe00e16b8aeaecf215799d060ae14f93378da6dc834" dependencies = [ "arrayref", "bytemuck", @@ -9608,9 +9656,9 @@ dependencies = [ [[package]] name = "spl-token-2022" -version = "4.0.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c10f3483e48679619c76598d4e4aebb955bc49b0a5cc63323afbf44135c9bf" +checksum = "5b27f7405010ef816587c944536b0eafbcc35206ab6ba0f2ca79f1d28e488f4f" dependencies = [ "arrayref", "bytemuck", @@ -9619,10 +9667,14 @@ dependencies = [ "num_enum", "solana-program", "solana-security-txt", - "solana-zk-token-sdk", + "solana-zk-sdk", + "spl-elgamal-registry", "spl-memo", "spl-pod", "spl-token", + "spl-token-confidential-transfer-ciphertext-arithmetic", + "spl-token-confidential-transfer-proof-extraction", + "spl-token-confidential-transfer-proof-generation", "spl-token-group-interface", "spl-token-metadata-interface", "spl-transfer-hook-interface", @@ -9631,59 +9683,123 @@ dependencies = [ ] [[package]] -name = "spl-token-group-interface" -version = "0.3.0" +name = "spl-token-confidential-transfer-ciphertext-arithmetic" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8752b85a5ecc1d9f3a43bce3dd9a6a053673aacf5deb513d1cbb88d3534ffd" +checksum = "c1f1bf731fc65546330a7929a9735679add70f828dd076a4e69b59d3afb5423c" +dependencies = [ + "base64 0.22.1", + "bytemuck", + "solana-curve25519", + "solana-zk-sdk", +] + +[[package]] +name = "spl-token-confidential-transfer-proof-extraction" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "383937e637ccbe546f736d5115344351ebd4d2a076907582335261da58236816" dependencies = [ "bytemuck", + "solana-curve25519", "solana-program", + "solana-zk-sdk", + "spl-pod", + "thiserror 1.0.69", +] + +[[package]] +name = "spl-token-confidential-transfer-proof-generation" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8627184782eec1894de8ea26129c61303f1f0adeed65c20e0b10bc584f09356d" +dependencies = [ + "curve25519-dalek 4.1.3", + "solana-zk-sdk", + "thiserror 1.0.69", +] + +[[package]] +name = "spl-token-group-interface" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d595667ed72dbfed8c251708f406d7c2814a3fa6879893b323d56a10bedfc799" +dependencies = [ + "bytemuck", + "num-derive", + "num-traits", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", "spl-discriminator", "spl-pod", - "spl-program-error", + "thiserror 1.0.69", ] [[package]] name = "spl-token-metadata-interface" -version = "0.4.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c2318ddff97e006ed9b1291ebec0750a78547f870f62a69c56fe3b46a5d8fc" +checksum = "dfb9c89dbc877abd735f05547dcf9e6e12c00c11d6d74d8817506cab4c99fdbb" dependencies = [ "borsh 1.5.3", - "solana-program", + "num-derive", + "num-traits", + "solana-borsh", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", "spl-discriminator", "spl-pod", - "spl-program-error", "spl-type-length-value", + "thiserror 1.0.69", ] [[package]] name = "spl-transfer-hook-interface" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a110f33d941275d9f868b96daaa993f1e73b6806cc8836e43075b4d3ad8338a7" +checksum = "4aa7503d52107c33c88e845e1351565050362c2314036ddf19a36cd25137c043" dependencies = [ "arrayref", "bytemuck", - "solana-program", + "num-derive", + "num-traits", + "solana-account-info", + "solana-cpi", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", "spl-discriminator", "spl-pod", "spl-program-error", "spl-tlv-account-resolution", "spl-type-length-value", + "thiserror 1.0.69", ] [[package]] name = "spl-type-length-value" -version = "0.5.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdcd73ec187bc409464c60759232e309f83b52a18a9c5610bf281c9c6432918c" +checksum = "ba70ef09b13af616a4c987797870122863cba03acc4284f226a4473b043923f9" dependencies = [ "bytemuck", - "solana-program", + "num-derive", + "num-traits", + "solana-account-info", + "solana-decode-error", + "solana-msg", + "solana-program-error", "spl-discriminator", "spl-pod", - "spl-program-error", + "thiserror 1.0.69", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 0a69c87500c164..de53025535647b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -564,14 +564,14 @@ solana-zk-sdk = { path = "zk-sdk", version = "=2.2.0" } solana-zk-token-proof-program = { path = "programs/zk-token-proof", version = "=2.2.0" } solana-zk-token-sdk = { path = "zk-token-sdk", version = "=2.2.0" } solana_rbpf = "=0.8.5" -spl-associated-token-account = "=4.0.0" -spl-instruction-padding = "0.2" -spl-memo = "=5.0.0" -spl-pod = "=0.3.0" -spl-token = "=6.0.0" -spl-token-2022 = "=4.0.0" -spl-token-group-interface = "=0.3.0" -spl-token-metadata-interface = "=0.4.0" +spl-associated-token-account = "=6.0.0" +spl-instruction-padding = "0.3" +spl-memo = "=6.0.0" +spl-pod = "=0.5.0" +spl-token = "=7.0.0" +spl-token-2022 = "=6.0.0" +spl-token-group-interface = "=0.5.0" +spl-token-metadata-interface = "=0.6.0" static_assertions = "1.1.0" stream-cancel = "0.8.2" strum = "0.24" @@ -647,9 +647,21 @@ crossbeam-epoch = { git = "https://github.com/anza-xyz/crossbeam", rev = "fd279d # There is a similar override in `programs/sbf/Cargo.toml`. Please keep both # comments and the overrides in sync. solana-curve25519 = { path = "curves/curve25519" } +solana-account-info = { path = "sdk/account-info" } +solana-borsh = { path = "sdk/borsh" } +solana-cpi = { path = "sdk/cpi" } +solana-decode-error = { path = "sdk/decode-error" } +solana-hash = { path = "sdk/hash" } +solana-instruction = { path = "sdk/instruction" } +solana-msg = { path = "sdk/msg" } solana-program = { path = "sdk/program" } +solana-program-entrypoint = { path = "sdk/program-entrypoint" } +solana-program-error = { path = "sdk/program-error" } +solana-program-option = { path = "sdk/program-option" } +solana-program-pack = { path = "sdk/program-pack" } +solana-pubkey = { path = "sdk/pubkey" } +solana-sha256-hasher = { path = "sdk/sha256-hasher" } solana-zk-sdk = { path = "zk-sdk" } -solana-zk-token-sdk = { path = "zk-token-sdk" } # curve25519-dalek uses the simd backend by default in v4 if possible, # which has very slow performance on some platforms with opt-level 0, diff --git a/account-decoder-client-types/src/token.rs b/account-decoder-client-types/src/token.rs index 49518b1f3a4765..105ab7e5dcbe9a 100644 --- a/account-decoder-client-types/src/token.rs +++ b/account-decoder-client-types/src/token.rs @@ -119,6 +119,7 @@ pub enum UiExtension { GroupMemberPointer(UiGroupMemberPointer), TokenGroup(UiTokenGroup), TokenGroupMember(UiTokenGroupMember), + ConfidentialMintBurn(UiConfidentialMintBurn), UnparseableExtension, } @@ -127,7 +128,7 @@ pub enum UiExtension { pub struct UiTokenGroupMember { pub mint: String, pub group: String, - pub member_number: u32, + pub member_number: u64, } #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] @@ -135,8 +136,8 @@ pub struct UiTokenGroupMember { pub struct UiTokenGroup { pub update_authority: Option, pub mint: String, - pub size: u32, - pub max_size: u32, + pub size: u64, + pub max_size: u64, } #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] @@ -288,6 +289,14 @@ pub struct UiTokenMetadata { pub additional_metadata: Vec<(String, String)>, } +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct UiConfidentialMintBurn { + pub confidential_supply: String, + pub decryptable_supply: String, + pub supply_elgamal_pubkey: String, +} + #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct UiMint { diff --git a/account-decoder/src/parse_token_extension.rs b/account-decoder/src/parse_token_extension.rs index 8ab6644980c4e3..0e63785c367676 100644 --- a/account-decoder/src/parse_token_extension.rs +++ b/account-decoder/src/parse_token_extension.rs @@ -1,5 +1,5 @@ pub use solana_account_decoder_client_types::token::{ - UiConfidentialTransferAccount, UiConfidentialTransferFeeAmount, + UiConfidentialMintBurn, UiConfidentialTransferAccount, UiConfidentialTransferFeeAmount, UiConfidentialTransferFeeConfig, UiConfidentialTransferMint, UiCpiGuard, UiDefaultAccountState, UiExtension, UiGroupMemberPointer, UiGroupPointer, UiInterestBearingConfig, UiMemoTransfer, UiMetadataPointer, UiMintCloseAuthority, UiPermanentDelegate, UiTokenGroup, UiTokenGroupMember, @@ -12,7 +12,7 @@ use { spl_token_2022::{ extension::{self, BaseState, BaseStateWithExtensions, ExtensionType, StateWithExtensions}, solana_program::pubkey::Pubkey, - solana_zk_token_sdk::zk_token_elgamal::pod::ElGamalPubkey, + solana_zk_sdk::encryption::pod::elgamal::PodElGamalPubkey, }, spl_token_group_interface::state::{TokenGroup, TokenGroupMember}, spl_token_metadata_interface::state::TokenMetadata, @@ -135,6 +135,12 @@ pub fn parse_extension( .get_extension::() .map(|&extension| UiExtension::TokenGroupMember(convert_token_group_member(extension))) .unwrap_or(UiExtension::UnparseableExtension), + ExtensionType::ConfidentialMintBurn => account + .get_extension::() + .map(|&extension| { + UiExtension::ConfidentialMintBurn(convert_confidential_mint_burn(extension)) + }) + .unwrap_or(UiExtension::UnparseableExtension), } } @@ -232,7 +238,7 @@ pub fn convert_confidential_transfer_mint( confidential_transfer_mint: extension::confidential_transfer::ConfidentialTransferMint, ) -> UiConfidentialTransferMint { let authority: Option = confidential_transfer_mint.authority.into(); - let auditor_elgamal_pubkey: Option = + let auditor_elgamal_pubkey: Option = confidential_transfer_mint.auditor_elgamal_pubkey.into(); UiConfidentialTransferMint { authority: authority.map(|pubkey| pubkey.to_string()), @@ -245,7 +251,7 @@ pub fn convert_confidential_transfer_fee_config( confidential_transfer_fee_config: extension::confidential_transfer_fee::ConfidentialTransferFeeConfig, ) -> UiConfidentialTransferFeeConfig { let authority: Option = confidential_transfer_fee_config.authority.into(); - let withdraw_withheld_authority_elgamal_pubkey: Option = + let withdraw_withheld_authority_elgamal_pubkey: Option = confidential_transfer_fee_config .withdraw_withheld_authority_elgamal_pubkey .into(); @@ -379,3 +385,13 @@ fn convert_token_group_member(member: TokenGroupMember) -> UiTokenGroupMember { member_number: member.member_number.into(), } } + +fn convert_confidential_mint_burn( + confidential_mint_burn: extension::confidential_mint_burn::ConfidentialMintBurn, +) -> UiConfidentialMintBurn { + UiConfidentialMintBurn { + confidential_supply: confidential_mint_burn.confidential_supply.to_string(), + decryptable_supply: confidential_mint_burn.decryptable_supply.to_string(), + supply_elgamal_pubkey: confidential_mint_burn.supply_elgamal_pubkey.to_string(), + } +} diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index af34c3be761c26..487ac318fa17b3 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -6680,7 +6680,6 @@ version = "2.2.0" dependencies = [ "solana-curve25519", "solana-program", - "solana-zk-token-sdk", ] [[package]] @@ -7874,28 +7873,39 @@ dependencies = [ [[package]] name = "spl-associated-token-account" -version = "4.0.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68034596cf4804880d265f834af1ff2f821ad5293e41fa0f8f59086c181fc38e" +checksum = "76fee7d65013667032d499adc3c895e286197a35a0d3a4643c80e7fd3e9969e3" dependencies = [ - "assert_matches", "borsh 1.5.3", "num-derive", "num-traits", "solana-program", + "spl-associated-token-account-client", "spl-token", "spl-token-2022", "thiserror 1.0.69", ] +[[package]] +name = "spl-associated-token-account-client" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f8349dbcbe575f354f9a533a21f272f3eb3808a49e2fdc1c34393b88ba76cb" +dependencies = [ + "solana-instruction", + "solana-pubkey", +] + [[package]] name = "spl-discriminator" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a38ea8b6dedb7065887f12d62ed62c1743aa70749e8558f963609793f6fb12bc" +checksum = "0a20542d4c8264856d205c0090512f374dbf7b3124479a3d93ab6184ae3631aa" dependencies = [ "bytemuck", - "solana-program", + "solana-program-error", + "solana-sha256-hasher", "spl-discriminator-derive", ] @@ -7924,33 +7934,57 @@ dependencies = [ ] [[package]] -name = "spl-memo" -version = "5.0.0" +name = "spl-elgamal-registry" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0dba2f2bb6419523405d21c301a32c9f9568354d4742552e7972af801f4bdb3" +checksum = "9a157622a63a4d12fbd8b347fd75ee442cb913137fa98647824c992fb049a15b" dependencies = [ + "bytemuck", "solana-program", + "solana-zk-sdk", + "spl-pod", + "spl-token-confidential-transfer-proof-extraction", +] + +[[package]] +name = "spl-memo" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f09647c0974e33366efeb83b8e2daebb329f0420149e74d3a4bd2c08cf9f7cb" +dependencies = [ + "solana-account-info", + "solana-instruction", + "solana-msg", + "solana-program-entrypoint", + "solana-program-error", + "solana-pubkey", ] [[package]] name = "spl-pod" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6166a591d93af33afd75bbd8573c5fd95fb1213f1bf254f0508c89fdb5ee156" +checksum = "41a7d5950993e1ff2680bd989df298eeb169367fb2f9deeef1f132de6e4e8016" dependencies = [ "borsh 1.5.3", "bytemuck", "bytemuck_derive", - "solana-program", - "solana-zk-token-sdk", - "spl-program-error", + "num-derive", + "num-traits", + "solana-decode-error", + "solana-msg", + "solana-program-error", + "solana-program-option", + "solana-pubkey", + "solana-zk-sdk", + "thiserror 1.0.69", ] [[package]] name = "spl-program-error" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7b28bed65356558133751cc32b48a7a5ddfc59ac4e941314630bbed1ac10532" +checksum = "9d39b5186f42b2b50168029d81e58e800b690877ef0b30580d107659250da1d1" dependencies = [ "num-derive", "num-traits", @@ -7973,23 +8007,31 @@ dependencies = [ [[package]] name = "spl-tlv-account-resolution" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a75a5f0fcc58126693ed78a17042e9dc53f07e357d6be91789f7d62aff61a4" +checksum = "cd99ff1e9ed2ab86e3fd582850d47a739fec1be9f4661cba1782d3a0f26805f3" dependencies = [ "bytemuck", - "solana-program", + "num-derive", + "num-traits", + "solana-account-info", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", "spl-discriminator", "spl-pod", "spl-program-error", "spl-type-length-value", + "thiserror 1.0.69", ] [[package]] name = "spl-token" -version = "6.0.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a0f06ac7f23dc0984931b1fe309468f14ea58e32660439c1cef19456f5d0e3" +checksum = "ed320a6c934128d4f7e54fe00e16b8aeaecf215799d060ae14f93378da6dc834" dependencies = [ "arrayref", "bytemuck", @@ -8002,9 +8044,9 @@ dependencies = [ [[package]] name = "spl-token-2022" -version = "4.0.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c10f3483e48679619c76598d4e4aebb955bc49b0a5cc63323afbf44135c9bf" +checksum = "5b27f7405010ef816587c944536b0eafbcc35206ab6ba0f2ca79f1d28e488f4f" dependencies = [ "arrayref", "bytemuck", @@ -8013,10 +8055,14 @@ dependencies = [ "num_enum", "solana-program", "solana-security-txt", - "solana-zk-token-sdk", + "solana-zk-sdk", + "spl-elgamal-registry", "spl-memo", "spl-pod", "spl-token", + "spl-token-confidential-transfer-ciphertext-arithmetic", + "spl-token-confidential-transfer-proof-extraction", + "spl-token-confidential-transfer-proof-generation", "spl-token-group-interface", "spl-token-metadata-interface", "spl-transfer-hook-interface", @@ -8025,59 +8071,123 @@ dependencies = [ ] [[package]] -name = "spl-token-group-interface" -version = "0.3.0" +name = "spl-token-confidential-transfer-ciphertext-arithmetic" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1f1bf731fc65546330a7929a9735679add70f828dd076a4e69b59d3afb5423c" +dependencies = [ + "base64 0.22.1", + "bytemuck", + "solana-curve25519", + "solana-zk-sdk", +] + +[[package]] +name = "spl-token-confidential-transfer-proof-extraction" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8752b85a5ecc1d9f3a43bce3dd9a6a053673aacf5deb513d1cbb88d3534ffd" +checksum = "383937e637ccbe546f736d5115344351ebd4d2a076907582335261da58236816" dependencies = [ "bytemuck", + "solana-curve25519", "solana-program", + "solana-zk-sdk", + "spl-pod", + "thiserror 1.0.69", +] + +[[package]] +name = "spl-token-confidential-transfer-proof-generation" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8627184782eec1894de8ea26129c61303f1f0adeed65c20e0b10bc584f09356d" +dependencies = [ + "curve25519-dalek 4.1.3", + "solana-zk-sdk", + "thiserror 1.0.69", +] + +[[package]] +name = "spl-token-group-interface" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d595667ed72dbfed8c251708f406d7c2814a3fa6879893b323d56a10bedfc799" +dependencies = [ + "bytemuck", + "num-derive", + "num-traits", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", "spl-discriminator", "spl-pod", - "spl-program-error", + "thiserror 1.0.69", ] [[package]] name = "spl-token-metadata-interface" -version = "0.4.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c2318ddff97e006ed9b1291ebec0750a78547f870f62a69c56fe3b46a5d8fc" +checksum = "dfb9c89dbc877abd735f05547dcf9e6e12c00c11d6d74d8817506cab4c99fdbb" dependencies = [ "borsh 1.5.3", - "solana-program", + "num-derive", + "num-traits", + "solana-borsh", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", "spl-discriminator", "spl-pod", - "spl-program-error", "spl-type-length-value", + "thiserror 1.0.69", ] [[package]] name = "spl-transfer-hook-interface" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a110f33d941275d9f868b96daaa993f1e73b6806cc8836e43075b4d3ad8338a7" +checksum = "4aa7503d52107c33c88e845e1351565050362c2314036ddf19a36cd25137c043" dependencies = [ "arrayref", "bytemuck", - "solana-program", + "num-derive", + "num-traits", + "solana-account-info", + "solana-cpi", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", "spl-discriminator", "spl-pod", "spl-program-error", "spl-tlv-account-resolution", "spl-type-length-value", + "thiserror 1.0.69", ] [[package]] name = "spl-type-length-value" -version = "0.5.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdcd73ec187bc409464c60759232e309f83b52a18a9c5610bf281c9c6432918c" +checksum = "ba70ef09b13af616a4c987797870122863cba03acc4284f226a4473b043923f9" dependencies = [ "bytemuck", - "solana-program", + "num-derive", + "num-traits", + "solana-account-info", + "solana-decode-error", + "solana-msg", + "solana-program-error", "spl-discriminator", "spl-pod", - "spl-program-error", + "thiserror 1.0.69", ] [[package]] diff --git a/programs/sbf/Cargo.toml b/programs/sbf/Cargo.toml index 6d33e9518d0924..7d48d1ab8efca2 100644 --- a/programs/sbf/Cargo.toml +++ b/programs/sbf/Cargo.toml @@ -67,7 +67,7 @@ solana-type-overrides = { path = "../../type-overrides", version = "=2.2.0" } solana-vote = { path = "../../vote", version = "=2.2.0" } solana-vote-program = { path = "../../programs/vote", version = "=2.2.0" } agave-validator = { path = "../../validator", version = "=2.2.0" } -solana-zk-token-sdk = { path = "../../zk-token-sdk", version = "=2.2.0" } +solana-zk-sdk = { path = "../../zk-sdk", version = "=2.2.0" } solana_rbpf = "=0.8.5" thiserror = "1.0" @@ -240,5 +240,18 @@ members = [ # There is a similar override in `../../Cargo.toml`. Please keep both comments # and the overrides in sync. solana-curve25519 = { path = "../../curves/curve25519" } +solana-account-info = { path = "../../sdk/account-info" } +solana-borsh = { path = "../../sdk/borsh" } +solana-cpi = { path = "../../sdk/cpi" } +solana-decode-error = { path = "../../sdk/decode-error" } +solana-hash = { path = "../../sdk/hash" } +solana-instruction = { path = "../../sdk/instruction" } +solana-msg = { path = "../../sdk/msg" } solana-program = { path = "../../sdk/program" } -solana-zk-token-sdk = { path = "../../zk-token-sdk" } +solana-program-entrypoint = { path = "../../sdk/program-entrypoint" } +solana-program-error = { path = "../../sdk/program-error" } +solana-program-option = { path = "../../sdk/program-option" } +solana-program-pack = { path = "../../sdk/program-pack" } +solana-pubkey = { path = "../../sdk/pubkey" } +solana-sha256-hasher = { path = "../../sdk/sha256-hasher" } +solana-zk-sdk = { path = "../../zk-sdk" } diff --git a/programs/sbf/rust/curve25519/Cargo.toml b/programs/sbf/rust/curve25519/Cargo.toml index ad555810ff203e..39f871552dc878 100644 --- a/programs/sbf/rust/curve25519/Cargo.toml +++ b/programs/sbf/rust/curve25519/Cargo.toml @@ -11,7 +11,6 @@ edition = { workspace = true } [dependencies] solana-curve25519 = { workspace = true } solana-program = { workspace = true } -solana-zk-token-sdk = { workspace = true } [lib] crate-type = ["cdylib"] diff --git a/transaction-status/src/parse_token.rs b/transaction-status/src/parse_token.rs index 4bc5ea14cbf41a..5660da3024afdc 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::{ - confidential_transfer::*, confidential_transfer_fee::*, cpi_guard::*, - default_account_state::*, group_member_pointer::*, group_pointer::*, + confidential_mint_burn::*, confidential_transfer::*, confidential_transfer_fee::*, + cpi_guard::*, default_account_state::*, group_member_pointer::*, group_pointer::*, interest_bearing_mint::*, memo_transfer::*, metadata_pointer::*, mint_close_authority::*, permanent_delegate::*, reallocate::*, token_group::*, token_metadata::*, transfer_fee::*, transfer_hook::*, @@ -682,6 +682,13 @@ pub fn parse_token( account_keys, ) } + TokenInstruction::ConfidentialMintBurnExtension => { + parse_confidential_mint_burn_instruction( + &instruction.data[1..], + &instruction.accounts, + account_keys, + ) + } } } else if let Ok(token_group_instruction) = TokenGroupInstruction::unpack(&instruction.data) { parse_token_group_instruction( @@ -775,6 +782,7 @@ pub enum UiExtensionType { GroupMemberPointer, TokenGroup, TokenGroupMember, + ConfidentialMintBurn, } impl From for UiExtensionType { @@ -810,6 +818,7 @@ impl From for UiExtensionType { ExtensionType::GroupMemberPointer => UiExtensionType::GroupMemberPointer, ExtensionType::TokenGroup => UiExtensionType::TokenGroup, ExtensionType::TokenGroupMember => UiExtensionType::TokenGroupMember, + ExtensionType::ConfidentialMintBurn => UiExtensionType::ConfidentialMintBurn, } } } diff --git a/transaction-status/src/parse_token/extension/confidential_mint_burn.rs b/transaction-status/src/parse_token/extension/confidential_mint_burn.rs new file mode 100644 index 00000000000000..9eba0158460555 --- /dev/null +++ b/transaction-status/src/parse_token/extension/confidential_mint_burn.rs @@ -0,0 +1,256 @@ +use { + super::*, + spl_token_2022::{ + extension::confidential_mint_burn::instruction::*, + instruction::{decode_instruction_data, decode_instruction_type}, + }, +}; + +pub(in crate::parse_token) fn parse_confidential_mint_burn_instruction( + instruction_data: &[u8], + account_indexes: &[u8], + account_keys: &AccountKeys, +) -> Result { + match decode_instruction_type(instruction_data) + .map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken))? + { + ConfidentialMintBurnInstruction::InitializeMint => { + check_num_token_accounts(account_indexes, 1)?; + let initialize_mint_data: InitializeMintData = + *decode_instruction_data(instruction_data).map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let value = json!({ + "mint": account_keys[account_indexes[0] as usize].to_string(), + "supplyElGamalPubkey": initialize_mint_data.supply_elgamal_pubkey.to_string(), + "decryptableSupply": initialize_mint_data.decryptable_supply.to_string(), + }); + Ok(ParsedInstructionEnum { + instruction_type: "initializeConfidentialMintBurnMint".to_string(), + info: value, + }) + } + ConfidentialMintBurnInstruction::UpdateDecryptableSupply => { + check_num_token_accounts(account_indexes, 2)?; + let update_decryptable_supply: UpdateDecryptableSupplyData = + *decode_instruction_data(instruction_data).map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let mut value = json!({ + "mint": account_keys[account_indexes[0] as usize].to_string(), + "newDecryptableSupply": update_decryptable_supply.new_decryptable_supply.to_string(), + + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 1, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "updateConfidentialMintBurnDecryptableSupply".to_string(), + info: value, + }) + } + ConfidentialMintBurnInstruction::RotateSupplyElGamalPubkey => { + check_num_token_accounts(account_indexes, 3)?; + let rotate_supply_data: RotateSupplyElGamalPubkeyData = + *decode_instruction_data(instruction_data).map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let mut value = json!({ + "mint": account_keys[account_indexes[0] as usize].to_string(), + "newSupplyElGamalPubkey": rotate_supply_data.new_supply_elgamal_pubkey.to_string(), + "proofInstructionOffset": rotate_supply_data.proof_instruction_offset, + + }); + let map = value.as_object_mut().unwrap(); + if rotate_supply_data.proof_instruction_offset == 0 { + map.insert( + "proofAccount".to_string(), + json!(account_keys[account_indexes[1] as usize].to_string()), + ); + } else { + map.insert( + "instructionsSysvar".to_string(), + json!(account_keys[account_indexes[1] as usize].to_string()), + ); + } + parse_signers( + map, + 2, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "rotateConfidentialMintBurnSupplyElGamalPubkey".to_string(), + info: value, + }) + } + ConfidentialMintBurnInstruction::Mint => { + check_num_token_accounts(account_indexes, 3)?; + let mint_data: MintInstructionData = *decode_instruction_data(instruction_data) + .map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let mut value = json!({ + "destination": account_keys[account_indexes[0] as usize].to_string(), + "mint": account_keys[account_indexes[1] as usize].to_string(), + "newDecryptableSupply": mint_data.new_decryptable_supply.to_string(), + "equalityProofInstructionOffset": mint_data.equality_proof_instruction_offset, + "ciphertextValidityProofInstructionOffset": mint_data.ciphertext_validity_proof_instruction_offset, + "rangeProofInstructionOffset": mint_data.range_proof_instruction_offset, + + }); + let mut offset = 2; + let map = value.as_object_mut().unwrap(); + if mint_data.equality_proof_instruction_offset != 0 + || mint_data.ciphertext_validity_proof_instruction_offset != 0 + || mint_data.range_proof_instruction_offset != 0 + { + map.insert( + "instructionsSysvar".to_string(), + json!(account_keys[account_indexes[offset] as usize].to_string()), + ); + offset += 1; + } + + // Assume that extra accounts are proof accounts and not multisig + // signers. This might be wrong, but it's the best possible option. + if offset < account_indexes.len() - 1 { + let label = if mint_data.equality_proof_instruction_offset == 0 { + "equalityProofContextStateAccount" + } else { + "equalityProofRecordAccount" + }; + map.insert( + label.to_string(), + json!(account_keys[account_indexes[offset] as usize].to_string()), + ); + offset += 1; + } + if offset < account_indexes.len() - 1 { + let label = if mint_data.ciphertext_validity_proof_instruction_offset == 0 { + "ciphertextValidityProofContextStateAccount" + } else { + "ciphertextValidityProofRecordAccount" + }; + map.insert( + label.to_string(), + json!(account_keys[account_indexes[offset] as usize].to_string()), + ); + offset += 1; + } + if offset < account_indexes.len() - 1 { + let label = if mint_data.range_proof_instruction_offset == 0 { + "rangeProofContextStateAccount" + } else { + "rangeProofRecordAccount" + }; + map.insert( + label.to_string(), + json!(account_keys[account_indexes[offset] as usize].to_string()), + ); + offset += 1; + } + + parse_signers( + map, + offset, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "confidentialMint".to_string(), + info: value, + }) + } + ConfidentialMintBurnInstruction::Burn => { + check_num_token_accounts(account_indexes, 3)?; + let burn_data: BurnInstructionData = *decode_instruction_data(instruction_data) + .map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let mut value = json!({ + "destination": account_keys[account_indexes[0] as usize].to_string(), + "mint": account_keys[account_indexes[1] as usize].to_string(), + "newDecryptableAvailableBalance": burn_data.new_decryptable_available_balance.to_string(), + "equalityProofInstructionOffset": burn_data.equality_proof_instruction_offset, + "ciphertextValidityProofInstructionOffset": burn_data.ciphertext_validity_proof_instruction_offset, + "rangeProofInstructionOffset": burn_data.range_proof_instruction_offset, + + }); + let mut offset = 2; + let map = value.as_object_mut().unwrap(); + if burn_data.equality_proof_instruction_offset != 0 + || burn_data.ciphertext_validity_proof_instruction_offset != 0 + || burn_data.range_proof_instruction_offset != 0 + { + map.insert( + "instructionsSysvar".to_string(), + json!(account_keys[account_indexes[offset] as usize].to_string()), + ); + offset += 1; + } + + // Assume that extra accounts are proof accounts and not multisig + // signers. This might be wrong, but it's the best possible option. + if offset < account_indexes.len() - 1 { + let label = if burn_data.equality_proof_instruction_offset == 0 { + "equalityProofContextStateAccount" + } else { + "equalityProofRecordAccount" + }; + map.insert( + label.to_string(), + json!(account_keys[account_indexes[offset] as usize].to_string()), + ); + offset += 1; + } + if offset < account_indexes.len() - 1 { + let label = if burn_data.ciphertext_validity_proof_instruction_offset == 0 { + "ciphertextValidityProofContextStateAccount" + } else { + "ciphertextValidityProofRecordAccount" + }; + map.insert( + label.to_string(), + json!(account_keys[account_indexes[offset] as usize].to_string()), + ); + offset += 1; + } + if offset < account_indexes.len() - 1 { + let label = if burn_data.range_proof_instruction_offset == 0 { + "rangeProofContextStateAccount" + } else { + "rangeProofRecordAccount" + }; + map.insert( + label.to_string(), + json!(account_keys[account_indexes[offset] as usize].to_string()), + ); + offset += 1; + } + + parse_signers( + map, + offset, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "confidentialBurn".to_string(), + info: value, + }) + } + } +} diff --git a/transaction-status/src/parse_token/extension/confidential_transfer.rs b/transaction-status/src/parse_token/extension/confidential_transfer.rs index 78b1c229c34638..4e15620cc6c187 100644 --- a/transaction-status/src/parse_token/extension/confidential_transfer.rs +++ b/transaction-status/src/parse_token/extension/confidential_transfer.rs @@ -1,9 +1,9 @@ use { super::*, - solana_account_decoder::parse_token_extension::convert_confidential_transfer_mint, spl_token_2022::{ - extension::confidential_transfer::{instruction::*, ConfidentialTransferMint}, + extension::confidential_transfer::instruction::*, instruction::{decode_instruction_data, decode_instruction_type}, + solana_zk_sdk::encryption::pod::elgamal::PodElGamalPubkey, }, }; @@ -17,44 +17,43 @@ pub(in crate::parse_token) fn parse_confidential_transfer_instruction( { ConfidentialTransferInstruction::InitializeMint => { check_num_token_accounts(account_indexes, 1)?; - let confidential_transfer_mint: ConfidentialTransferMint = + let initialize_mint_data: InitializeMintData = *decode_instruction_data(instruction_data).map_err(|_| { ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) })?; - let confidential_transfer_mint = - convert_confidential_transfer_mint(confidential_transfer_mint); let mut value = json!({ "mint": account_keys[account_indexes[0] as usize].to_string(), + "autoApproveNewAccounts": bool::from(initialize_mint_data.auto_approve_new_accounts), + "auditorElGamalPubkey": Option::::from(initialize_mint_data.auditor_elgamal_pubkey).map(|k| k.to_string()), }); let map = value.as_object_mut().unwrap(); - map.append(json!(confidential_transfer_mint).as_object_mut().unwrap()); + if let Some(authority) = Option::::from(initialize_mint_data.authority) { + map.insert("authority".to_string(), json!(authority.to_string())); + } 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(|_| { + check_num_token_accounts(account_indexes, 2)?; + let update_mint_data: UpdateMintData = *decode_instruction_data(instruction_data) + .map_err(|_| { ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) })?; - let confidential_transfer_mint = - convert_confidential_transfer_mint(confidential_transfer_mint); - let mut value = json!({ + let 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(), + "autoApproveNewAccounts": bool::from(update_mint_data.auto_approve_new_accounts), + "auditorElGamalPubkey": Option::::from(update_mint_data.auditor_elgamal_pubkey).map(|k| k.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)?; + check_num_token_accounts(account_indexes, 4)?; let configure_account_data: ConfigureAccountInstructionData = *decode_instruction_data(instruction_data).map_err(|_| { ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) @@ -67,12 +66,36 @@ pub(in crate::parse_token) fn parse_confidential_transfer_instruction( "mint": account_keys[account_indexes[1] as usize].to_string(), "decryptableZeroBalance": format!("{}", configure_account_data.decryptable_zero_balance), "maximumPendingBalanceCreditCounter": maximum_pending_balance_credit_counter, + "proofInstructionOffset": configure_account_data.proof_instruction_offset, }); let map = value.as_object_mut().unwrap(); + let offset = if configure_account_data.proof_instruction_offset == 0 { + map.insert( + "proofContextStateAccount".to_string(), + json!(account_keys[account_indexes[2] as usize].to_string()), + ); + 3 + } else { + map.insert( + "instructionsSysvar".to_string(), + json!(account_keys[account_indexes[2] as usize].to_string()), + ); + // Assume that the extra account is a proof account and not a multisig + // signer. This might be wrong, but it's the best possible option. + if account_indexes.len() > 4 { + map.insert( + "recordAccount".to_string(), + json!(account_keys[account_indexes[3] as usize].to_string()), + ); + 4 + } else { + 3 + } + }; parse_signers( map, - 2, + offset, account_keys, account_indexes, "owner", @@ -103,14 +126,36 @@ pub(in crate::parse_token) fn parse_confidential_transfer_instruction( 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(); + let offset = if proof_instruction_offset == 0 { + map.insert( + "proofContextStateAccount".to_string(), + json!(account_keys[account_indexes[1] as usize].to_string()), + ); + 2 + } else { + map.insert( + "instructionsSysvar".to_string(), + json!(account_keys[account_indexes[1] as usize].to_string()), + ); + if account_indexes.len() > 3 { + // Assume that the extra account is a proof account and not a multisig + // signer. This might be wrong, but it's the best possible option. + map.insert( + "recordAccount".to_string(), + json!(account_keys[account_indexes[2] as usize].to_string()), + ); + 3 + } else { + 2 + } + }; parse_signers( map, - 2, + offset, account_keys, account_indexes, "owner", @@ -157,22 +202,59 @@ pub(in crate::parse_token) fn parse_confidential_transfer_instruction( 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, + "equalityProofInstructionOffset": withdrawal_data.equality_proof_instruction_offset, + "rangeProofInstructionOffset": withdrawal_data.range_proof_instruction_offset, }); + let mut offset = 3; let map = value.as_object_mut().unwrap(); + if withdrawal_data.equality_proof_instruction_offset != 0 + || withdrawal_data.range_proof_instruction_offset != 0 + { + map.insert( + "instructionsSysvar".to_string(), + json!(account_keys[account_indexes[offset] as usize].to_string()), + ); + offset += 1; + } + + // Assume that extra accounts are proof accounts and not multisig + // signers. This might be wrong, but it's the best possible option. + if offset < account_indexes.len() - 1 { + let label = if withdrawal_data.equality_proof_instruction_offset == 0 { + "equalityProofContextStateAccount" + } else { + "equalityProofRecordAccount" + }; + map.insert( + label.to_string(), + json!(account_keys[account_indexes[offset] as usize].to_string()), + ); + offset += 1; + } + + if offset < account_indexes.len() - 1 { + let label = if withdrawal_data.range_proof_instruction_offset == 0 { + "rangeProofContextStateAccount" + } else { + "rangeProofRecordAccount" + }; + map.insert( + label.to_string(), + json!(account_keys[account_indexes[offset] as usize].to_string()), + ); + offset += 1; + } parse_signers( map, - 4, + offset, account_keys, account_indexes, "owner", @@ -184,25 +266,78 @@ pub(in crate::parse_token) fn parse_confidential_transfer_instruction( }) } ConfidentialTransferInstruction::Transfer => { - check_num_token_accounts(account_indexes, 5)?; + check_num_token_accounts(account_indexes, 4)?; 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(), "mint": account_keys[account_indexes[1] as usize].to_string(), "destination": 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, + "equalityProofInstructionOffset": transfer_data.equality_proof_instruction_offset, + "ciphertextValidityProofInstructionOffset": transfer_data.ciphertext_validity_proof_instruction_offset, + "rangeProofInstructionOffset": transfer_data.range_proof_instruction_offset, }); + let mut offset = 3; let map = value.as_object_mut().unwrap(); + if transfer_data.equality_proof_instruction_offset != 0 + || transfer_data.ciphertext_validity_proof_instruction_offset != 0 + || transfer_data.range_proof_instruction_offset != 0 + { + map.insert( + "instructionsSysvar".to_string(), + json!(account_keys[account_indexes[offset] as usize].to_string()), + ); + offset += 1; + } + + // Assume that extra accounts are proof accounts and not multisig + // signers. This might be wrong, but it's the best possible option. + if offset < account_indexes.len() - 1 { + let label = if transfer_data.equality_proof_instruction_offset == 0 { + "equalityProofContextStateAccount" + } else { + "equalityProofRecordAccount" + }; + map.insert( + label.to_string(), + json!(account_keys[account_indexes[offset] as usize].to_string()), + ); + offset += 1; + } + + if offset < account_indexes.len() - 1 { + let label = if transfer_data.ciphertext_validity_proof_instruction_offset == 0 { + "ciphertextValidityProofContextStateAccount" + } else { + "ciphertextValidityProofRecordAccount" + }; + map.insert( + label.to_string(), + json!(account_keys[account_indexes[offset] as usize].to_string()), + ); + offset += 1; + } + + if offset < account_indexes.len() - 1 { + let label = if transfer_data.range_proof_instruction_offset == 0 { + "rangeProofContextStateAccount" + } else { + "rangeProofRecordAccount" + }; + map.insert( + label.to_string(), + json!(account_keys[account_indexes[offset] as usize].to_string()), + ); + offset += 1; + } + parse_signers( map, - 4, + offset, account_keys, account_indexes, "owner", @@ -213,6 +348,123 @@ pub(in crate::parse_token) fn parse_confidential_transfer_instruction( info: value, }) } + ConfidentialTransferInstruction::TransferWithFee => { + check_num_token_accounts(account_indexes, 4)?; + let transfer_data: TransferWithFeeInstructionData = + *decode_instruction_data(instruction_data).map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let equality_proof_instruction_offset: i8 = + transfer_data.equality_proof_instruction_offset; + let transfer_amount_ciphertext_validity_proof_instruction_offset: i8 = + transfer_data.transfer_amount_ciphertext_validity_proof_instruction_offset; + let fee_sigma_proof_instruction_offset: i8 = + transfer_data.fee_sigma_proof_instruction_offset; + let fee_ciphertext_validity_proof_instruction_offset: i8 = + transfer_data.fee_ciphertext_validity_proof_instruction_offset; + let range_proof_instruction_offset: i8 = transfer_data.range_proof_instruction_offset; + let mut value = json!({ + "source": account_keys[account_indexes[0] as usize].to_string(), + "mint": account_keys[account_indexes[1] as usize].to_string(), + "destination": account_keys[account_indexes[2] as usize].to_string(), + "newSourceDecryptableAvailableBalance": format!("{}", transfer_data.new_source_decryptable_available_balance), + "equalityProofInstructionOffset": equality_proof_instruction_offset, + "transferAmountCiphertextValidityProofInstructionOffset": transfer_amount_ciphertext_validity_proof_instruction_offset, + "feeCiphertextValidityProofInstructionOffset": fee_ciphertext_validity_proof_instruction_offset, + "feeSigmaProofInstructionOffset": fee_sigma_proof_instruction_offset, + "rangeProofInstructionOffset": range_proof_instruction_offset, + }); + + let mut offset = 3; + let map = value.as_object_mut().unwrap(); + if equality_proof_instruction_offset != 0 + || transfer_amount_ciphertext_validity_proof_instruction_offset != 0 + || fee_ciphertext_validity_proof_instruction_offset != 0 + || fee_sigma_proof_instruction_offset != 0 + || range_proof_instruction_offset != 0 + { + map.insert( + "instructionsSysvar".to_string(), + json!(account_keys[account_indexes[offset] as usize].to_string()), + ); + offset += 1; + } + + // Assume that extra accounts are proof accounts and not multisig + // signers. This might be wrong, but it's the best possible option. + if offset < account_indexes.len() - 1 { + let label = if equality_proof_instruction_offset == 0 { + "equalityProofContextStateAccount" + } else { + "equalityProofRecordAccount" + }; + map.insert( + label.to_string(), + json!(account_keys[account_indexes[offset] as usize].to_string()), + ); + offset += 1; + } + if offset < account_indexes.len() - 1 { + let label = if transfer_amount_ciphertext_validity_proof_instruction_offset == 0 { + "transferAmountCiphertextValidityProofContextStateAccount" + } else { + "transferAmountCiphertextValidityProofRecordAccount" + }; + map.insert( + label.to_string(), + json!(account_keys[account_indexes[offset] as usize].to_string()), + ); + offset += 1; + } + if offset < account_indexes.len() - 1 { + let label = if fee_ciphertext_validity_proof_instruction_offset == 0 { + "feeCiphertextValidityProofContextStateAccount" + } else { + "feeCiphertextValidityProofRecordAccount" + }; + map.insert( + label.to_string(), + json!(account_keys[account_indexes[offset] as usize].to_string()), + ); + offset += 1; + } + if offset < account_indexes.len() - 1 { + let label = if fee_sigma_proof_instruction_offset == 0 { + "feeSigmaProofContextStateAccount" + } else { + "feeSigmaProofRecordAccount" + }; + map.insert( + label.to_string(), + json!(account_keys[account_indexes[offset] as usize].to_string()), + ); + offset += 1; + } + if offset < account_indexes.len() - 1 { + let label = if range_proof_instruction_offset == 0 { + "rangeProofContextStateAccount" + } else { + "rangeProofRecordAccount" + }; + map.insert( + label.to_string(), + json!(account_keys[account_indexes[offset] as usize].to_string()), + ); + offset += 1; + } + parse_signers( + map, + offset, + 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 = @@ -322,37 +574,15 @@ pub(in crate::parse_token) fn parse_confidential_transfer_instruction( info: value, }) } - ConfidentialTransferInstruction::TransferWithSplitProofs => { - check_num_token_accounts(account_indexes, 7)?; - let transfer_data: TransferWithSplitProofsInstructionData = - *decode_instruction_data(instruction_data).map_err(|_| { - ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) - })?; - let mut value = json!({ - "source": account_keys[account_indexes[0] as usize].to_string(), + ConfidentialTransferInstruction::ConfigureAccountWithRegistry => { + check_num_token_accounts(account_indexes, 3)?; + let value = json!({ + "account": account_keys[account_indexes[0] as usize].to_string(), "mint": account_keys[account_indexes[1] as usize].to_string(), - "destination": account_keys[account_indexes[2] as usize].to_string(), - "ciphertextCommitmentEqualityContext": account_keys[account_indexes[3] as usize].to_string(), - "batchedGroupedCiphertext2HandlesValidityContext": account_keys[account_indexes[4] as usize].to_string(), - "batchedRangeProofContext": account_keys[account_indexes[5] as usize].to_string(), - "owner": account_keys[account_indexes[6] as usize].to_string(), - "newSourceDecryptableAvailableBalance": format!("{}", transfer_data.new_source_decryptable_available_balance), - "noOpOnUninitializedSplitContextState": bool::from(transfer_data.no_op_on_uninitialized_split_context_state), - "closeSplitContextStateOnExecution": bool::from(transfer_data.close_split_context_state_on_execution), + "registry": account_keys[account_indexes[2] as usize].to_string(), }); - let map = value.as_object_mut().unwrap(); - if transfer_data.close_split_context_state_on_execution.into() { - map.insert( - "lamportDestination".to_string(), - json!(account_keys[account_indexes[7] as usize].to_string()), - ); - map.insert( - "contextStateOwner".to_string(), - json!(account_keys[account_indexes[8] as usize].to_string()), - ); - } Ok(ParsedInstructionEnum { - instruction_type: "confidentialTransferWithSplitProofs".to_string(), + instruction_type: "configureConfidentialAccountWithRegistry".to_string(), info: value, }) } diff --git a/transaction-status/src/parse_token/extension/confidential_transfer_fee.rs b/transaction-status/src/parse_token/extension/confidential_transfer_fee.rs index 9a6856835c864b..8370038d508a40 100644 --- a/transaction-status/src/parse_token/extension/confidential_transfer_fee.rs +++ b/transaction-status/src/parse_token/extension/confidential_transfer_fee.rs @@ -1,9 +1,9 @@ use { super::*, - solana_account_decoder::parse_token_extension::convert_confidential_transfer_fee_config, spl_token_2022::{ - extension::confidential_transfer_fee::{instruction::*, ConfidentialTransferFeeConfig}, + extension::confidential_transfer_fee::instruction::*, instruction::{decode_instruction_data, decode_instruction_type}, + solana_zk_sdk::encryption::pod::elgamal::PodElGamalPubkey, }, }; @@ -17,17 +17,18 @@ pub(in crate::parse_token) fn parse_confidential_transfer_fee_instruction( { ConfidentialTransferFeeInstruction::InitializeConfidentialTransferFeeConfig => { check_num_token_accounts(account_indexes, 1)?; - let confidential_transfer_mint: ConfidentialTransferFeeConfig = + let transfer_fee_config: InitializeConfidentialTransferFeeConfigData = *decode_instruction_data(instruction_data).map_err(|_| { ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) })?; - let confidential_transfer_mint = - convert_confidential_transfer_fee_config(confidential_transfer_mint); let mut value = json!({ "mint": account_keys[account_indexes[0] as usize].to_string(), + "withdrawWithheldAuthorityElGamalPubkey": Option::::from(transfer_fee_config.withdraw_withheld_authority_elgamal_pubkey).map(|k| k.to_string()), }); let map = value.as_object_mut().unwrap(); - map.append(json!(confidential_transfer_mint).as_object_mut().unwrap()); + if let Some(authority) = Option::::from(transfer_fee_config.authority) { + map.insert("authority".to_string(), json!(authority.to_string())); + } Ok(ParsedInstructionEnum { instruction_type: "initializeConfidentialTransferFeeConfig".to_string(), info: value, @@ -43,14 +44,36 @@ pub(in crate::parse_token) fn parse_confidential_transfer_fee_instruction( 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, - + "newDecryptableAvailableBalance": format!("{}", withdraw_withheld_data.new_decryptable_available_balance), }); let map = value.as_object_mut().unwrap(); + let offset = if proof_instruction_offset == 0 { + map.insert( + "proofContextStateAccount".to_string(), + json!(account_keys[account_indexes[2] as usize].to_string()), + ); + 3 + } else { + map.insert( + "instructionsSysvar".to_string(), + json!(account_keys[account_indexes[2] as usize].to_string()), + ); + // Assume that the extra account is a proof account and not a multisig + // signer. This might be wrong, but it's the best possible option. + if account_indexes.len() > 4 { + map.insert( + "recordAccount".to_string(), + json!(account_keys[account_indexes[3] as usize].to_string()), + ); + 4 + } else { + 3 + } + }; parse_signers( map, - 3, + offset, account_keys, account_indexes, "withdrawWithheldAuthority", @@ -72,21 +95,44 @@ pub(in crate::parse_token) fn parse_confidential_transfer_fee_instruction( 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, + "newDecryptableAvailableBalance": format!("{}", withdraw_withheld_data.new_decryptable_available_balance), }); 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); + let offset = if proof_instruction_offset == 0 { + map.insert( + "proofContextStateAccount".to_string(), + json!(account_keys[account_indexes[2] as usize].to_string()), + ); + 3 + } else { + map.insert( + "instructionsSysvar".to_string(), + json!(account_keys[account_indexes[2] as usize].to_string()), + ); + if first_source_account_index > 4 { + // Assume that the extra account is a proof account and not a multisig + // signer. This might be wrong, but it's the best possible option. + map.insert( + "proofAccount".to_string(), + json!(account_keys[account_indexes[3] as usize].to_string()), + ); + 4 + } else { + 3 + } + }; + let mut source_accounts: Vec = vec![]; 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, + offset, account_keys, &account_indexes[..first_source_account_index], "withdrawWithheldAuthority", diff --git a/transaction-status/src/parse_token/extension/mod.rs b/transaction-status/src/parse_token/extension/mod.rs index e34ec6bd11e8af..68070ea92e8a7a 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_mint_burn; pub(super) mod confidential_transfer; pub(super) mod confidential_transfer_fee; pub(super) mod cpi_guard; diff --git a/transaction-status/src/parse_token/extension/token_group.rs b/transaction-status/src/parse_token/extension/token_group.rs index 04479734049f22..c62a3c20d15163 100644 --- a/transaction-status/src/parse_token/extension/token_group.rs +++ b/transaction-status/src/parse_token/extension/token_group.rs @@ -19,7 +19,7 @@ pub(in crate::parse_token) fn parse_token_group_instruction( } = group; let value = json!({ "group": account_keys[account_indexes[0] as usize].to_string(), - "maxSize": u32::from(*max_size), + "maxSize": u64::from(*max_size), "mint": account_keys[account_indexes[1] as usize].to_string(), "mintAuthority": account_keys[account_indexes[2] as usize].to_string(), "updateAuthority": Option::::from(*update_authority).map(|v| v.to_string()) @@ -34,7 +34,7 @@ pub(in crate::parse_token) fn parse_token_group_instruction( let UpdateGroupMaxSize { max_size } = update; let value = json!({ "group": account_keys[account_indexes[0] as usize].to_string(), - "maxSize": u32::from(*max_size), + "maxSize": u64::from(*max_size), "updateAuthority": account_keys[account_indexes[1] as usize].to_string(), }); Ok(ParsedInstructionEnum {