spl: Bump token-2022 and friends (#33453)

* token: Update to 4.0.0

* token-2022: Bump and support new account and instruction types

* Update token-2022 in fetch_spl / program-test

* Fixup downstream uses

* Mint and destination were flipped in 0.9.0

* Don't use `convert_pubkey`

* Bump spl dependencies to versions which avoid recompilations
This commit is contained in:
Jon Cinque 2023-09-29 19:12:06 +02:00 committed by GitHub
parent 0e9e91c65e
commit de38b05ad1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 1255 additions and 200 deletions

224
Cargo.lock generated
View File

@ -709,7 +709,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b"
dependencies = [
"borsh-derive 0.10.3",
"hashbrown 0.13.2",
"hashbrown 0.12.3",
]
[[package]]
@ -3299,6 +3299,17 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "num-derive"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e6a0fd4f737c707bd9086cc16c925f294943eb62eb71499e9fd4cf71f8b9f4e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.37",
]
[[package]]
name = "num-integer"
version = "0.1.44"
@ -3370,6 +3381,15 @@ dependencies = [
"num_enum_derive 0.6.1",
]
[[package]]
name = "num_enum"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70bf6736f74634d299d00086f02986875b3c2d924781a6a2cb6c201e73da0ceb"
dependencies = [
"num_enum_derive 0.7.0",
]
[[package]]
name = "num_enum_derive"
version = "0.5.11"
@ -3394,6 +3414,18 @@ dependencies = [
"syn 2.0.37",
]
[[package]]
name = "num_enum_derive"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56ea360eafe1022f7cc56cd7b869ed57330fb2453d0c7831d99b74c65d2f5597"
dependencies = [
"proc-macro-crate 1.1.0",
"proc-macro2",
"quote",
"syn 2.0.37",
]
[[package]]
name = "num_threads"
version = "0.1.3"
@ -5077,8 +5109,10 @@ dependencies = [
"serde_json",
"solana-config-program",
"solana-sdk",
"spl-pod",
"spl-token",
"spl-token-2022",
"spl-token-metadata-interface",
"thiserror",
"zstd",
]
@ -5155,7 +5189,7 @@ dependencies = [
"memmap2",
"memoffset 0.9.0",
"modular-bitfield",
"num-derive",
"num-derive 0.3.3",
"num-traits",
"num_cpus",
"num_enum 0.6.1",
@ -5198,7 +5232,7 @@ dependencies = [
"bincode",
"bytemuck",
"log",
"num-derive",
"num-derive 0.3.3",
"num-traits",
"rustc_version 0.4.0",
"serde",
@ -6160,6 +6194,7 @@ dependencies = [
"solana-transaction-status",
"solana-vote",
"solana-vote-program",
"spl-pod",
"spl-token",
"spl-token-2022",
"static_assertions",
@ -6483,7 +6518,7 @@ dependencies = [
"log",
"memoffset 0.9.0",
"num-bigint 0.4.4",
"num-derive",
"num-derive 0.3.3",
"num-traits",
"parking_lot 0.12.1",
"rand 0.8.5",
@ -6519,7 +6554,7 @@ dependencies = [
"libc",
"libsecp256k1",
"log",
"num-derive",
"num-derive 0.3.3",
"num-traits",
"percentage",
"rand 0.8.5",
@ -6632,7 +6667,7 @@ dependencies = [
"dialoguer",
"hidapi",
"log",
"num-derive",
"num-derive 0.3.3",
"num-traits",
"parking_lot 0.12.1",
"qstring",
@ -6691,6 +6726,7 @@ dependencies = [
"solana-version",
"solana-vote",
"solana-vote-program",
"spl-pod",
"spl-token",
"spl-token-2022",
"stream-cancel",
@ -6824,7 +6860,7 @@ dependencies = [
"memmap2",
"memoffset 0.9.0",
"modular-bitfield",
"num-derive",
"num-derive 0.3.3",
"num-traits",
"num_cpus",
"num_enum 0.6.1",
@ -6904,7 +6940,7 @@ dependencies = [
"libsecp256k1",
"log",
"memmap2",
"num-derive",
"num-derive 0.3.3",
"num-traits",
"num_enum 0.6.1",
"pbkdf2 0.11.0",
@ -7232,7 +7268,7 @@ dependencies = [
"Inflector",
"base64 0.21.4",
"bincode",
"borsh 0.9.3",
"borsh 0.10.3",
"bs58",
"lazy_static",
"log",
@ -7410,7 +7446,7 @@ dependencies = [
"assert_matches",
"bincode",
"log",
"num-derive",
"num-derive 0.3.3",
"num-traits",
"rustc_version 0.4.0",
"serde",
@ -7471,7 +7507,7 @@ dependencies = [
"bytemuck",
"criterion",
"curve25519-dalek",
"num-derive",
"num-derive 0.3.3",
"num-traits",
"solana-program-runtime",
"solana-sdk",
@ -7504,7 +7540,7 @@ dependencies = [
"itertools",
"lazy_static",
"merlin",
"num-derive",
"num-derive 0.3.3",
"num-traits",
"rand 0.7.3",
"serde",
@ -7562,13 +7598,13 @@ dependencies = [
[[package]]
name = "spl-associated-token-account"
version = "1.1.3"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978dba3bcbe88d0c2c58366c254d9ea41c5f73357e72fc0bdee4d6b5fc99c8f4"
checksum = "385e31c29981488f2820b2022d8e731aae3b02e6e18e2fd854e4c9a94dc44fc3"
dependencies = [
"assert_matches",
"borsh 0.9.3",
"num-derive",
"borsh 0.10.3",
"num-derive 0.4.0",
"num-traits",
"solana-program",
"spl-token",
@ -7576,6 +7612,41 @@ dependencies = [
"thiserror",
]
[[package]]
name = "spl-discriminator"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cce5d563b58ef1bb2cdbbfe0dfb9ffdc24903b10ae6a4df2d8f425ece375033f"
dependencies = [
"bytemuck",
"solana-program",
"spl-discriminator-derive",
]
[[package]]
name = "spl-discriminator-derive"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fadbefec4f3c678215ca72bd71862697bb06b41fd77c0088902dd3203354387b"
dependencies = [
"quote",
"spl-discriminator-syn",
"syn 2.0.37",
]
[[package]]
name = "spl-discriminator-syn"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e5f2044ca42c8938d54d1255ce599c79a1ffd86b677dfab695caa20f9ffc3f2"
dependencies = [
"proc-macro2",
"quote",
"sha2 0.10.7",
"syn 2.0.37",
"thiserror",
]
[[package]]
name = "spl-instruction-padding"
version = "0.1.0"
@ -7588,46 +7659,145 @@ dependencies = [
[[package]]
name = "spl-memo"
version = "3.0.1"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd0dc6f70db6bacea7ff25870b016a65ba1d1b6013536f08e4fd79a8f9005325"
checksum = "f0f180b03318c3dbab3ef4e1e4d46d5211ae3c780940dd0a28695aba4b59a75a"
dependencies = [
"solana-program",
]
[[package]]
name = "spl-token"
version = "3.5.0"
name = "spl-pod"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e85e168a785e82564160dcb87b2a8e04cee9bfd1f4d488c729d53d6a4bd300d"
checksum = "2881dddfca792737c0706fa0175345ab282b1b0879c7d877bad129645737c079"
dependencies = [
"borsh 0.10.3",
"bytemuck",
"solana-program",
"solana-zk-token-sdk",
"spl-program-error",
]
[[package]]
name = "spl-program-error"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "249e0318493b6bcf27ae9902600566c689b7dfba9f1bdff5893e92253374e78c"
dependencies = [
"num-derive 0.4.0",
"num-traits",
"solana-program",
"spl-program-error-derive",
"thiserror",
]
[[package]]
name = "spl-program-error-derive"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5269c8e868da17b6552ef35a51355a017bd8e0eae269c201fef830d35fa52c"
dependencies = [
"proc-macro2",
"quote",
"sha2 0.10.7",
"syn 2.0.37",
]
[[package]]
name = "spl-tlv-account-resolution"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "062e148d3eab7b165582757453632ffeef490c02c86a48bfdb4988f63eefb3b9"
dependencies = [
"bytemuck",
"solana-program",
"spl-discriminator",
"spl-pod",
"spl-program-error",
"spl-type-length-value",
]
[[package]]
name = "spl-token"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08459ba1b8f7c1020b4582c4edf0f5c7511a5e099a7a97570c9698d4f2337060"
dependencies = [
"arrayref",
"bytemuck",
"num-derive",
"num-derive 0.3.3",
"num-traits",
"num_enum 0.5.11",
"num_enum 0.6.1",
"solana-program",
"thiserror",
]
[[package]]
name = "spl-token-2022"
version = "0.6.1"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0043b590232c400bad5ee9eb983ced003d15163c4c5d56b090ac6d9a57457b47"
checksum = "e4abf34a65ba420584a0c35f3903f8d727d1f13ababbdc3f714c6b065a686e86"
dependencies = [
"arrayref",
"bytemuck",
"num-derive",
"num-derive 0.4.0",
"num-traits",
"num_enum 0.5.11",
"num_enum 0.7.0",
"solana-program",
"solana-zk-token-sdk",
"spl-memo",
"spl-pod",
"spl-token",
"spl-token-metadata-interface",
"spl-transfer-hook-interface",
"spl-type-length-value",
"thiserror",
]
[[package]]
name = "spl-token-metadata-interface"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c16ce3ba6979645fb7627aa1e435576172dd63088dc7848cb09aa331fa1fe4f"
dependencies = [
"borsh 0.10.3",
"solana-program",
"spl-discriminator",
"spl-pod",
"spl-program-error",
"spl-type-length-value",
]
[[package]]
name = "spl-transfer-hook-interface"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "051d31803f873cabe71aec3c1b849f35248beae5d19a347d93a5c9cccc5d5a9b"
dependencies = [
"arrayref",
"bytemuck",
"solana-program",
"spl-discriminator",
"spl-pod",
"spl-program-error",
"spl-tlv-account-resolution",
"spl-type-length-value",
]
[[package]]
name = "spl-type-length-value"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a468e6f6371f9c69aae760186ea9f1a01c2908351b06a5e0026d21cfc4d7ecac"
dependencies = [
"bytemuck",
"solana-program",
"spl-discriminator",
"spl-pod",
"spl-program-error",
]
[[package]]
name = "static_assertions"
version = "1.1.0"

View File

@ -373,11 +373,13 @@ solana-vote-program = { path = "programs/vote", version = "=1.17.0" }
solana-zk-keygen = { path = "zk-keygen", version = "=1.17.0" }
solana-zk-token-proof-program = { path = "programs/zk-token-proof", version = "=1.17.0" }
solana-zk-token-sdk = { path = "zk-token-sdk", version = "=1.17.0" }
spl-associated-token-account = "=1.1.3"
spl-associated-token-account = "=2.2.0"
spl-instruction-padding = "0.1"
spl-memo = "=3.0.1"
spl-token = "=3.5.0"
spl-token-2022 = "=0.6.1"
spl-memo = "=4.0.0"
spl-pod = "=0.1.0"
spl-token = "=4.0.0"
spl-token-2022 = "=0.9.0"
spl-token-metadata-interface = "=0.2.0"
static_assertions = "1.1.0"
stream-cancel = "0.8.1"
strum = "0.24"
@ -423,8 +425,10 @@ crossbeam-epoch = { git = "https://github.com/solana-labs/crossbeam", rev = "fd2
# * spl-associated-token-account
# * spl-instruction-padding
# * spl-memo
# * spl-pod
# * spl-token
# * spl-token-2022
# * spl-token-metadata-interface
#
# They, in turn, depend on a number of crates that we also include directly using `path`
# specifications. For example, `spl-token` depends on `solana-program`. And we explicitly specify

View File

@ -23,11 +23,13 @@ solana-config-program = { workspace = true }
solana-sdk = { workspace = true }
spl-token = { workspace = true, features = ["no-entrypoint"] }
spl-token-2022 = { workspace = true, features = ["no-entrypoint"] }
spl-token-metadata-interface = { workspace = true }
thiserror = { workspace = true }
zstd = { workspace = true }
[dev-dependencies]
assert_matches = { workspace = true }
spl-pod = { workspace = true }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -290,12 +290,10 @@ mod test {
use {
super::*,
crate::parse_token_extension::{UiMemoTransfer, UiMintCloseAuthority},
spl_token_2022::{
extension::{
immutable_owner::ImmutableOwner, memo_transfer::MemoTransfer,
mint_close_authority::MintCloseAuthority, ExtensionType, StateWithExtensionsMut,
},
pod::OptionalNonZeroPubkey,
spl_pod::optional_keys::OptionalNonZeroPubkey,
spl_token_2022::extension::{
immutable_owner::ImmutableOwner, memo_transfer::MemoTransfer,
mint_close_authority::MintCloseAuthority, ExtensionType, StateWithExtensionsMut,
},
};
@ -506,10 +504,11 @@ mod test {
delegate: COption::None,
delegated_amount: 0,
};
let account_size = ExtensionType::get_account_len::<Account>(&[
let account_size = ExtensionType::try_calculate_account_len::<Account>(&[
ExtensionType::ImmutableOwner,
ExtensionType::MemoTransfer,
]);
])
.unwrap();
let mut account_data = vec![0; account_size];
let mut account_state =
StateWithExtensionsMut::<Account>::unpack_uninitialized(&mut account_data).unwrap();
@ -586,7 +585,8 @@ mod test {
fn test_parse_token_mint_with_extensions() {
let owner_pubkey = SplTokenPubkey::new_from_array([3; 32]);
let mint_size =
ExtensionType::get_account_len::<Mint>(&[ExtensionType::MintCloseAuthority]);
ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::MintCloseAuthority])
.unwrap();
let mint_base = Mint {
mint_authority: COption::Some(owner_pubkey),
supply: 42,

View File

@ -6,6 +6,7 @@ use {
solana_program::pubkey::Pubkey,
solana_zk_token_sdk::zk_token_elgamal::pod::ElGamalPubkey,
},
spl_token_metadata_interface::state::TokenMetadata,
};
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
@ -24,15 +25,21 @@ pub enum UiExtension {
InterestBearingConfig(UiInterestBearingConfig),
CpiGuard(UiCpiGuard),
PermanentDelegate(UiPermanentDelegate),
UnparseableExtension,
NonTransferableAccount,
ConfidentialTransferFeeConfig(UiConfidentialTransferFeeConfig),
ConfidentialTransferFeeAmount(UiConfidentialTransferFeeAmount),
TransferHook(UiTransferHook),
TransferHookAccount(UiTransferHookAccount),
MetadataPointer(UiMetadataPointer),
TokenMetadata(UiTokenMetadata),
UnparseableExtension,
}
pub fn parse_extension<S: BaseState>(
extension_type: &ExtensionType,
account: &StateWithExtensions<S>,
) -> UiExtension {
match &extension_type {
match extension_type {
ExtensionType::Uninitialized => UiExtension::Uninitialized,
ExtensionType::TransferFeeConfig => account
.get_extension::<extension::transfer_fee::TransferFeeConfig>()
@ -50,10 +57,18 @@ pub fn parse_extension<S: BaseState>(
.get_extension::<extension::confidential_transfer::ConfidentialTransferMint>()
.map(|&extension| UiExtension::ConfidentialTransferMint(extension.into()))
.unwrap_or(UiExtension::UnparseableExtension),
ExtensionType::ConfidentialTransferFeeConfig => account
.get_extension::<extension::confidential_transfer_fee::ConfidentialTransferFeeConfig>()
.map(|&extension| UiExtension::ConfidentialTransferFeeConfig(extension.into()))
.unwrap_or(UiExtension::UnparseableExtension),
ExtensionType::ConfidentialTransferAccount => account
.get_extension::<extension::confidential_transfer::ConfidentialTransferAccount>()
.map(|&extension| UiExtension::ConfidentialTransferAccount(extension.into()))
.unwrap_or(UiExtension::UnparseableExtension),
ExtensionType::ConfidentialTransferFeeAmount => account
.get_extension::<extension::confidential_transfer_fee::ConfidentialTransferFeeAmount>()
.map(|&extension| UiExtension::ConfidentialTransferFeeAmount(extension.into()))
.unwrap_or(UiExtension::UnparseableExtension),
ExtensionType::DefaultAccountState => account
.get_extension::<extension::default_account_state::DefaultAccountState>()
.map(|&extension| UiExtension::DefaultAccountState(extension.into()))
@ -77,6 +92,22 @@ pub fn parse_extension<S: BaseState>(
.map(|&extension| UiExtension::PermanentDelegate(extension.into()))
.unwrap_or(UiExtension::UnparseableExtension),
ExtensionType::NonTransferableAccount => UiExtension::NonTransferableAccount,
ExtensionType::MetadataPointer => account
.get_extension::<extension::metadata_pointer::MetadataPointer>()
.map(|&extension| UiExtension::MetadataPointer(extension.into()))
.unwrap_or(UiExtension::UnparseableExtension),
ExtensionType::TokenMetadata => account
.get_variable_len_extension::<TokenMetadata>()
.map(|extension| UiExtension::TokenMetadata(extension.into()))
.unwrap_or(UiExtension::UnparseableExtension),
ExtensionType::TransferHook => account
.get_extension::<extension::transfer_hook::TransferHook>()
.map(|&extension| UiExtension::TransferHook(extension.into()))
.unwrap_or(UiExtension::UnparseableExtension),
ExtensionType::TransferHookAccount => account
.get_extension::<extension::transfer_hook::TransferHookAccount>()
.map(|&extension| UiExtension::TransferHookAccount(extension.into()))
.unwrap_or(UiExtension::UnparseableExtension),
}
}
@ -251,9 +282,7 @@ impl From<extension::permanent_delegate::PermanentDelegate> for UiPermanentDeleg
pub struct UiConfidentialTransferMint {
pub authority: Option<String>,
pub auto_approve_new_accounts: bool,
pub auditor_encryption_pubkey: Option<String>,
pub withdraw_withheld_authority_encryption_pubkey: Option<String>,
pub withheld_amount: String,
pub auditor_elgamal_pubkey: Option<String>,
}
impl From<extension::confidential_transfer::ConfidentialTransferMint>
@ -263,19 +292,44 @@ impl From<extension::confidential_transfer::ConfidentialTransferMint>
confidential_transfer_mint: extension::confidential_transfer::ConfidentialTransferMint,
) -> Self {
let authority: Option<Pubkey> = confidential_transfer_mint.authority.into();
let auditor_encryption_pubkey: Option<ElGamalPubkey> =
confidential_transfer_mint.auditor_encryption_pubkey.into();
let withdraw_withheld_authority_encryption_pubkey: Option<ElGamalPubkey> =
confidential_transfer_mint
.withdraw_withheld_authority_encryption_pubkey
.into();
let auditor_elgamal_pubkey: Option<ElGamalPubkey> =
confidential_transfer_mint.auditor_elgamal_pubkey.into();
Self {
authority: authority.map(|pubkey| pubkey.to_string()),
auto_approve_new_accounts: confidential_transfer_mint.auto_approve_new_accounts.into(),
auditor_encryption_pubkey: auditor_encryption_pubkey.map(|pubkey| pubkey.to_string()),
withdraw_withheld_authority_encryption_pubkey:
withdraw_withheld_authority_encryption_pubkey.map(|pubkey| pubkey.to_string()),
withheld_amount: format!("{}", confidential_transfer_mint.withheld_amount),
auditor_elgamal_pubkey: auditor_elgamal_pubkey.map(|pubkey| pubkey.to_string()),
}
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct UiConfidentialTransferFeeConfig {
pub authority: Option<String>,
pub withdraw_withheld_authority_elgamal_pubkey: Option<String>,
pub harvest_to_mint_enabled: bool,
pub withheld_amount: String,
}
impl From<extension::confidential_transfer_fee::ConfidentialTransferFeeConfig>
for UiConfidentialTransferFeeConfig
{
fn from(
confidential_transfer_fee_config: extension::confidential_transfer_fee::ConfidentialTransferFeeConfig,
) -> Self {
let authority: Option<Pubkey> = confidential_transfer_fee_config.authority.into();
let withdraw_withheld_authority_elgamal_pubkey: Option<ElGamalPubkey> =
confidential_transfer_fee_config
.withdraw_withheld_authority_elgamal_pubkey
.into();
Self {
authority: authority.map(|pubkey| pubkey.to_string()),
withdraw_withheld_authority_elgamal_pubkey: withdraw_withheld_authority_elgamal_pubkey
.map(|pubkey| pubkey.to_string()),
harvest_to_mint_enabled: confidential_transfer_fee_config
.harvest_to_mint_enabled
.into(),
withheld_amount: format!("{}", confidential_transfer_fee_config.withheld_amount),
}
}
}
@ -284,7 +338,7 @@ impl From<extension::confidential_transfer::ConfidentialTransferMint>
#[serde(rename_all = "camelCase")]
pub struct UiConfidentialTransferAccount {
pub approved: bool,
pub encryption_pubkey: String,
pub elgamal_pubkey: String,
pub pending_balance_lo: String,
pub pending_balance_hi: String,
pub available_balance: String,
@ -295,7 +349,6 @@ pub struct UiConfidentialTransferAccount {
pub maximum_pending_balance_credit_counter: u64,
pub expected_pending_balance_credit_counter: u64,
pub actual_pending_balance_credit_counter: u64,
pub withheld_amount: String,
}
impl From<extension::confidential_transfer::ConfidentialTransferAccount>
@ -306,7 +359,7 @@ impl From<extension::confidential_transfer::ConfidentialTransferAccount>
) -> Self {
Self {
approved: confidential_transfer_account.approved.into(),
encryption_pubkey: format!("{}", confidential_transfer_account.encryption_pubkey),
elgamal_pubkey: format!("{}", confidential_transfer_account.elgamal_pubkey),
pending_balance_lo: format!("{}", confidential_transfer_account.pending_balance_lo),
pending_balance_hi: format!("{}", confidential_transfer_account.pending_balance_hi),
available_balance: format!("{}", confidential_transfer_account.available_balance),
@ -332,7 +385,99 @@ impl From<extension::confidential_transfer::ConfidentialTransferAccount>
actual_pending_balance_credit_counter: confidential_transfer_account
.actual_pending_balance_credit_counter
.into(),
withheld_amount: format!("{}", confidential_transfer_account.withheld_amount),
}
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct UiConfidentialTransferFeeAmount {
pub withheld_amount: String,
}
impl From<extension::confidential_transfer_fee::ConfidentialTransferFeeAmount>
for UiConfidentialTransferFeeAmount
{
fn from(
confidential_transfer_fee_amount: extension::confidential_transfer_fee::ConfidentialTransferFeeAmount,
) -> Self {
Self {
withheld_amount: format!("{}", confidential_transfer_fee_amount.withheld_amount),
}
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct UiMetadataPointer {
pub authority: Option<String>,
pub metadata_address: Option<String>,
}
impl From<extension::metadata_pointer::MetadataPointer> for UiMetadataPointer {
fn from(metadata_pointer: extension::metadata_pointer::MetadataPointer) -> Self {
let authority: Option<Pubkey> = metadata_pointer.authority.into();
let metadata_address: Option<Pubkey> = metadata_pointer.metadata_address.into();
Self {
authority: authority.map(|pubkey| pubkey.to_string()),
metadata_address: metadata_address.map(|pubkey| pubkey.to_string()),
}
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct UiTokenMetadata {
pub update_authority: Option<String>,
pub mint: String,
pub name: String,
pub symbol: String,
pub uri: String,
pub additional_metadata: Vec<(String, String)>,
}
impl From<TokenMetadata> for UiTokenMetadata {
fn from(token_metadata: TokenMetadata) -> Self {
let update_authority: Option<Pubkey> = token_metadata.update_authority.into();
Self {
update_authority: update_authority.map(|pubkey| pubkey.to_string()),
mint: token_metadata.mint.to_string(),
name: token_metadata.name,
symbol: token_metadata.symbol,
uri: token_metadata.uri,
additional_metadata: token_metadata.additional_metadata,
}
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct UiTransferHook {
pub authority: Option<String>,
pub program_id: Option<String>,
}
impl From<extension::transfer_hook::TransferHook> for UiTransferHook {
fn from(transfer_hook: extension::transfer_hook::TransferHook) -> Self {
let authority: Option<Pubkey> = transfer_hook.authority.into();
let program_id: Option<Pubkey> = transfer_hook.program_id.into();
Self {
authority: authority.map(|pubkey| pubkey.to_string()),
program_id: program_id.map(|pubkey| pubkey.to_string()),
}
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct UiTransferHookAccount {
pub transferring: bool,
}
impl From<extension::transfer_hook::TransferHookAccount> for UiTransferHookAccount {
fn from(transfer_hook: extension::transfer_hook::TransferHookAccount) -> Self {
Self {
transferring: transfer_hook.transferring.into(),
}
}
}

View File

@ -45,7 +45,7 @@ fetch_program() {
}
fetch_program token 3.5.0 TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA BPFLoader2111111111111111111111111111111111
fetch_program token-2022 0.6.0 TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb BPFLoaderUpgradeab1e11111111111111111111111
fetch_program token-2022 0.9.0 TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb BPFLoaderUpgradeab1e11111111111111111111111
fetch_program memo 1.0.0 Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo BPFLoader1111111111111111111111111111111111
fetch_program memo 3.0.0 MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr BPFLoader2111111111111111111111111111111111
fetch_program associated-token-account 1.1.2 ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL BPFLoader2111111111111111111111111111111111

View File

@ -78,6 +78,7 @@ features = ["lz4"]
bs58 = { workspace = true }
solana-account-decoder = { workspace = true }
solana-logger = { workspace = true }
spl-pod = { workspace = true }
test-case = { workspace = true }
[build-dependencies]

View File

@ -121,12 +121,12 @@ mod test {
use {
super::*,
solana_sdk::{account::Account, genesis_config::create_genesis_config},
spl_pod::optional_keys::OptionalNonZeroPubkey,
spl_token_2022::{
extension::{
immutable_owner::ImmutableOwner, memo_transfer::MemoTransfer,
mint_close_authority::MintCloseAuthority, ExtensionType, StateWithExtensionsMut,
},
pod::OptionalNonZeroPubkey,
solana_program::{program_option::COption, program_pack::Pack},
},
std::collections::BTreeMap,
@ -291,7 +291,8 @@ mod test {
let mint_authority = Pubkey::new_unique();
let mint_size =
ExtensionType::get_account_len::<Mint>(&[ExtensionType::MintCloseAuthority]);
ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::MintCloseAuthority])
.unwrap();
let mint_base = Mint {
mint_authority: COption::None,
supply: 4242,
@ -339,10 +340,11 @@ mod test {
delegated_amount: 0,
close_authority: COption::None,
};
let account_size = ExtensionType::get_account_len::<TokenAccount>(&[
let account_size = ExtensionType::try_calculate_account_len::<TokenAccount>(&[
ExtensionType::ImmutableOwner,
ExtensionType::MemoTransfer,
]);
])
.unwrap();
let mut account_data = vec![0; account_size];
let mut account_state =
StateWithExtensionsMut::<TokenAccount>::unpack_uninitialized(&mut account_data)
@ -381,10 +383,11 @@ mod test {
delegated_amount: 0,
close_authority: COption::None,
};
let account_size = ExtensionType::get_account_len::<TokenAccount>(&[
let account_size = ExtensionType::try_calculate_account_len::<TokenAccount>(&[
ExtensionType::ImmutableOwner,
ExtensionType::MemoTransfer,
]);
])
.unwrap();
let mut account_data = vec![0; account_size];
let mut account_state =
StateWithExtensionsMut::<TokenAccount>::unpack_uninitialized(&mut account_data)

View File

@ -30,7 +30,7 @@ static SPL_PROGRAMS: &[(Pubkey, Pubkey, &[u8])] = &[
(
spl_token_2022::ID,
solana_sdk::bpf_loader_upgradeable::ID,
include_bytes!("programs/spl_token_2022-0.6.0.so"),
include_bytes!("programs/spl_token_2022-0.9.0.so"),
),
(
spl_memo_1_0::ID,

Binary file not shown.

234
programs/sbf/Cargo.lock generated
View File

@ -2918,6 +2918,17 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "num-derive"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e6a0fd4f737c707bd9086cc16c925f294943eb62eb71499e9fd4cf71f8b9f4e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.37",
]
[[package]]
name = "num-integer"
version = "0.1.42"
@ -2970,15 +2981,6 @@ dependencies = [
"libc",
]
[[package]]
name = "num_enum"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d829733185c1ca374f17e52b762f24f535ec625d2cc1f070e34c8a9068f341b"
dependencies = [
"num_enum_derive 0.5.9",
]
[[package]]
name = "num_enum"
version = "0.6.1"
@ -2989,15 +2991,12 @@ dependencies = [
]
[[package]]
name = "num_enum_derive"
version = "0.5.9"
name = "num_enum"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2be1598bf1c313dcdd12092e3f1920f463462525a21b7b4e11b4168353d0123e"
checksum = "70bf6736f74634d299d00086f02986875b3c2d924781a6a2cb6c201e73da0ceb"
dependencies = [
"proc-macro-crate 1.1.3",
"proc-macro2",
"quote",
"syn 1.0.109",
"num_enum_derive 0.7.0",
]
[[package]]
@ -3012,6 +3011,18 @@ dependencies = [
"syn 2.0.37",
]
[[package]]
name = "num_enum_derive"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56ea360eafe1022f7cc56cd7b869ed57330fb2453d0c7831d99b74c65d2f5597"
dependencies = [
"proc-macro-crate 1.1.3",
"proc-macro2",
"quote",
"syn 2.0.37",
]
[[package]]
name = "num_threads"
version = "0.1.5"
@ -4450,6 +4461,7 @@ dependencies = [
"solana-sdk",
"spl-token",
"spl-token-2022",
"spl-token-metadata-interface",
"thiserror",
"zstd",
]
@ -4478,7 +4490,7 @@ dependencies = [
"lz4",
"memmap2",
"modular-bitfield",
"num-derive",
"num-derive 0.3.0",
"num-traits",
"num_cpus",
"num_enum 0.6.1",
@ -4518,7 +4530,7 @@ dependencies = [
"bincode",
"bytemuck",
"log",
"num-derive",
"num-derive 0.3.0",
"num-traits",
"rustc_version",
"serde",
@ -5241,7 +5253,7 @@ dependencies = [
"log",
"memoffset 0.9.0",
"num-bigint 0.4.4",
"num-derive",
"num-derive 0.3.0",
"num-traits",
"parking_lot 0.12.1",
"rand 0.8.5",
@ -5273,7 +5285,7 @@ dependencies = [
"itertools",
"libc",
"log",
"num-derive",
"num-derive 0.3.0",
"num-traits",
"percentage",
"rand 0.8.5",
@ -5378,7 +5390,7 @@ dependencies = [
"console",
"dialoguer",
"log",
"num-derive",
"num-derive 0.3.0",
"num-traits",
"parking_lot 0.12.1",
"qstring",
@ -5525,7 +5537,7 @@ dependencies = [
"lz4",
"memmap2",
"modular-bitfield",
"num-derive",
"num-derive 0.3.0",
"num-traits",
"num_cpus",
"num_enum 0.6.1",
@ -5695,7 +5707,7 @@ dependencies = [
name = "solana-sbf-rust-error-handling"
version = "1.17.0"
dependencies = [
"num-derive",
"num-derive 0.3.0",
"num-traits",
"solana-program",
"thiserror",
@ -6020,7 +6032,7 @@ dependencies = [
"libsecp256k1 0.6.0",
"log",
"memmap2",
"num-derive",
"num-derive 0.3.0",
"num-traits",
"num_enum 0.6.1",
"pbkdf2 0.11.0",
@ -6246,7 +6258,7 @@ dependencies = [
"Inflector",
"base64 0.21.4",
"bincode",
"borsh 0.9.3",
"borsh 0.10.3",
"bs58",
"lazy_static",
"log",
@ -6409,7 +6421,7 @@ version = "1.17.0"
dependencies = [
"bincode",
"log",
"num-derive",
"num-derive 0.3.0",
"num-traits",
"rustc_version",
"serde",
@ -6428,7 +6440,7 @@ name = "solana-zk-token-proof-program"
version = "1.17.0"
dependencies = [
"bytemuck",
"num-derive",
"num-derive 0.3.0",
"num-traits",
"solana-program-runtime",
"solana-sdk",
@ -6449,7 +6461,7 @@ dependencies = [
"itertools",
"lazy_static",
"merlin",
"num-derive",
"num-derive 0.3.0",
"num-traits",
"rand 0.7.3",
"serde",
@ -6505,13 +6517,13 @@ dependencies = [
[[package]]
name = "spl-associated-token-account"
version = "1.1.3"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978dba3bcbe88d0c2c58366c254d9ea41c5f73357e72fc0bdee4d6b5fc99c8f4"
checksum = "385e31c29981488f2820b2022d8e731aae3b02e6e18e2fd854e4c9a94dc44fc3"
dependencies = [
"assert_matches",
"borsh 0.9.3",
"num-derive",
"borsh 0.10.3",
"num-derive 0.4.0",
"num-traits",
"solana-program",
"spl-token",
@ -6520,47 +6532,181 @@ dependencies = [
]
[[package]]
name = "spl-memo"
version = "3.0.1"
name = "spl-discriminator"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd0dc6f70db6bacea7ff25870b016a65ba1d1b6013536f08e4fd79a8f9005325"
checksum = "cce5d563b58ef1bb2cdbbfe0dfb9ffdc24903b10ae6a4df2d8f425ece375033f"
dependencies = [
"bytemuck",
"solana-program",
"spl-discriminator-derive",
]
[[package]]
name = "spl-discriminator-derive"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fadbefec4f3c678215ca72bd71862697bb06b41fd77c0088902dd3203354387b"
dependencies = [
"quote",
"spl-discriminator-syn",
"syn 2.0.37",
]
[[package]]
name = "spl-discriminator-syn"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e5f2044ca42c8938d54d1255ce599c79a1ffd86b677dfab695caa20f9ffc3f2"
dependencies = [
"proc-macro2",
"quote",
"sha2 0.10.7",
"syn 2.0.37",
"thiserror",
]
[[package]]
name = "spl-memo"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0f180b03318c3dbab3ef4e1e4d46d5211ae3c780940dd0a28695aba4b59a75a"
dependencies = [
"solana-program",
]
[[package]]
name = "spl-token"
version = "3.5.0"
name = "spl-pod"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e85e168a785e82564160dcb87b2a8e04cee9bfd1f4d488c729d53d6a4bd300d"
checksum = "2881dddfca792737c0706fa0175345ab282b1b0879c7d877bad129645737c079"
dependencies = [
"borsh 0.10.3",
"bytemuck",
"solana-program",
"solana-zk-token-sdk",
"spl-program-error",
]
[[package]]
name = "spl-program-error"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "249e0318493b6bcf27ae9902600566c689b7dfba9f1bdff5893e92253374e78c"
dependencies = [
"num-derive 0.4.0",
"num-traits",
"solana-program",
"spl-program-error-derive",
"thiserror",
]
[[package]]
name = "spl-program-error-derive"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5269c8e868da17b6552ef35a51355a017bd8e0eae269c201fef830d35fa52c"
dependencies = [
"proc-macro2",
"quote",
"sha2 0.10.7",
"syn 2.0.37",
]
[[package]]
name = "spl-tlv-account-resolution"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "062e148d3eab7b165582757453632ffeef490c02c86a48bfdb4988f63eefb3b9"
dependencies = [
"bytemuck",
"solana-program",
"spl-discriminator",
"spl-pod",
"spl-program-error",
"spl-type-length-value",
]
[[package]]
name = "spl-token"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08459ba1b8f7c1020b4582c4edf0f5c7511a5e099a7a97570c9698d4f2337060"
dependencies = [
"arrayref",
"bytemuck",
"num-derive",
"num-derive 0.3.0",
"num-traits",
"num_enum 0.5.9",
"num_enum 0.6.1",
"solana-program",
"thiserror",
]
[[package]]
name = "spl-token-2022"
version = "0.6.1"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0043b590232c400bad5ee9eb983ced003d15163c4c5d56b090ac6d9a57457b47"
checksum = "e4abf34a65ba420584a0c35f3903f8d727d1f13ababbdc3f714c6b065a686e86"
dependencies = [
"arrayref",
"bytemuck",
"num-derive",
"num-derive 0.4.0",
"num-traits",
"num_enum 0.5.9",
"num_enum 0.7.0",
"solana-program",
"solana-zk-token-sdk",
"spl-memo",
"spl-pod",
"spl-token",
"spl-token-metadata-interface",
"spl-transfer-hook-interface",
"spl-type-length-value",
"thiserror",
]
[[package]]
name = "spl-token-metadata-interface"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c16ce3ba6979645fb7627aa1e435576172dd63088dc7848cb09aa331fa1fe4f"
dependencies = [
"borsh 0.10.3",
"solana-program",
"spl-discriminator",
"spl-pod",
"spl-program-error",
"spl-type-length-value",
]
[[package]]
name = "spl-transfer-hook-interface"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "051d31803f873cabe71aec3c1b849f35248beae5d19a347d93a5c9cccc5d5a9b"
dependencies = [
"arrayref",
"bytemuck",
"solana-program",
"spl-discriminator",
"spl-pod",
"spl-program-error",
"spl-tlv-account-resolution",
"spl-type-length-value",
]
[[package]]
name = "spl-type-length-value"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a468e6f6371f9c69aae760186ea9f1a01c2908351b06a5e0026d21cfc4d7ecac"
dependencies = [
"bytemuck",
"solana-program",
"spl-discriminator",
"spl-pod",
"spl-program-error",
]
[[package]]
name = "static_assertions"
version = "1.1.0"

View File

@ -171,8 +171,10 @@ targets = ["x86_64-unknown-linux-gnu"]
# * spl-associated-token-account
# * spl-instruction-padding
# * spl-memo
# * spl-pod
# * spl-token
# * spl-token-2022
# * spl-token-metadata-interface
#
# They are included indirectly, for example, `account-decoder` depends on
#

View File

@ -64,6 +64,7 @@ tokio-util = { workspace = true, features = ["codec", "compat"] }
serial_test = { workspace = true }
solana-net-utils = { workspace = true }
solana-stake-program = { workspace = true }
spl-pod = { workspace = true }
symlink = { workspace = true }
[lib]

View File

@ -4683,12 +4683,12 @@ pub mod tests {
vote_instruction,
vote_state::{self, Vote, VoteInit, VoteStateVersions, MAX_LOCKOUT_HISTORY},
},
spl_pod::optional_keys::OptionalNonZeroPubkey,
spl_token_2022::{
extension::{
immutable_owner::ImmutableOwner, memo_transfer::MemoTransfer,
mint_close_authority::MintCloseAuthority, ExtensionType, StateWithExtensionsMut,
},
pod::OptionalNonZeroPubkey,
solana_program::{program_option::COption, pubkey::Pubkey as SplTokenPubkey},
state::{AccountState as TokenAccountState, Mint},
},
@ -7439,10 +7439,11 @@ pub mod tests {
delegated_amount: 30,
close_authority: COption::Some(owner),
};
let account_size = ExtensionType::get_account_len::<TokenAccount>(&[
let account_size = ExtensionType::try_calculate_account_len::<TokenAccount>(&[
ExtensionType::ImmutableOwner,
ExtensionType::MemoTransfer,
]);
])
.unwrap();
let mut account_data = vec![0; account_size];
let mut account_state =
StateWithExtensionsMut::<TokenAccount>::unpack_uninitialized(&mut account_data)
@ -7466,8 +7467,10 @@ pub mod tests {
bank.store_account(&token_account_pubkey, &token_account);
// Add the mint
let mint_size =
ExtensionType::get_account_len::<Mint>(&[ExtensionType::MintCloseAuthority]);
let mint_size = ExtensionType::try_calculate_account_len::<Mint>(&[
ExtensionType::MintCloseAuthority,
])
.unwrap();
let mint_base = Mint {
mint_authority: COption::Some(owner),
supply: 500,
@ -7931,10 +7934,11 @@ pub mod tests {
delegated_amount: 30,
close_authority: COption::Some(owner),
};
let account_size = ExtensionType::get_account_len::<TokenAccount>(&[
let account_size = ExtensionType::try_calculate_account_len::<TokenAccount>(&[
ExtensionType::ImmutableOwner,
ExtensionType::MemoTransfer,
]);
])
.unwrap();
let mut account_data = vec![0; account_size];
let mut account_state =
StateWithExtensionsMut::<TokenAccount>::unpack_uninitialized(&mut account_data)
@ -7957,8 +7961,10 @@ pub mod tests {
});
bank.store_account(&token_account_pubkey, &token_account);
let mint_size =
ExtensionType::get_account_len::<Mint>(&[ExtensionType::MintCloseAuthority]);
let mint_size = ExtensionType::try_calculate_account_len::<Mint>(&[
ExtensionType::MintCloseAuthority,
])
.unwrap();
let mint_base = Mint {
mint_authority: COption::Some(owner),
supply: 500,

View File

@ -13,8 +13,7 @@ edition = { workspace = true }
Inflector = { workspace = true }
base64 = { workspace = true }
bincode = { workspace = true }
# NOTE: Use the workspace version once spl-associated-token-account uses borsh 0.10.
borsh0-9 = { package = "borsh", version = "0.9.3" }
borsh = { workspace = true }
bs58 = { workspace = true }
lazy_static = { workspace = true }
log = { workspace = true }

View File

@ -2,7 +2,7 @@ use {
crate::parse_instruction::{
check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum,
},
borsh0_9::BorshDeserialize,
borsh::BorshDeserialize,
serde_json::json,
solana_sdk::{instruction::CompiledInstruction, message::AccountKeys, pubkey::Pubkey},
spl_associated_token_account::instruction::AssociatedTokenAccountInstruction,

View File

@ -3,9 +3,10 @@ use {
check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum,
},
extension::{
confidential_transfer::*, cpi_guard::*, default_account_state::*, interest_bearing_mint::*,
memo_transfer::*, mint_close_authority::*, permanent_delegate::*, reallocate::*,
transfer_fee::*,
confidential_transfer::*, confidential_transfer_fee::*, cpi_guard::*,
default_account_state::*, interest_bearing_mint::*, memo_transfer::*, metadata_pointer::*,
mint_close_authority::*, permanent_delegate::*, reallocate::*, transfer_fee::*,
transfer_hook::*,
},
serde_json::{json, Map, Value},
solana_account_decoder::parse_token::{token_amount_to_ui_amount, UiAccountState},
@ -229,7 +230,10 @@ pub fn parse_token(
| AuthorityType::CloseMint
| AuthorityType::InterestRate
| AuthorityType::PermanentDelegate
| AuthorityType::ConfidentialTransferMint => "mint",
| AuthorityType::ConfidentialTransferMint
| AuthorityType::TransferHookProgramId
| AuthorityType::ConfidentialTransferFeeConfig
| AuthorityType::MetadataPointer => "mint",
AuthorityType::AccountOwner | AuthorityType::CloseAccount => "account",
};
let mut value = json!({
@ -590,6 +594,62 @@ pub fn parse_token(
account_keys,
)
}
TokenInstruction::TransferHookExtension => {
if instruction.data.len() < 2 {
return Err(ParseInstructionError::InstructionNotParsable(
ParsableProgram::SplToken,
));
}
parse_transfer_hook_instruction(
&instruction.data[1..],
&instruction.accounts,
account_keys,
)
}
TokenInstruction::ConfidentialTransferFeeExtension => {
if instruction.data.len() < 2 {
return Err(ParseInstructionError::InstructionNotParsable(
ParsableProgram::SplToken,
));
}
parse_confidential_transfer_fee_instruction(
&instruction.data[1..],
&instruction.accounts,
account_keys,
)
}
TokenInstruction::WithdrawExcessLamports => {
check_num_token_accounts(&instruction.accounts, 3)?;
let mut value = json!({
"source": account_keys[instruction.accounts[0] as usize].to_string(),
"destination": account_keys[instruction.accounts[1] as usize].to_string(),
});
let map = value.as_object_mut().unwrap();
parse_signers(
map,
2,
account_keys,
&instruction.accounts,
"authority",
"multisigAuthority",
);
Ok(ParsedInstructionEnum {
instruction_type: "withdrawExcessLamports".to_string(),
info: value,
})
}
TokenInstruction::MetadataPointerExtension => {
if instruction.data.len() < 2 {
return Err(ParseInstructionError::InstructionNotParsable(
ParsableProgram::SplToken,
));
}
parse_metadata_pointer_instruction(
&instruction.data[1..],
&instruction.accounts,
account_keys,
)
}
}
}
@ -606,6 +666,9 @@ pub enum UiAuthorityType {
InterestRate,
PermanentDelegate,
ConfidentialTransferMint,
TransferHookProgramId,
ConfidentialTransferFeeConfig,
MetadataPointer,
}
impl From<AuthorityType> for UiAuthorityType {
@ -621,6 +684,11 @@ impl From<AuthorityType> for UiAuthorityType {
AuthorityType::InterestRate => UiAuthorityType::InterestRate,
AuthorityType::PermanentDelegate => UiAuthorityType::PermanentDelegate,
AuthorityType::ConfidentialTransferMint => UiAuthorityType::ConfidentialTransferMint,
AuthorityType::TransferHookProgramId => UiAuthorityType::TransferHookProgramId,
AuthorityType::ConfidentialTransferFeeConfig => {
UiAuthorityType::ConfidentialTransferFeeConfig
}
AuthorityType::MetadataPointer => UiAuthorityType::MetadataPointer,
}
}
}
@ -642,6 +710,12 @@ pub enum UiExtensionType {
CpiGuard,
PermanentDelegate,
NonTransferableAccount,
TransferHook,
TransferHookAccount,
ConfidentialTransferFeeConfig,
ConfidentialTransferFeeAmount,
MetadataPointer,
TokenMetadata,
}
impl From<ExtensionType> for UiExtensionType {
@ -663,6 +737,16 @@ impl From<ExtensionType> for UiExtensionType {
ExtensionType::CpiGuard => UiExtensionType::CpiGuard,
ExtensionType::PermanentDelegate => UiExtensionType::PermanentDelegate,
ExtensionType::NonTransferableAccount => UiExtensionType::NonTransferableAccount,
ExtensionType::TransferHook => UiExtensionType::TransferHook,
ExtensionType::TransferHookAccount => UiExtensionType::TransferHookAccount,
ExtensionType::ConfidentialTransferFeeConfig => {
UiExtensionType::ConfidentialTransferFeeConfig
}
ExtensionType::ConfidentialTransferFeeAmount => {
UiExtensionType::ConfidentialTransferFeeAmount
}
ExtensionType::MetadataPointer => UiExtensionType::MetadataPointer,
ExtensionType::TokenMetadata => UiExtensionType::TokenMetadata,
}
}
}

View File

@ -192,8 +192,8 @@ pub(in crate::parse_token) fn parse_confidential_transfer_instruction(
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(),
"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,
@ -322,85 +322,37 @@ pub(in crate::parse_token) fn parse_confidential_transfer_instruction(
info: value,
})
}
ConfidentialTransferInstruction::WithdrawWithheldTokensFromMint => {
check_num_token_accounts(account_indexes, 4)?;
let withdraw_withheld_data: WithdrawWithheldTokensFromMintData =
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 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,
"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(),
"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),
});
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<String> = 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());
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()),
);
}
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<String> = 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(),
instruction_type: "confidentialTransferWithSplitProofs".to_string(),
info: value,
})
}

View File

@ -0,0 +1,159 @@
use {
super::*,
solana_account_decoder::parse_token_extension::UiConfidentialTransferFeeConfig,
spl_token_2022::{
extension::confidential_transfer_fee::{instruction::*, ConfidentialTransferFeeConfig},
instruction::{decode_instruction_data, decode_instruction_type},
},
};
pub(in crate::parse_token) fn parse_confidential_transfer_fee_instruction(
instruction_data: &[u8],
account_indexes: &[u8],
account_keys: &AccountKeys,
) -> Result<ParsedInstructionEnum, ParseInstructionError> {
match decode_instruction_type(instruction_data)
.map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken))?
{
ConfidentialTransferFeeInstruction::InitializeConfidentialTransferFeeConfig => {
check_num_token_accounts(account_indexes, 1)?;
let confidential_transfer_mint: ConfidentialTransferFeeConfig =
*decode_instruction_data(instruction_data).map_err(|_| {
ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken)
})?;
let confidential_transfer_mint: UiConfidentialTransferFeeConfig =
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: "initializeConfidentialTransferFeeConfig".to_string(),
info: value,
})
}
ConfidentialTransferFeeInstruction::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,
})
}
ConfidentialTransferFeeInstruction::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<String> = 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,
})
}
ConfidentialTransferFeeInstruction::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<String> = 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,
})
}
ConfidentialTransferFeeInstruction::EnableHarvestToMint => {
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: "enableConfidentialTransferFeeHarvestToMint".to_string(),
info: value,
})
}
ConfidentialTransferFeeInstruction::DisableHarvestToMint => {
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: "disableConfidentialTransferFeeHarvestToMint".to_string(),
info: value,
})
}
}
}

View File

@ -0,0 +1,192 @@
use {
super::*,
spl_token_2022::{
extension::metadata_pointer::instruction::*,
instruction::{decode_instruction_data, decode_instruction_type},
},
};
pub(in crate::parse_token) fn parse_metadata_pointer_instruction(
instruction_data: &[u8],
account_indexes: &[u8],
account_keys: &AccountKeys,
) -> Result<ParsedInstructionEnum, ParseInstructionError> {
match decode_instruction_type(instruction_data)
.map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken))?
{
MetadataPointerInstruction::Initialize => {
check_num_token_accounts(account_indexes, 1)?;
let InitializeInstructionData {
authority,
metadata_address,
} = *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(),
});
let map = value.as_object_mut().unwrap();
if let Some(authority) = Option::<Pubkey>::from(authority) {
map.insert("authority".to_string(), json!(authority.to_string()));
}
if let Some(metadata_address) = Option::<Pubkey>::from(metadata_address) {
map.insert(
"metadataAddress".to_string(),
json!(metadata_address.to_string()),
);
}
Ok(ParsedInstructionEnum {
instruction_type: "initializeMetadataPointer".to_string(),
info: value,
})
}
MetadataPointerInstruction::Update => {
check_num_token_accounts(account_indexes, 2)?;
let UpdateInstructionData { metadata_address } =
*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(),
});
let map = value.as_object_mut().unwrap();
if let Some(metadata_address) = Option::<Pubkey>::from(metadata_address) {
map.insert(
"metadataAddress".to_string(),
json!(metadata_address.to_string()),
);
}
parse_signers(
map,
1,
account_keys,
account_indexes,
"authority",
"multisigAuthority",
);
Ok(ParsedInstructionEnum {
instruction_type: "updateMetadataPointer".to_string(),
info: value,
})
}
}
}
#[cfg(test)]
mod test {
use {
super::*, crate::parse_token::test::*, solana_sdk::pubkey::Pubkey,
spl_token_2022::solana_program::message::Message,
};
#[test]
fn test_parse_metadata_pointer_instruction() {
let mint_pubkey = Pubkey::new_unique();
let authority = Pubkey::new_unique();
let metadata_address = Pubkey::new_unique();
// Initialize variations
let init_ix = initialize(
&spl_token_2022::id(),
&mint_pubkey,
Some(authority),
Some(metadata_address),
)
.unwrap();
let message = Message::new(&[init_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!(
parse_token(
&compiled_instruction,
&AccountKeys::new(&message.account_keys, None)
)
.unwrap(),
ParsedInstructionEnum {
instruction_type: "initializeMetadataPointer".to_string(),
info: json!({
"mint": mint_pubkey.to_string(),
"authority": authority.to_string(),
"metadataAddress": metadata_address.to_string(),
})
}
);
let init_ix = initialize(&spl_token_2022::id(), &mint_pubkey, None, None).unwrap();
let message = Message::new(&[init_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!(
parse_token(
&compiled_instruction,
&AccountKeys::new(&message.account_keys, None)
)
.unwrap(),
ParsedInstructionEnum {
instruction_type: "initializeMetadataPointer".to_string(),
info: json!({
"mint": mint_pubkey.to_string(),
})
}
);
// Single owner Update
let update_ix = update(
&spl_token_2022::id(),
&mint_pubkey,
&authority,
&[],
Some(metadata_address),
)
.unwrap();
let message = Message::new(&[update_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!(
parse_token(
&compiled_instruction,
&AccountKeys::new(&message.account_keys, None)
)
.unwrap(),
ParsedInstructionEnum {
instruction_type: "updateMetadataPointer".to_string(),
info: json!({
"mint": mint_pubkey.to_string(),
"authority": authority.to_string(),
"metadataAddress": metadata_address.to_string(),
})
}
);
// Multisig Update
let multisig_pubkey = Pubkey::new_unique();
let multisig_signer0 = Pubkey::new_unique();
let multisig_signer1 = Pubkey::new_unique();
let update_ix = update(
&spl_token_2022::id(),
&mint_pubkey,
&multisig_pubkey,
&[&multisig_signer0, &multisig_signer1],
Some(metadata_address),
)
.unwrap();
let message = Message::new(&[update_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!(
parse_token(
&compiled_instruction,
&AccountKeys::new(&message.account_keys, None)
)
.unwrap(),
ParsedInstructionEnum {
instruction_type: "updateMetadataPointer".to_string(),
info: json!({
"mint": mint_pubkey.to_string(),
"metadataAddress": metadata_address.to_string(),
"multisigAuthority": multisig_pubkey.to_string(),
"signers": vec![
multisig_signer0.to_string(),
multisig_signer1.to_string(),
],
})
}
);
}
}

View File

@ -1,11 +1,14 @@
use super::*;
pub(super) mod confidential_transfer;
pub(super) mod confidential_transfer_fee;
pub(super) mod cpi_guard;
pub(super) mod default_account_state;
pub(super) mod interest_bearing_mint;
pub(super) mod memo_transfer;
pub(super) mod metadata_pointer;
pub(super) mod mint_close_authority;
pub(super) mod permanent_delegate;
pub(super) mod reallocate;
pub(super) mod transfer_fee;
pub(super) mod transfer_hook;

View File

@ -0,0 +1,186 @@
use {
super::*,
spl_token_2022::{
extension::transfer_hook::instruction::*,
instruction::{decode_instruction_data, decode_instruction_type},
},
};
pub(in crate::parse_token) fn parse_transfer_hook_instruction(
instruction_data: &[u8],
account_indexes: &[u8],
account_keys: &AccountKeys,
) -> Result<ParsedInstructionEnum, ParseInstructionError> {
match decode_instruction_type(instruction_data)
.map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken))?
{
TransferHookInstruction::Initialize => {
check_num_token_accounts(account_indexes, 1)?;
let InitializeInstructionData {
authority,
program_id,
} = *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(),
});
let map = value.as_object_mut().unwrap();
if let Some(authority) = Option::<Pubkey>::from(authority) {
map.insert("authority".to_string(), json!(authority.to_string()));
}
if let Some(program_id) = Option::<Pubkey>::from(program_id) {
map.insert("programId".to_string(), json!(program_id.to_string()));
}
Ok(ParsedInstructionEnum {
instruction_type: "initializeTransferHook".to_string(),
info: value,
})
}
TransferHookInstruction::Update => {
check_num_token_accounts(account_indexes, 2)?;
let UpdateInstructionData { program_id } = *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(),
});
let map = value.as_object_mut().unwrap();
if let Some(program_id) = Option::<Pubkey>::from(program_id) {
map.insert("programId".to_string(), json!(program_id.to_string()));
}
parse_signers(
map,
1,
account_keys,
account_indexes,
"authority",
"multisigAuthority",
);
Ok(ParsedInstructionEnum {
instruction_type: "updateTransferHook".to_string(),
info: value,
})
}
}
}
#[cfg(test)]
mod test {
use {
super::*, crate::parse_token::test::*, solana_sdk::pubkey::Pubkey,
spl_token_2022::solana_program::message::Message,
};
#[test]
fn test_parse_transfer_hook_instruction() {
let mint_pubkey = Pubkey::new_unique();
let authority = Pubkey::new_unique();
let program_id = Pubkey::new_unique();
// Initialize variations
let init_ix = initialize(
&spl_token_2022::id(),
&mint_pubkey,
Some(authority),
Some(program_id),
)
.unwrap();
let message = Message::new(&[init_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!(
parse_token(
&compiled_instruction,
&AccountKeys::new(&message.account_keys, None)
)
.unwrap(),
ParsedInstructionEnum {
instruction_type: "initializeTransferHook".to_string(),
info: json!({
"mint": mint_pubkey.to_string(),
"authority": authority.to_string(),
"programId": program_id.to_string(),
})
}
);
let init_ix = initialize(&spl_token_2022::id(), &mint_pubkey, None, None).unwrap();
let message = Message::new(&[init_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!(
parse_token(
&compiled_instruction,
&AccountKeys::new(&message.account_keys, None)
)
.unwrap(),
ParsedInstructionEnum {
instruction_type: "initializeTransferHook".to_string(),
info: json!({
"mint": mint_pubkey.to_string(),
})
}
);
// Single owner Update
let update_ix = update(
&spl_token_2022::id(),
&mint_pubkey,
&authority,
&[],
Some(program_id),
)
.unwrap();
let message = Message::new(&[update_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!(
parse_token(
&compiled_instruction,
&AccountKeys::new(&message.account_keys, None)
)
.unwrap(),
ParsedInstructionEnum {
instruction_type: "updateTransferHook".to_string(),
info: json!({
"mint": mint_pubkey.to_string(),
"authority": authority.to_string(),
"programId": program_id.to_string(),
})
}
);
// Multisig Update
let multisig_pubkey = Pubkey::new_unique();
let multisig_signer0 = Pubkey::new_unique();
let multisig_signer1 = Pubkey::new_unique();
let update_ix = update(
&spl_token_2022::id(),
&mint_pubkey,
&multisig_pubkey,
&[&multisig_signer0, &multisig_signer1],
Some(program_id),
)
.unwrap();
let message = Message::new(&[update_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!(
parse_token(
&compiled_instruction,
&AccountKeys::new(&message.account_keys, None)
)
.unwrap(),
ParsedInstructionEnum {
instruction_type: "updateTransferHook".to_string(),
info: json!({
"mint": mint_pubkey.to_string(),
"programId": program_id.to_string(),
"multisigAuthority": multisig_pubkey.to_string(),
"signers": vec![
multisig_signer0.to_string(),
multisig_signer1.to_string(),
],
})
}
);
}
}