diff --git a/Cargo.lock b/Cargo.lock index 133aeb03a2a..51f215d322e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4575,7 +4575,7 @@ dependencies = [ "solana-sdk 1.10.35", "solana-vote-program", "spl-token", - "spl-token-2022 0.3.0", + "spl-token-2022 0.4.1", "thiserror", "zstd", ] @@ -4985,7 +4985,7 @@ dependencies = [ "solana-transaction-status", "solana-version", "solana-vote-program", - "spl-token-2022 0.3.0", + "spl-token-2022 0.4.1", "thiserror", "tokio", "tokio-stream", @@ -5973,7 +5973,7 @@ dependencies = [ "solana-version", "solana-vote-program", "spl-token", - "spl-token-2022 0.3.0", + "spl-token-2022 0.4.1", "stream-cancel", "symlink", "thiserror", @@ -6447,7 +6447,7 @@ dependencies = [ "spl-associated-token-account", "spl-memo", "spl-token", - "spl-token-2022 0.3.0", + "spl-token-2022 0.4.1", "thiserror", ] @@ -6760,9 +6760,9 @@ dependencies = [ [[package]] name = "spl-token-2022" -version = "0.3.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83f001b3579e69a695a22e458fa1a4b76ab25a142544a094e3f65af63dc61c2f" +checksum = "e684f055853cf11bdc9cd3301da1a3238bc95d25c9e563fd67533c2314900eab" dependencies = [ "arrayref", "bytemuck", diff --git a/account-decoder/Cargo.toml b/account-decoder/Cargo.toml index a7f34a58dfd..55430c25bac 100644 --- a/account-decoder/Cargo.toml +++ b/account-decoder/Cargo.toml @@ -23,7 +23,7 @@ solana-config-program = { path = "../programs/config", version = "=1.10.35" } solana-sdk = { path = "../sdk", version = "=1.10.35" } solana-vote-program = { path = "../programs/vote", version = "=1.10.35" } spl-token = { version = "=3.3.1", features = ["no-entrypoint"] } -spl-token-2022 = { version = "=0.3.0", features = ["no-entrypoint"] } +spl-token-2022 = { version = "=0.4.1", features = ["no-entrypoint"] } thiserror = "1.0" zstd = "0.11.1" diff --git a/account-decoder/src/parse_token.rs b/account-decoder/src/parse_token.rs index 5c24fef42c6..c982630e9bf 100644 --- a/account-decoder/src/parse_token.rs +++ b/account-decoder/src/parse_token.rs @@ -552,8 +552,10 @@ mod test { account_state.pack_base(); account_state.init_account_type().unwrap(); - account_state.init_extension::().unwrap(); - let mut memo_transfer = account_state.init_extension::().unwrap(); + account_state + .init_extension::(true) + .unwrap(); + let mut memo_transfer = account_state.init_extension::(true).unwrap(); memo_transfer.require_incoming_transfer_memos = true.into(); assert!(parse_token(&account_data, None).is_err()); @@ -620,7 +622,9 @@ mod test { let mut mint_state = StateWithExtensionsMut::::unpack_uninitialized(&mut mint_data).unwrap(); - let mut mint_close_authority = mint_state.init_extension::().unwrap(); + let mut mint_close_authority = mint_state + .init_extension::(true) + .unwrap(); mint_close_authority.close_authority = OptionalNonZeroPubkey::try_from(Some(owner_pubkey)).unwrap(); diff --git a/account-decoder/src/parse_token_extension.rs b/account-decoder/src/parse_token_extension.rs index c031fbad385..7a3dcf681f1 100644 --- a/account-decoder/src/parse_token_extension.rs +++ b/account-decoder/src/parse_token_extension.rs @@ -1,5 +1,6 @@ use { crate::parse_token::UiAccountState, + solana_sdk::clock::UnixTimestamp, spl_token_2022::{ extension::{self, BaseState, ExtensionType, StateWithExtensions}, solana_program::pubkey::Pubkey, @@ -18,6 +19,8 @@ pub enum UiExtension { DefaultAccountState(UiDefaultAccountState), ImmutableOwner, MemoTransfer(UiMemoTransfer), + NonTransferable, + InterestBearingConfig(UiInterestBearingConfig), UnparseableExtension, } @@ -50,6 +53,11 @@ pub fn parse_extension( .get_extension::() .map(|&extension| UiExtension::MemoTransfer(extension.into())) .unwrap_or(UiExtension::UnparseableExtension), + ExtensionType::NonTransferable => UiExtension::NonTransferable, + ExtensionType::InterestBearingConfig => account + .get_extension::() + .map(|&extension| UiExtension::InterestBearingConfig(extension.into())) + .unwrap_or(UiExtension::UnparseableExtension), } } @@ -159,3 +167,33 @@ impl From for UiMemoTransfer { } } } + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct UiInterestBearingConfig { + pub rate_authority: Option, + pub initialization_timestamp: UnixTimestamp, + pub pre_update_average_rate: i16, + pub last_update_timestamp: UnixTimestamp, + pub current_rate: i16, +} + +impl From for UiInterestBearingConfig { + fn from( + interest_bearing_config: extension::interest_bearing_mint::InterestBearingConfig, + ) -> Self { + let rate_authority: Option = interest_bearing_config.rate_authority.into(); + + Self { + rate_authority: rate_authority.map(|pubkey| pubkey.to_string()), + initialization_timestamp: UnixTimestamp::from( + interest_bearing_config.initialization_timestamp, + ), + pre_update_average_rate: i16::from(interest_bearing_config.pre_update_average_rate), + last_update_timestamp: UnixTimestamp::from( + interest_bearing_config.last_update_timestamp, + ), + current_rate: i16::from(interest_bearing_config.current_rate), + } + } +} diff --git a/client/Cargo.toml b/client/Cargo.toml index 451ca99dadd..880dcab4e41 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -50,7 +50,7 @@ solana-streamer = { path = "../streamer", version = "=1.10.35" } solana-transaction-status = { path = "../transaction-status", version = "=1.10.35" } solana-version = { path = "../version", version = "=1.10.35" } solana-vote-program = { path = "../programs/vote", version = "=1.10.35" } -spl-token-2022 = { version = "=0.3.0", features = ["no-entrypoint"] } +spl-token-2022 = { version = "=0.4.1", features = ["no-entrypoint"] } thiserror = "1.0" tokio = { version = "~1.14.1", features = ["full"] } tokio-stream = "0.1.8" diff --git a/programs/bpf/Cargo.lock b/programs/bpf/Cargo.lock index ec3f9d1c535..3435099e682 100644 --- a/programs/bpf/Cargo.lock +++ b/programs/bpf/Cargo.lock @@ -4057,7 +4057,7 @@ dependencies = [ "solana-sdk 1.10.35", "solana-vote-program", "spl-token", - "spl-token-2022 0.3.0", + "spl-token-2022 0.4.1", "thiserror", "zstd", ] @@ -4647,7 +4647,7 @@ dependencies = [ "solana-transaction-status", "solana-version", "solana-vote-program", - "spl-token-2022 0.3.0", + "spl-token-2022 0.4.1", "thiserror", "tokio", "tokio-stream", @@ -5309,7 +5309,7 @@ dependencies = [ "solana-version", "solana-vote-program", "spl-token", - "spl-token-2022 0.3.0", + "spl-token-2022 0.4.1", "stream-cancel", "thiserror", "tokio", @@ -5664,7 +5664,7 @@ dependencies = [ "spl-associated-token-account", "spl-memo", "spl-token", - "spl-token-2022 0.3.0", + "spl-token-2022 0.4.1", "thiserror", ] @@ -5950,9 +5950,9 @@ dependencies = [ [[package]] name = "spl-token-2022" -version = "0.3.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83f001b3579e69a695a22e458fa1a4b76ab25a142544a094e3f65af63dc61c2f" +checksum = "e684f055853cf11bdc9cd3301da1a3238bc95d25c9e563fd67533c2314900eab" dependencies = [ "arrayref", "bytemuck", diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index e10896579f0..0c1d5b4c6f9 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -49,7 +49,7 @@ solana-transaction-status = { path = "../transaction-status", version = "=1.10.3 solana-version = { path = "../version", version = "=1.10.35" } solana-vote-program = { path = "../programs/vote", version = "=1.10.35" } spl-token = { version = "=3.3.1", features = ["no-entrypoint"] } -spl-token-2022 = { version = "=0.3.0", features = ["no-entrypoint"] } +spl-token-2022 = { version = "=0.4.1", features = ["no-entrypoint"] } stream-cancel = "0.8.1" thiserror = "1.0" tokio = { version = "~1.14.1", features = ["full"] } diff --git a/rpc/src/rpc.rs b/rpc/src/rpc.rs index a3edba1792d..c645735e8e2 100644 --- a/rpc/src/rpc.rs +++ b/rpc/src/rpc.rs @@ -7265,8 +7265,10 @@ pub mod tests { account_state.base = account_base; account_state.pack_base(); account_state.init_account_type().unwrap(); - account_state.init_extension::().unwrap(); - let mut memo_transfer = account_state.init_extension::().unwrap(); + account_state + .init_extension::(true) + .unwrap(); + let mut memo_transfer = account_state.init_extension::(true).unwrap(); memo_transfer.require_incoming_transfer_memos = true.into(); let token_account = AccountSharedData::from(Account { @@ -7294,8 +7296,9 @@ pub mod tests { mint_state.base = mint_base; mint_state.pack_base(); mint_state.init_account_type().unwrap(); - let mut mint_close_authority = - mint_state.init_extension::().unwrap(); + let mut mint_close_authority = mint_state + .init_extension::(true) + .unwrap(); mint_close_authority.close_authority = OptionalNonZeroPubkey::try_from(Some(owner)).unwrap(); @@ -7763,8 +7766,10 @@ pub mod tests { account_state.base = account_base; account_state.pack_base(); account_state.init_account_type().unwrap(); - account_state.init_extension::().unwrap(); - let mut memo_transfer = account_state.init_extension::().unwrap(); + account_state + .init_extension::(true) + .unwrap(); + let mut memo_transfer = account_state.init_extension::(true).unwrap(); memo_transfer.require_incoming_transfer_memos = true.into(); let token_account = AccountSharedData::from(Account { @@ -7791,8 +7796,9 @@ pub mod tests { mint_state.base = mint_base; mint_state.pack_base(); mint_state.init_account_type().unwrap(); - let mut mint_close_authority = - mint_state.init_extension::().unwrap(); + let mut mint_close_authority = mint_state + .init_extension::(true) + .unwrap(); mint_close_authority.close_authority = OptionalNonZeroPubkey::try_from(Some(owner)).unwrap(); diff --git a/transaction-status/Cargo.toml b/transaction-status/Cargo.toml index 17932a9cd7d..73c776a0f24 100644 --- a/transaction-status/Cargo.toml +++ b/transaction-status/Cargo.toml @@ -28,7 +28,7 @@ solana-vote-program = { path = "../programs/vote", version = "=1.10.35" } spl-associated-token-account = { version = "=1.0.5", features = ["no-entrypoint"] } spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] } spl-token = { version = "=3.3.1", features = ["no-entrypoint"] } -spl-token-2022 = { version = "=0.3.0", features = ["no-entrypoint"] } +spl-token-2022 = { version = "=0.4.1", features = ["no-entrypoint"] } thiserror = "1.0" [package.metadata.docs.rs] diff --git a/transaction-status/src/parse_token.rs b/transaction-status/src/parse_token.rs index 70e997bfd90..0deac4bce8f 100644 --- a/transaction-status/src/parse_token.rs +++ b/transaction-status/src/parse_token.rs @@ -3,8 +3,8 @@ use { check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum, }, extension::{ - default_account_state::*, memo_transfer::*, mint_close_authority::*, reallocate::*, - transfer_fee::*, + default_account_state::*, interest_bearing_mint::*, memo_transfer::*, + mint_close_authority::*, reallocate::*, transfer_fee::*, }, serde_json::{json, Map, Value}, solana_account_decoder::parse_token::{ @@ -227,7 +227,8 @@ pub fn parse_token( | AuthorityType::FreezeAccount | AuthorityType::TransferFeeConfig | AuthorityType::WithheldWithdraw - | AuthorityType::CloseMint => "mint", + | AuthorityType::CloseMint + | AuthorityType::InterestRate => "mint", AuthorityType::AccountOwner | AuthorityType::CloseAccount => "account", }; let mut value = json!({ @@ -550,6 +551,27 @@ pub fn parse_token( }), }) } + TokenInstruction::InitializeNonTransferableMint => { + check_num_token_accounts(&instruction.accounts, 1)?; + Ok(ParsedInstructionEnum { + instruction_type: "initializeNonTransferableMint".to_string(), + info: json!({ + "mint": account_keys[instruction.accounts[0] as usize].to_string(), + }), + }) + } + TokenInstruction::InterestBearingMintExtension => { + if instruction.data.len() < 2 { + return Err(ParseInstructionError::InstructionNotParsable( + ParsableProgram::SplToken, + )); + } + parse_interest_bearing_mint_instruction( + &instruction.data[1..], + &instruction.accounts, + account_keys, + ) + } } } @@ -563,6 +585,7 @@ pub enum UiAuthorityType { TransferFeeConfig, WithheldWithdraw, CloseMint, + InterestRate, } impl From for UiAuthorityType { @@ -575,6 +598,7 @@ impl From for UiAuthorityType { AuthorityType::TransferFeeConfig => UiAuthorityType::TransferFeeConfig, AuthorityType::WithheldWithdraw => UiAuthorityType::WithheldWithdraw, AuthorityType::CloseMint => UiAuthorityType::CloseMint, + AuthorityType::InterestRate => UiAuthorityType::InterestRate, } } } @@ -591,6 +615,8 @@ pub enum UiExtensionType { DefaultAccountState, ImmutableOwner, MemoTransfer, + NonTransferable, + InterestBearingConfig, } impl From for UiExtensionType { @@ -607,6 +633,8 @@ impl From for UiExtensionType { ExtensionType::DefaultAccountState => UiExtensionType::DefaultAccountState, ExtensionType::ImmutableOwner => UiExtensionType::ImmutableOwner, ExtensionType::MemoTransfer => UiExtensionType::MemoTransfer, + ExtensionType::NonTransferable => UiExtensionType::NonTransferable, + ExtensionType::InterestBearingConfig => UiExtensionType::InterestBearingConfig, } } } diff --git a/transaction-status/src/parse_token/extension/interest_bearing_mint.rs b/transaction-status/src/parse_token/extension/interest_bearing_mint.rs new file mode 100644 index 00000000000..a3e6b7d42aa --- /dev/null +++ b/transaction-status/src/parse_token/extension/interest_bearing_mint.rs @@ -0,0 +1,64 @@ +use { + super::*, + spl_token_2022::{ + extension::interest_bearing_mint::{ + instruction::{InitializeInstructionData, InterestBearingMintInstruction}, + BasisPoints, + }, + instruction::{decode_instruction_data, decode_instruction_type}, + }, +}; + +pub(in crate::parse_token) fn parse_interest_bearing_mint_instruction( + instruction_data: &[u8], + account_indexes: &[u8], + account_keys: &AccountKeys, +) -> Result { + match decode_instruction_type(instruction_data) + .map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken))? + { + InterestBearingMintInstruction::Initialize => { + check_num_token_accounts(account_indexes, 1)?; + let InitializeInstructionData { + rate_authority, + rate, + } = *decode_instruction_data(instruction_data).map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let rate_authority = rate_authority; + let rate_authority: Option = rate_authority.into(); + Ok(ParsedInstructionEnum { + instruction_type: "initializeInterestBearingConfig".to_string(), + info: json!({ + "mint": account_keys[account_indexes[0] as usize].to_string(), + "rateAuthority": rate_authority.map(|pubkey| pubkey.to_string()), + "rate": i16::from(rate), + }), + }) + } + InterestBearingMintInstruction::UpdateRate => { + check_num_token_accounts(account_indexes, 2)?; + let new_rate: BasisPoints = + *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(), + "newRate": i16::from(new_rate), + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 1, + account_keys, + account_indexes, + "rateAuthority", + "multisigRateAuthority", + ); + Ok(ParsedInstructionEnum { + instruction_type: "updateInterestBearingConfigRate".to_string(), + info: value, + }) + } + } +} diff --git a/transaction-status/src/parse_token/extension/mod.rs b/transaction-status/src/parse_token/extension/mod.rs index 5a658005652..3c84942651a 100644 --- a/transaction-status/src/parse_token/extension/mod.rs +++ b/transaction-status/src/parse_token/extension/mod.rs @@ -1,6 +1,7 @@ use super::*; pub(super) mod default_account_state; +pub(super) mod interest_bearing_mint; pub(super) mod memo_transfer; pub(super) mod mint_close_authority; pub(super) mod reallocate;