From aba6719c9d824b9cad2e16a65aa2711f8c65237c Mon Sep 17 00:00:00 2001 From: Tokenwrap Date: Tue, 8 Oct 2024 11:12:08 +0200 Subject: [PATCH 1/9] solana: Validation of the new spl metadata account Problem: Attestation is failing for tokens that were created recently. Cause: Metaplex has resized the SPL token metadata account from 679 bytes to 607 bytes in the commit (6b2e869) However, the token-bridge is still using the older version of the SPL token metadata and validating the size based on the older constant. As a result, this check fails to attest tokens that are using the new metadata accounts. Solution: To resolve this, I have added an additional condition with the new metadata length constant, allowing the validation to pass if the SPL token has either of the two metadata account lengths. --- solana/modules/token_bridge/program/src/accounts.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/solana/modules/token_bridge/program/src/accounts.rs b/solana/modules/token_bridge/program/src/accounts.rs index 4dc55f43ee..1069e24f0d 100644 --- a/solana/modules/token_bridge/program/src/accounts.rs +++ b/solana/modules/token_bridge/program/src/accounts.rs @@ -106,6 +106,9 @@ impl<'b> Seeded<&SplTokenMetaDerivationData> for SplTokenMeta<'b> { } } +//New data length of spl token metadata account +pub const NEW_MAX_METADATA_LEN: usize = 607; + /// This method removes code duplication when checking token metadata. When metadata is read for /// attestation and transfers, Token Bridge does not invoke Metaplex's Token Metadata program, so /// it must validate the account the same way Token Metadata program does to ensure the correct @@ -128,7 +131,9 @@ pub fn deserialize_and_verify_metadata( } // Account must be the expected Metadata length. - if info.data_len() != spl_token_metadata::state::MAX_METADATA_LEN { + if info.data_len() != spl_token_metadata::state::MAX_METADATA_LEN + && info.data_len() != NEW_MAX_METADATA_LEN + { return Err(TokenBridgeError::InvalidMetadata.into()); } From a46d9f05b1162cd3d2857d38911ffbe74829378c Mon Sep 17 00:00:00 2001 From: Csongor Kiss Date: Thu, 29 May 2025 13:06:20 +0100 Subject: [PATCH 2/9] solana/Makefile: update upgrade authorities These have since been transferred to the self-upgrade PDA --- solana/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/solana/Makefile b/solana/Makefile index 667742eb03..6d3103a20f 100644 --- a/solana/Makefile +++ b/solana/Makefile @@ -8,9 +8,9 @@ nft_bridge_AUTHORITY_mainnet=3cVZHphy4QUYnU1hYFyvHF9joeZJ6ZTxpWx1nzavaUa8 # Testnet buffer authority is the deployer public key bridge_ADDRESS_testnet=3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5 -bridge_AUTHORITY_testnet=9r6q2iEg4MBevjC8reaLmQUDxueF3vabUoqDkZ2LoAYe +bridge_AUTHORITY_testnet=J8am6SkUHRTtLPJpnfUd6Uy38U7Yh17fa7ZtiqaLoJcV token_bridge_ADDRESS_testnet=DZnkkTmCiFWfYTfT41X3Rd1kDgozqzxWaHqsw6W4x2oe -token_bridge_AUTHORITY_testnet=9r6q2iEg4MBevjC8reaLmQUDxueF3vabUoqDkZ2LoAYe +token_bridge_AUTHORITY_testnet=FQAHqBcVHiiiLP8qXKPDQGr3mEXLv7RSdvfHJ3ZLugBV nft_bridge_ADDRESS_testnet=2rHhojZ7hpu1zA91nvZmT8TqWWvMcKmmNBCr2mKTtMq4 nft_bridge_AUTHORITY_testnet=9r6q2iEg4MBevjC8reaLmQUDxueF3vabUoqDkZ2LoAYe From 67e87f13678a831e446e83ffeb2ab5158921df4a Mon Sep 17 00:00:00 2001 From: Csongor Kiss Date: Thu, 29 May 2025 18:36:56 +0100 Subject: [PATCH 3/9] solana/token_bridge: add support for token2022 --- solana/bridge/program/src/accounts/claim.rs | 8 +- .../program/src/accounts/guardian_set.rs | 8 +- .../bridge/program/src/accounts/posted_vaa.rs | 8 +- .../bridge/program/src/accounts/sequence.rs | 8 +- solana/migration/src/types.rs | 4 + .../modules/nft_bridge/program/src/types.rs | 7 ++ .../program/src/api/complete_transfer.rs | 14 ++- .../src/api/complete_transfer_payload.rs | 8 +- .../program/src/api/create_wrapped.rs | 13 ++- .../token_bridge/program/src/api/transfer.rs | 16 ++- .../modules/token_bridge/program/src/types.rs | 28 ++++- solana/solitaire/program/src/macros.rs | 18 +++- .../solitaire/program/src/processors/peel.rs | 5 + .../program/src/processors/seeded.rs | 101 +++++++++++++++++- 14 files changed, 226 insertions(+), 20 deletions(-) diff --git a/solana/bridge/program/src/accounts/claim.rs b/solana/bridge/program/src/accounts/claim.rs index 3c0eca2dc3..1ed656b4f0 100644 --- a/solana/bridge/program/src/accounts/claim.rs +++ b/solana/bridge/program/src/accounts/claim.rs @@ -34,7 +34,10 @@ use serde::{ }; use solana_program::pubkey::Pubkey; use solitaire::{ - processors::seeded::Seeded, + processors::seeded::{ + Seeded, + SingleOwned, + }, AccountOwner, AccountState::*, CreationLamports::Exempt, @@ -100,6 +103,9 @@ impl Owned for ClaimData { } } +impl SingleOwned for ClaimData { +} + pub struct ClaimDerivationData { pub emitter_address: [u8; 32], pub emitter_chain: u16, diff --git a/solana/bridge/program/src/accounts/guardian_set.rs b/solana/bridge/program/src/accounts/guardian_set.rs index fd945948b7..60d775f3e6 100644 --- a/solana/bridge/program/src/accounts/guardian_set.rs +++ b/solana/bridge/program/src/accounts/guardian_set.rs @@ -11,7 +11,10 @@ use serde::{ Serialize, }; use solitaire::{ - processors::seeded::Seeded, + processors::seeded::{ + Seeded, + SingleOwned, + }, AccountOwner, AccountState, Data, @@ -63,3 +66,6 @@ impl Owned for GuardianSetData { AccountOwner::This } } + +impl SingleOwned for GuardianSetData { +} diff --git a/solana/bridge/program/src/accounts/posted_vaa.rs b/solana/bridge/program/src/accounts/posted_vaa.rs index e8e429bbf8..d0a3b34bfe 100644 --- a/solana/bridge/program/src/accounts/posted_vaa.rs +++ b/solana/bridge/program/src/accounts/posted_vaa.rs @@ -4,7 +4,10 @@ use borsh::{ BorshSerialize, }; use solitaire::{ - processors::seeded::Seeded, + processors::seeded::{ + Seeded, + SingleOwned, + }, AccountOwner, AccountState, Data, @@ -94,6 +97,9 @@ impl Owned for PostedVAAData { } } +impl SingleOwned for PostedVAAData { +} + #[cfg(feature = "cpi")] impl Owned for PostedVAAData { fn owner(&self) -> AccountOwner { diff --git a/solana/bridge/program/src/accounts/sequence.rs b/solana/bridge/program/src/accounts/sequence.rs index e651380fb8..c1ee28f2f2 100644 --- a/solana/bridge/program/src/accounts/sequence.rs +++ b/solana/bridge/program/src/accounts/sequence.rs @@ -4,7 +4,10 @@ use borsh::{ }; use solana_program::pubkey::Pubkey; use solitaire::{ - processors::seeded::Seeded, + processors::seeded::{ + Seeded, + SingleOwned, + }, AccountOwner, AccountState, Data, @@ -36,3 +39,6 @@ impl Owned for SequenceTracker { AccountOwner::This } } + +impl SingleOwned for SequenceTracker { +} diff --git a/solana/migration/src/types.rs b/solana/migration/src/types.rs index 9dd7894939..348503e605 100644 --- a/solana/migration/src/types.rs +++ b/solana/migration/src/types.rs @@ -12,6 +12,7 @@ use solitaire::{ processors::seeded::{ AccountOwner, Owned, + SingleOwned, }, }; use spl_token::state::{ @@ -31,5 +32,8 @@ impl Owned for PoolData { } } +impl SingleOwned for PoolData { +} + pack_type!(SplMint, Mint, AccountOwner::Other(spl_token::id())); pack_type!(SplAccount, Account, AccountOwner::Other(spl_token::id())); diff --git a/solana/modules/nft_bridge/program/src/types.rs b/solana/modules/nft_bridge/program/src/types.rs index 7cd80087a1..789a2a630e 100644 --- a/solana/modules/nft_bridge/program/src/types.rs +++ b/solana/modules/nft_bridge/program/src/types.rs @@ -12,6 +12,7 @@ use solitaire::{ processors::seeded::{ AccountOwner, Owned, + SingleOwned, }, }; use spl_token::state::{ @@ -45,6 +46,9 @@ impl Owned for EndpointRegistration { } } +impl SingleOwned for EndpointRegistration { +} + #[derive(Default, Clone, Copy, BorshDeserialize, BorshSerialize, Serialize, Deserialize)] pub struct WrappedMeta { pub chain: ChainID, @@ -58,5 +62,8 @@ impl Owned for WrappedMeta { } } +impl SingleOwned for WrappedMeta { +} + pack_type!(SplMint, Mint, AccountOwner::Other(spl_token::id())); pack_type!(SplAccount, Account, AccountOwner::Other(spl_token::id())); diff --git a/solana/modules/token_bridge/program/src/api/complete_transfer.rs b/solana/modules/token_bridge/program/src/api/complete_transfer.rs index 2abf4b22af..f0e6e22177 100644 --- a/solana/modules/token_bridge/program/src/api/complete_transfer.rs +++ b/solana/modules/token_bridge/program/src/api/complete_transfer.rs @@ -133,9 +133,11 @@ pub fn complete_native( .checked_sub(fee) .ok_or(SolitaireError::InsufficientFunds)?; + let token_program = accs.mint.info().owner; + // Transfer tokens let transfer_ix = spl_token::instruction::transfer( - &spl_token::id(), + token_program, accs.custody.info().key, accs.to.info().key, accs.custody_signer.key, @@ -144,9 +146,11 @@ pub fn complete_native( )?; invoke_seeded(&transfer_ix, ctx, &accs.custody_signer, None)?; + let token_program = accs.mint.info().owner; + // Transfer fees let transfer_ix = spl_token::instruction::transfer( - &spl_token::id(), + token_program, accs.custody.info().key, accs.to_fees.info().key, accs.custody_signer.key, @@ -249,9 +253,11 @@ pub fn complete_wrapped( .checked_sub(accs.vaa.fee.as_u64()) .ok_or(SolitaireError::InsufficientFunds)?; + let token_program = accs.mint.info().owner; + // Mint tokens let mint_ix = spl_token::instruction::mint_to( - &spl_token::id(), + token_program, accs.mint.info().key, accs.to.info().key, accs.mint_authority.key, @@ -262,7 +268,7 @@ pub fn complete_wrapped( // Mint fees let mint_ix = spl_token::instruction::mint_to( - &spl_token::id(), + token_program, accs.mint.info().key, accs.to_fees.info().key, accs.mint_authority.key, diff --git a/solana/modules/token_bridge/program/src/api/complete_transfer_payload.rs b/solana/modules/token_bridge/program/src/api/complete_transfer_payload.rs index 42b514369c..5be7350390 100644 --- a/solana/modules/token_bridge/program/src/api/complete_transfer_payload.rs +++ b/solana/modules/token_bridge/program/src/api/complete_transfer_payload.rs @@ -193,9 +193,11 @@ pub fn complete_native_with_payload( amount *= 10u64.pow((accs.mint.decimals - 8) as u32); } + let token_program = accs.mint.info().owner; + // Transfer tokens let transfer_ix = spl_token::instruction::transfer( - &spl_token::id(), + token_program, accs.custody.info().key, accs.to.info().key, accs.custody_signer.key, @@ -298,9 +300,11 @@ pub fn complete_wrapped_with_payload( claim::consume(ctx, accs.payer.key, &mut accs.claim, &accs.vaa)?; + let token_program = accs.mint.info().owner; + // Mint tokens let mint_ix = spl_token::instruction::mint_to( - &spl_token::id(), + token_program, accs.mint.info().key, accs.to.info().key, accs.mint_authority.key, diff --git a/solana/modules/token_bridge/program/src/api/create_wrapped.rs b/solana/modules/token_bridge/program/src/api/create_wrapped.rs index 31ea5852da..561220875b 100644 --- a/solana/modules/token_bridge/program/src/api/create_wrapped.rs +++ b/solana/modules/token_bridge/program/src/api/create_wrapped.rs @@ -34,6 +34,7 @@ use solana_program::{ use solitaire::{ processors::seeded::{ invoke_seeded, + CreatableWithOwner, Seeded, }, CreationLamports::Exempt, @@ -130,9 +131,17 @@ pub fn create_accounts( accs: &mut CreateWrapped, _data: CreateWrappedData, ) -> Result<()> { + // wrapped accounts use legacy spl + let token_program = spl_token::id(); + // Create mint account - accs.mint - .create(&((&*accs).into()), ctx, accs.payer.key, Exempt)?; + accs.mint.create_with_owner( + &token_program, + &((&*accs).into()), + ctx, + accs.payer.key, + Exempt, + )?; // Initialize mint let init_ix = spl_token::instruction::initialize_mint( diff --git a/solana/modules/token_bridge/program/src/api/transfer.rs b/solana/modules/token_bridge/program/src/api/transfer.rs index 3c81703908..31d2a5fa58 100644 --- a/solana/modules/token_bridge/program/src/api/transfer.rs +++ b/solana/modules/token_bridge/program/src/api/transfer.rs @@ -45,6 +45,7 @@ use solana_program::{ use solitaire::{ processors::seeded::{ invoke_seeded, + CreatableWithOwner, Seeded, }, CreationLamports::Exempt, @@ -203,11 +204,13 @@ pub fn verify_and_execute_native_transfers( } } + let token_program = mint.info().owner; + if !custody.is_initialized() { - custody.create(derivation_data, ctx, payer.key, Exempt)?; + custody.create_with_owner(token_program, derivation_data, ctx, payer.key, Exempt)?; let init_ix = spl_token::instruction::initialize_account( - &spl_token::id(), + token_program, custody.info().key, mint.info().key, custody_signer.key, @@ -222,9 +225,12 @@ pub fn verify_and_execute_native_transfers( // Untruncate the amount to drop the remainder so we don't "burn" user's funds. let amount_trunc: u64 = amount * trunc_divisor; + // the token program is the owner of the mint account (either spl or token2022) + let token_program = mint.info().owner; + // Transfer tokens let transfer_ix = spl_token::instruction::transfer( - &spl_token::id(), + token_program, from.info().key, custody.info().key, authority_signer.key, @@ -397,9 +403,11 @@ pub fn verify_and_execute_wrapped_transfers( // Verify that meta is correct wrapped_meta.verify_derivation(ctx.program_id, derivation_data)?; + let token_program = mint.info().owner; + // Burn tokens let burn_ix = spl_token::instruction::burn( - &spl_token::id(), + token_program, from.info().key, mint.info().key, authority_signer.key, diff --git a/solana/modules/token_bridge/program/src/types.rs b/solana/modules/token_bridge/program/src/types.rs index 6c6339aa84..8219f571ba 100644 --- a/solana/modules/token_bridge/program/src/types.rs +++ b/solana/modules/token_bridge/program/src/types.rs @@ -12,6 +12,7 @@ use solitaire::{ processors::seeded::{ AccountOwner, Owned, + SingleOwned, }, }; use spl_token::state::{ @@ -55,6 +56,9 @@ impl Owned for EndpointRegistration { } } +impl SingleOwned for EndpointRegistration { +} + #[cfg(feature = "cpi")] impl Owned for EndpointRegistration { fn owner(&self) -> AccountOwner { @@ -70,6 +74,9 @@ pub struct WrappedMeta { pub original_decimals: u8, } +impl SingleOwned for WrappedMeta { +} + #[cfg(not(feature = "cpi"))] impl Owned for WrappedMeta { fn owner(&self) -> AccountOwner { @@ -85,5 +92,22 @@ impl Owned for WrappedMeta { } } -pack_type!(SplMint, Mint, AccountOwner::Other(spl_token::id())); -pack_type!(SplAccount, Account, AccountOwner::Other(spl_token::id())); +pub mod spl_token_2022 { + use solana_program::pubkey::Pubkey; + use std::str::FromStr; + + pub fn id() -> Pubkey { + Pubkey::from_str("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb").unwrap() + } +} + +pack_type!( + SplMint, + Mint, + AccountOwner::OneOf(vec![spl_token::id(), spl_token_2022::id()]) +); +pack_type!( + SplAccount, + Account, + AccountOwner::OneOf(vec![spl_token::id(), spl_token_2022::id()]) +); diff --git a/solana/solitaire/program/src/macros.rs b/solana/solitaire/program/src/macros.rs index ed604b21a0..a98db2249d 100644 --- a/solana/solitaire/program/src/macros.rs +++ b/solana/solitaire/program/src/macros.rs @@ -110,7 +110,7 @@ macro_rules! solitaire { } #[macro_export] -macro_rules! pack_type { +macro_rules! pack_type_impl { ($name:ident, $embed:ty, $owner:expr) => { #[repr(transparent)] pub struct $name(pub $embed); @@ -158,3 +158,19 @@ macro_rules! pack_type { } }; } + +#[macro_export] +macro_rules! pack_type { + ($name:ident, $embed:ty, AccountOwner::OneOf($owner:expr)) => { + solitaire::pack_type_impl!($name, $embed, AccountOwner::OneOf($owner)); + + impl solitaire::processors::seeded::MultiOwned for $name { + } + }; + ($name:ident, $embed:ty, $owner:expr) => { + solitaire::pack_type_impl!($name, $embed, $owner); + + impl solitaire::processors::seeded::SingleOwned for $name { + } + }; +} diff --git a/solana/solitaire/program/src/processors/peel.rs b/solana/solitaire/program/src/processors/peel.rs index d15c9961cb..8584d6c1b3 100644 --- a/solana/solitaire/program/src/processors/peel.rs +++ b/solana/solitaire/program/src/processors/peel.rs @@ -217,6 +217,11 @@ impl< return Err(SolitaireError::InvalidOwner(*ctx.info.owner)); } } + AccountOwner::OneOf(vs) => { + if !vs.contains(ctx.info.owner) { + return Err(SolitaireError::InvalidOwner(*ctx.info.owner)); + } + } AccountOwner::Any => {} }; } diff --git a/solana/solitaire/program/src/processors/seeded.rs b/solana/solitaire/program/src/processors/seeded.rs index 1d477040b6..38c61b22aa 100644 --- a/solana/solitaire/program/src/processors/seeded.rs +++ b/solana/solitaire/program/src/processors/seeded.rs @@ -25,16 +25,59 @@ pub trait AccountSize { pub enum AccountOwner { This, Other(Pubkey), + OneOf(Vec), Any, } +/// The ownership story: +/// We (solitaire) require every account to have an owner specified for two reasons: +/// 1. Security checks (i.e. solitaire checks that the account is owned by whoever we expect) +/// 2. Account creation +/// +/// Solitaire programs create many accounts, most of which are owned by the program itself. +/// However, some accounts are owned by external programs. When performing security +/// checks, the ownership is expressed as a constraint on the account, and this +/// constraint may allow multiple potential owners. +/// But when creating an account, we need to know exactly who the owner is. +/// +/// An example of this is a token account, which, when checking its owner, we +/// check that it's owned by _either_ the SPL token program, or the +/// Token2022 program. However, when creating a token account, we need to +/// know exactly which program is the expected owner. +/// +/// To this end, we have two traits: +/// - `SingleOwned` is for accounts that have a single owner +/// - `MultiOwned` is for accounts that can have multiple potential owners +/// +/// This stratification allows us then have two separate creation flows: +/// `Creatable` for accounts with a single owner, and `CreatableWithOwner` for +/// accounts whose owner is one of a set of owners. This way, assuming that only the +/// appropriate trait is implemented for a type, the compiler statically +/// guarantees that the appropriate creation flow is used (.create vs .create_with_owner). pub trait Owned { fn owner(&self) -> AccountOwner; +} +/// A trait for accounts that may have a single owner. Most accounts are like this. +pub trait SingleOwned: Owned { fn owner_pubkey(&self, program_id: &Pubkey) -> Result { match self.owner() { AccountOwner::This => Ok(*program_id), AccountOwner::Other(v) => Ok(v), + AccountOwner::OneOf(_) => Err(SolitaireError::AmbiguousOwner), + AccountOwner::Any => Err(SolitaireError::AmbiguousOwner), + } + } +} + +/// A trait for accounts that may have multiple potential owners. This is the +/// equivalent of an `InterfaceAccount` in anchor. +pub trait MultiOwned: Owned { + fn owners_pubkeys(&self, program_id: &Pubkey) -> Result> { + match self.owner() { + AccountOwner::This => Ok(vec![*program_id]), + AccountOwner::Other(v) => Ok(vec![v]), + AccountOwner::OneOf(v) => Ok(v), AccountOwner::Any => Err(SolitaireError::AmbiguousOwner), } } @@ -48,6 +91,16 @@ impl<'a, T: Owned + Default, const IS_INITIALIZED: AccountState> Owned } } +impl<'a, T: SingleOwned + Default, const IS_INITIALIZED: AccountState> SingleOwned + for Data<'a, T, IS_INITIALIZED> +{ +} + +impl<'a, T: MultiOwned + Default, const IS_INITIALIZED: AccountState> MultiOwned + for Data<'a, T, IS_INITIALIZED> +{ +} + pub trait Seeded { fn seeds(accs: I) -> Vec>; @@ -105,6 +158,17 @@ pub trait Creatable<'a, I> { ) -> Result<()>; } +pub trait CreatableWithOwner<'a, I> { + fn create_with_owner( + &'a self, + owner: &Pubkey, + accs: I, + ctx: &'a ExecutionContext, + payer: &'a Pubkey, + lamports: CreationLamports, + ) -> Result<()>; +} + impl AccountSize for Data<'_, T, IS_INITIALIZED> { @@ -113,7 +177,9 @@ impl Ac } } -impl<'a, 'b: 'a, K, T: AccountSize + Seeded + Keyed<'a, 'b> + Owned> Creatable<'a, K> for T { +impl<'a, 'b: 'a, K, T: AccountSize + Seeded + Keyed<'a, 'b> + SingleOwned> Creatable<'a, K> + for T +{ fn create( &'a self, accs: K, @@ -139,6 +205,39 @@ impl<'a, 'b: 'a, K, T: AccountSize + Seeded + Keyed<'a, 'b> + Owned> Creatabl } } +impl<'a, 'b: 'a, K, T: AccountSize + Seeded + Keyed<'a, 'b> + MultiOwned> + CreatableWithOwner<'a, K> for T +{ + fn create_with_owner( + &'a self, + owner: &Pubkey, + accs: K, + ctx: &'a ExecutionContext<'_, '_>, + payer: &'a Pubkey, + lamports: CreationLamports, + ) -> Result<()> { + if !self.owners_pubkeys(ctx.program_id)?.contains(owner) { + return Err(SolitaireError::InvalidOwner(*owner)); + } + + let seeds = T::bumped_seeds(accs, ctx.program_id); + let size = self.size(); + + let s: Vec<&[u8]> = seeds.iter().map(|item| item.as_slice()).collect(); + let seed_slice = s.as_slice(); + + create_account( + ctx, + self.info(), + payer, + lamports, + size, + owner, + SignedWithSeeds(&[seed_slice]), + ) + } +} + impl<'a, const SEED: &'static str, T> Seeded> for Derive { fn seeds(_accs: Option<()>) -> Vec> { vec![SEED.as_bytes().to_vec()] From 5b650eca73127810fcb7fb41bc6c87d11b95f652 Mon Sep 17 00:00:00 2001 From: Csongor Kiss Date: Fri, 13 Jun 2025 16:03:20 +0100 Subject: [PATCH 4/9] solana/token_bridge: vendor spl-token and relax program id check --- solana/Cargo.lock | 3 +-- solana/Cargo.toml | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/solana/Cargo.lock b/solana/Cargo.lock index 9ff31e8837..654949defe 100644 --- a/solana/Cargo.lock +++ b/solana/Cargo.lock @@ -3969,8 +3969,7 @@ dependencies = [ [[package]] name = "spl-token" version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc67166ef99d10c18cb5e9c208901e6d8255c6513bb1f877977eba48e6cc4fb" +source = "git+https://github.com/wormholelabs-xyz/spl-token.git?rev=7ae1b55#7ae1b553adb5889e57e7afb054e557ab7dd0d873" dependencies = [ "arrayref", "num-derive", diff --git a/solana/Cargo.toml b/solana/Cargo.toml index c939d9e560..869bf14915 100644 --- a/solana/Cargo.toml +++ b/solana/Cargo.toml @@ -14,3 +14,4 @@ members = [ [patch.crates-io] memmap2 = { path = "bridge/memmap2-rs" } +spl-token = { git = "https://github.com/wormholelabs-xyz/spl-token.git", rev = "7ae1b55" } From 265b30e10fd4f23ef476e968fdad2efcd87f4ab6 Mon Sep 17 00:00:00 2001 From: Csongor Kiss Date: Wed, 27 Aug 2025 20:05:42 +0100 Subject: [PATCH 5/9] solana: add 0-dep parser for token2022 metadata --- solana/Cargo.toml | 3 + .../token_metadata_parser/Cargo.lock | 3875 +++++++++++++++++ .../token_metadata_parser/Cargo.toml | 20 + .../token_metadata_parser/README.md | 13 + .../token_metadata_parser/src/lib.rs | 544 +++ 5 files changed, 4455 insertions(+) create mode 100644 solana/modules/token_bridge/token_metadata_parser/Cargo.lock create mode 100644 solana/modules/token_bridge/token_metadata_parser/Cargo.toml create mode 100644 solana/modules/token_bridge/token_metadata_parser/README.md create mode 100644 solana/modules/token_bridge/token_metadata_parser/src/lib.rs diff --git a/solana/Cargo.toml b/solana/Cargo.toml index 869bf14915..bde76b60d3 100644 --- a/solana/Cargo.toml +++ b/solana/Cargo.toml @@ -11,6 +11,9 @@ members = [ "solitaire/program", "solitaire/rocksalt", ] +exclude = [ + "modules/token_bridge/token_metadata_parser", +] [patch.crates-io] memmap2 = { path = "bridge/memmap2-rs" } diff --git a/solana/modules/token_bridge/token_metadata_parser/Cargo.lock b/solana/modules/token_bridge/token_metadata_parser/Cargo.lock new file mode 100644 index 0000000000..e6c753d4ab --- /dev/null +++ b/solana/modules/token_bridge/token_metadata_parser/Cargo.lock @@ -0,0 +1,3875 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm-siv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "polyval", + "subtle", + "zeroize", +] + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.3", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "2.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" +dependencies = [ + "serde", +] + +[[package]] +name = "blake3" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "digest 0.10.7", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "borsh" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115e54d64eb62cdebad391c19efc9dce4981c690c85a33a12199d99bb9546fee" +dependencies = [ + "borsh-derive 0.10.4", + "hashbrown 0.13.2", +] + +[[package]] +name = "borsh" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" +dependencies = [ + "borsh-derive 1.5.7", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831213f80d9423998dd696e2c5345aba6be7a0bd8cd19e31c5243e13df1cef89" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate 0.1.5", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3" +dependencies = [ + "once_cell", + "proc-macro-crate 3.3.0", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65d6ba50644c98714aa2a70d13d7df3cd75cd2b523a2b452bf010443800976b3" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276691d96f063427be83e6692b86148e488ebba9f48f77788724ca027ba3b6d4" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "bv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8834bb1d8ee5dc048ee3124f2c7c1afcc6bc9aed03f11e9dfd8c69470a5db340" +dependencies = [ + "feature-probe", + "serde", +] + +[[package]] +name = "bytemuck" +version = "1.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "cfg_eval" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45565fc9416b9896014f5732ac776f810ee53a66730c17e4020c3ec064a8f88f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "num-traits", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "console_log" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89f72f65e8501878b8a004d5a1afb780987e2ce2b4532c562e367a72c57499f" +dependencies = [ + "log", + "web-sys", +] + +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rand_core 0.6.4", + "rustc_version", + "serde", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.106", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "derivation-path" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5c37193a1db1d8ed868c03ec7b152175f26160a5b740e5e484143877e0adf0" + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", + "subtle", +] + +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek 3.2.0", + "ed25519", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "ed25519-dalek-bip32" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d2be62a4061b872c8c0873ee4fc6f101ce7b889d039f019c5fa2af471a59908" +dependencies = [ + "derivation-path", + "ed25519-dalek", + "hmac 0.12.1", + "sha2 0.10.9", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "feature-probe" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "five8" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75b8549488b4715defcb0d8a8a1c1c76a80661b5fa106b4ca0e7fce59d7d875" +dependencies = [ + "five8_core", +] + +[[package]] +name = "five8_const" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26dec3da8bc3ef08f2c04f61eab298c3ab334523e55f076354d6d6f613799a7b" +dependencies = [ + "five8_core", +] + +[[package]] +name = "five8_core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2551bf44bc5f776c15044b9b94153a00198be06743e262afaaa61f11ac7523a5" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array", + "hmac 0.8.1", +] + +[[package]] +name = "humantime" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" +dependencies = [ + "equivalent", + "hashbrown 0.15.5", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.175" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" + +[[package]] +name = "libsecp256k1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d220bc1feda2ac231cb78c3d26f27676b8cf82c96971f7aeef3d0cf2797c73" +dependencies = [ + "arrayref", + "base64 0.12.3", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f6ab710cec28cef759c5f18671a27dae2a5f952cdaaee1d8e2908cb2478a80" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccab96b584d38fac86a83f07e659f0deafd0253dc096dab5a36d53efe653c5c3" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67abfe149395e3aa1c48a2beb32b068e2334402df8181f818d3aee2b304c4f5d" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.6.4", + "zeroize", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +dependencies = [ + "proc-macro-crate 3.3.0", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl" +version = "0.10.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "openssl-sys" +version = "0.9.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "qstring" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "redox_syscall" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-big-array" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_bytes" +version = "0.11.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "serde_json" +version = "1.0.143" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" +dependencies = [ + "serde", + "serde_derive", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "solana-account" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f949fe4edaeaea78c844023bfc1c898e0b1f5a100f8a8d2d0f85d0a7b090258" +dependencies = [ + "bincode", + "serde", + "serde_bytes", + "serde_derive", + "solana-account-info", + "solana-clock", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-sysvar", +] + +[[package]] +name = "solana-account-info" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8f5152a288ef1912300fc6efa6c2d1f9bb55d9398eb6c72326360b8063987da" +dependencies = [ + "bincode", + "serde", + "solana-program-error", + "solana-program-memory", + "solana-pubkey", +] + +[[package]] +name = "solana-address-lookup-table-interface" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1673f67efe870b64a65cb39e6194be5b26527691ce5922909939961a6e6b395" +dependencies = [ + "bincode", + "bytemuck", + "serde", + "serde_derive", + "solana-clock", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-slot-hashes", +] + +[[package]] +name = "solana-atomic-u64" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52e52720efe60465b052b9e7445a01c17550666beec855cce66f44766697bc2" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "solana-big-mod-exp" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75db7f2bbac3e62cfd139065d15bcda9e2428883ba61fc8d27ccb251081e7567" +dependencies = [ + "num-bigint", + "num-traits", + "solana-define-syscall", +] + +[[package]] +name = "solana-bincode" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a3787b8cf9c9fe3dd360800e8b70982b9e5a8af9e11c354b6665dd4a003adc" +dependencies = [ + "bincode", + "serde", + "solana-instruction", +] + +[[package]] +name = "solana-blake3-hasher" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a0801e25a1b31a14494fc80882a036be0ffd290efc4c2d640bfcca120a4672" +dependencies = [ + "blake3", + "solana-define-syscall", + "solana-hash", + "solana-sanitize", +] + +[[package]] +name = "solana-bn254" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4420f125118732833f36facf96a27e7b78314b2d642ba07fa9ffdacd8d79e243" +dependencies = [ + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-serialize", + "bytemuck", + "solana-define-syscall", + "thiserror 2.0.16", +] + +[[package]] +name = "solana-borsh" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718333bcd0a1a7aed6655aa66bef8d7fb047944922b2d3a18f49cbc13e73d004" +dependencies = [ + "borsh 0.10.4", + "borsh 1.5.7", +] + +[[package]] +name = "solana-client-traits" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83f0071874e629f29e0eb3dab8a863e98502ac7aba55b7e0df1803fc5cac72a7" +dependencies = [ + "solana-account", + "solana-commitment-config", + "solana-epoch-info", + "solana-hash", + "solana-instruction", + "solana-keypair", + "solana-message", + "solana-pubkey", + "solana-signature", + "solana-signer", + "solana-system-interface", + "solana-transaction", + "solana-transaction-error", +] + +[[package]] +name = "solana-clock" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb482ab70fced82ad3d7d3d87be33d466a3498eb8aa856434ff3c0dfc2e2e31" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-cluster-type" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ace9fea2daa28354d107ea879cff107181d85cd4e0f78a2bedb10e1a428c97e" +dependencies = [ + "serde", + "serde_derive", + "solana-hash", +] + +[[package]] +name = "solana-commitment-config" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac49c4dde3edfa832de1697e9bcdb7c3b3f7cb7a1981b7c62526c8bb6700fb73" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "solana-compute-budget-interface" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8432d2c4c22d0499aa06d62e4f7e333f81777b3d7c96050ae9e5cb71a8c3aee4" +dependencies = [ + "borsh 1.5.7", + "serde", + "serde_derive", + "solana-instruction", + "solana-sdk-ids", +] + +[[package]] +name = "solana-cpi" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dc71126edddc2ba014622fc32d0f5e2e78ec6c5a1e0eb511b85618c09e9ea11" +dependencies = [ + "solana-account-info", + "solana-define-syscall", + "solana-instruction", + "solana-program-error", + "solana-pubkey", + "solana-stable-layout", +] + +[[package]] +name = "solana-curve25519" +version = "2.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b162f50499b391b785d57b2f2c73e3b9754d88fd4894bef444960b00bda8dcca" +dependencies = [ + "bytemuck", + "bytemuck_derive", + "curve25519-dalek 4.1.3", + "solana-define-syscall", + "subtle", + "thiserror 2.0.16", +] + +[[package]] +name = "solana-decode-error" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c781686a18db2f942e70913f7ca15dc120ec38dcab42ff7557db2c70c625a35" +dependencies = [ + "num-traits", +] + +[[package]] +name = "solana-define-syscall" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae3e2abcf541c8122eafe9a625d4d194b4023c20adde1e251f94e056bb1aee2" + +[[package]] +name = "solana-derivation-path" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "939756d798b25c5ec3cca10e06212bdca3b1443cb9bb740a38124f58b258737b" +dependencies = [ + "derivation-path", + "qstring", + "uriparse", +] + +[[package]] +name = "solana-ed25519-program" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1feafa1691ea3ae588f99056f4bdd1293212c7ece28243d7da257c443e84753" +dependencies = [ + "bytemuck", + "bytemuck_derive", + "ed25519-dalek", + "solana-feature-set", + "solana-instruction", + "solana-precompile-error", + "solana-sdk-ids", +] + +[[package]] +name = "solana-epoch-info" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ef6f0b449290b0b9f32973eefd95af35b01c5c0c34c569f936c34c5b20d77b" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "solana-epoch-rewards" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b575d3dd323b9ea10bb6fe89bf6bf93e249b215ba8ed7f68f1a3633f384db7" +dependencies = [ + "serde", + "serde_derive", + "solana-hash", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-epoch-rewards-hasher" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c5fd2662ae7574810904585fd443545ed2b568dbd304b25a31e79ccc76e81b" +dependencies = [ + "siphasher", + "solana-hash", + "solana-pubkey", +] + +[[package]] +name = "solana-epoch-schedule" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fce071fbddecc55d727b1d7ed16a629afe4f6e4c217bc8d00af3b785f6f67ed" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-example-mocks" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84461d56cbb8bb8d539347151e0525b53910102e4bced875d49d5139708e39d3" +dependencies = [ + "serde", + "serde_derive", + "solana-address-lookup-table-interface", + "solana-clock", + "solana-hash", + "solana-instruction", + "solana-keccak-hasher", + "solana-message", + "solana-nonce", + "solana-pubkey", + "solana-sdk-ids", + "solana-system-interface", + "thiserror 2.0.16", +] + +[[package]] +name = "solana-feature-gate-interface" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f5c5382b449e8e4e3016fb05e418c53d57782d8b5c30aa372fc265654b956d" +dependencies = [ + "bincode", + "serde", + "serde_derive", + "solana-account", + "solana-account-info", + "solana-instruction", + "solana-program-error", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-system-interface", +] + +[[package]] +name = "solana-feature-set" +version = "2.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93b93971e289d6425f88e6e3cb6668c4b05df78b3c518c249be55ced8efd6b6d" +dependencies = [ + "ahash", + "lazy_static", + "solana-epoch-schedule", + "solana-hash", + "solana-pubkey", + "solana-sha256-hasher", +] + +[[package]] +name = "solana-fee-calculator" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89bc408da0fb3812bc3008189d148b4d3e08252c79ad810b245482a3f70cd8d" +dependencies = [ + "log", + "serde", + "serde_derive", +] + +[[package]] +name = "solana-fee-structure" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33adf673581c38e810bf618f745bf31b683a0a4a4377682e6aaac5d9a058dd4e" +dependencies = [ + "serde", + "serde_derive", + "solana-message", + "solana-native-token", +] + +[[package]] +name = "solana-genesis-config" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3725085d47b96d37fef07a29d78d2787fc89a0b9004c66eed7753d1e554989f" +dependencies = [ + "bincode", + "chrono", + "memmap2", + "serde", + "serde_derive", + "solana-account", + "solana-clock", + "solana-cluster-type", + "solana-epoch-schedule", + "solana-fee-calculator", + "solana-hash", + "solana-inflation", + "solana-keypair", + "solana-logger", + "solana-poh-config", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-sha256-hasher", + "solana-shred-version", + "solana-signer", + "solana-time-utils", +] + +[[package]] +name = "solana-hard-forks" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c28371f878e2ead55611d8ba1b5fb879847156d04edea13693700ad1a28baf" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "solana-hash" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b96e9f0300fa287b545613f007dfe20043d7812bee255f418c1eb649c93b63" +dependencies = [ + "borsh 1.5.7", + "bytemuck", + "bytemuck_derive", + "five8", + "js-sys", + "serde", + "serde_derive", + "solana-atomic-u64", + "solana-sanitize", + "wasm-bindgen", +] + +[[package]] +name = "solana-inflation" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23eef6a09eb8e568ce6839573e4966850e85e9ce71e6ae1a6c930c1c43947de3" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "solana-instruction" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47298e2ce82876b64f71e9d13a46bc4b9056194e7f9937ad3084385befa50885" +dependencies = [ + "bincode", + "borsh 1.5.7", + "getrandom 0.2.16", + "js-sys", + "num-traits", + "serde", + "serde_derive", + "solana-define-syscall", + "solana-pubkey", + "wasm-bindgen", +] + +[[package]] +name = "solana-instructions-sysvar" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0e85a6fad5c2d0c4f5b91d34b8ca47118fc593af706e523cdbedf846a954f57" +dependencies = [ + "bitflags", + "solana-account-info", + "solana-instruction", + "solana-program-error", + "solana-pubkey", + "solana-sanitize", + "solana-sdk-ids", + "solana-serialize-utils", + "solana-sysvar-id", +] + +[[package]] +name = "solana-keccak-hasher" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7aeb957fbd42a451b99235df4942d96db7ef678e8d5061ef34c9b34cae12f79" +dependencies = [ + "sha3", + "solana-define-syscall", + "solana-hash", + "solana-sanitize", +] + +[[package]] +name = "solana-keypair" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd3f04aa1a05c535e93e121a95f66e7dcccf57e007282e8255535d24bf1e98bb" +dependencies = [ + "ed25519-dalek", + "ed25519-dalek-bip32", + "five8", + "rand 0.7.3", + "solana-derivation-path", + "solana-pubkey", + "solana-seed-derivable", + "solana-seed-phrase", + "solana-signature", + "solana-signer", + "wasm-bindgen", +] + +[[package]] +name = "solana-last-restart-slot" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a6360ac2fdc72e7463565cd256eedcf10d7ef0c28a1249d261ec168c1b55cdd" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-loader-v2-interface" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8ab08006dad78ae7cd30df8eea0539e207d08d91eaefb3e1d49a446e1c49654" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", +] + +[[package]] +name = "solana-loader-v3-interface" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f7162a05b8b0773156b443bccd674ea78bb9aa406325b467ea78c06c99a63a2" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-system-interface", +] + +[[package]] +name = "solana-loader-v4-interface" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "706a777242f1f39a83e2a96a2a6cb034cb41169c6ecbee2cf09cb873d9659e7e" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-system-interface", +] + +[[package]] +name = "solana-logger" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8e777ec1afd733939b532a42492d888ec7c88d8b4127a5d867eb45c6eb5cd5" +dependencies = [ + "env_logger", + "lazy_static", + "libc", + "log", + "signal-hook", +] + +[[package]] +name = "solana-message" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1796aabce376ff74bf89b78d268fa5e683d7d7a96a0a4e4813ec34de49d5314b" +dependencies = [ + "bincode", + "blake3", + "lazy_static", + "serde", + "serde_derive", + "solana-bincode", + "solana-hash", + "solana-instruction", + "solana-pubkey", + "solana-sanitize", + "solana-sdk-ids", + "solana-short-vec", + "solana-system-interface", + "solana-transaction-error", + "wasm-bindgen", +] + +[[package]] +name = "solana-msg" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36a1a14399afaabc2781a1db09cb14ee4cc4ee5c7a5a3cfcc601811379a8092" +dependencies = [ + "solana-define-syscall", +] + +[[package]] +name = "solana-native-token" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61515b880c36974053dd499c0510066783f0cc6ac17def0c7ef2a244874cf4a9" + +[[package]] +name = "solana-nonce" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703e22eb185537e06204a5bd9d509b948f0066f2d1d814a6f475dafb3ddf1325" +dependencies = [ + "serde", + "serde_derive", + "solana-fee-calculator", + "solana-hash", + "solana-pubkey", + "solana-sha256-hasher", +] + +[[package]] +name = "solana-nonce-account" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde971a20b8dbf60144d6a84439dda86b5466e00e2843091fe731083cda614da" +dependencies = [ + "solana-account", + "solana-hash", + "solana-nonce", + "solana-sdk-ids", +] + +[[package]] +name = "solana-offchain-message" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b526398ade5dea37f1f147ce55dae49aa017a5d7326606359b0445ca8d946581" +dependencies = [ + "num_enum", + "solana-hash", + "solana-packet", + "solana-pubkey", + "solana-sanitize", + "solana-sha256-hasher", + "solana-signature", + "solana-signer", +] + +[[package]] +name = "solana-packet" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "004f2d2daf407b3ec1a1ca5ec34b3ccdfd6866dd2d3c7d0715004a96e4b6d127" +dependencies = [ + "bincode", + "bitflags", + "cfg_eval", + "serde", + "serde_derive", + "serde_with", +] + +[[package]] +name = "solana-poh-config" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d650c3b4b9060082ac6b0efbbb66865089c58405bfb45de449f3f2b91eccee75" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "solana-precompile-error" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d87b2c1f5de77dfe2b175ee8dd318d196aaca4d0f66f02842f80c852811f9f8" +dependencies = [ + "num-traits", + "solana-decode-error", +] + +[[package]] +name = "solana-precompiles" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36e92768a57c652edb0f5d1b30a7d0bc64192139c517967c18600debe9ae3832" +dependencies = [ + "lazy_static", + "solana-ed25519-program", + "solana-feature-set", + "solana-message", + "solana-precompile-error", + "solana-pubkey", + "solana-sdk-ids", + "solana-secp256k1-program", + "solana-secp256r1-program", +] + +[[package]] +name = "solana-presigner" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81a57a24e6a4125fc69510b6774cd93402b943191b6cddad05de7281491c90fe" +dependencies = [ + "solana-pubkey", + "solana-signature", + "solana-signer", +] + +[[package]] +name = "solana-program" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98eca145bd3545e2fbb07166e895370576e47a00a7d824e325390d33bf467210" +dependencies = [ + "bincode", + "blake3", + "borsh 0.10.4", + "borsh 1.5.7", + "bs58", + "bytemuck", + "console_error_panic_hook", + "console_log", + "getrandom 0.2.16", + "lazy_static", + "log", + "memoffset", + "num-bigint", + "num-derive", + "num-traits", + "rand 0.8.5", + "serde", + "serde_bytes", + "serde_derive", + "solana-account-info", + "solana-address-lookup-table-interface", + "solana-atomic-u64", + "solana-big-mod-exp", + "solana-bincode", + "solana-blake3-hasher", + "solana-borsh", + "solana-clock", + "solana-cpi", + "solana-decode-error", + "solana-define-syscall", + "solana-epoch-rewards", + "solana-epoch-schedule", + "solana-example-mocks", + "solana-feature-gate-interface", + "solana-fee-calculator", + "solana-hash", + "solana-instruction", + "solana-instructions-sysvar", + "solana-keccak-hasher", + "solana-last-restart-slot", + "solana-loader-v2-interface", + "solana-loader-v3-interface", + "solana-loader-v4-interface", + "solana-message", + "solana-msg", + "solana-native-token", + "solana-nonce", + "solana-program-entrypoint", + "solana-program-error", + "solana-program-memory", + "solana-program-option", + "solana-program-pack", + "solana-pubkey", + "solana-rent", + "solana-sanitize", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-secp256k1-recover", + "solana-serde-varint", + "solana-serialize-utils", + "solana-sha256-hasher", + "solana-short-vec", + "solana-slot-hashes", + "solana-slot-history", + "solana-stable-layout", + "solana-stake-interface", + "solana-system-interface", + "solana-sysvar", + "solana-sysvar-id", + "solana-vote-interface", + "thiserror 2.0.16", + "wasm-bindgen", +] + +[[package]] +name = "solana-program-entrypoint" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32ce041b1a0ed275290a5008ee1a4a6c48f5054c8a3d78d313c08958a06aedbd" +dependencies = [ + "solana-account-info", + "solana-msg", + "solana-program-error", + "solana-pubkey", +] + +[[package]] +name = "solana-program-error" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ee2e0217d642e2ea4bee237f37bd61bb02aec60da3647c48ff88f6556ade775" +dependencies = [ + "borsh 1.5.7", + "num-traits", + "serde", + "serde_derive", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-pubkey", +] + +[[package]] +name = "solana-program-memory" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a5426090c6f3fd6cfdc10685322fede9ca8e5af43cd6a59e98bfe4e91671712" +dependencies = [ + "solana-define-syscall", +] + +[[package]] +name = "solana-program-option" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc677a2e9bc616eda6dbdab834d463372b92848b2bfe4a1ed4e4b4adba3397d0" + +[[package]] +name = "solana-program-pack" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "319f0ef15e6e12dc37c597faccb7d62525a509fec5f6975ecb9419efddeb277b" +dependencies = [ + "solana-program-error", +] + +[[package]] +name = "solana-pubkey" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b62adb9c3261a052ca1f999398c388f1daf558a1b492f60a6d9e64857db4ff1" +dependencies = [ + "borsh 0.10.4", + "borsh 1.5.7", + "bytemuck", + "bytemuck_derive", + "curve25519-dalek 4.1.3", + "five8", + "five8_const", + "getrandom 0.2.16", + "js-sys", + "num-traits", + "rand 0.8.5", + "serde", + "serde_derive", + "solana-atomic-u64", + "solana-decode-error", + "solana-define-syscall", + "solana-sanitize", + "solana-sha256-hasher", + "wasm-bindgen", +] + +[[package]] +name = "solana-quic-definitions" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf0d4d5b049eb1d0c35f7b18f305a27c8986fc5c0c9b383e97adaa35334379e" +dependencies = [ + "solana-keypair", +] + +[[package]] +name = "solana-rent" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1aea8fdea9de98ca6e8c2da5827707fb3842833521b528a713810ca685d2480" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-rent-collector" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "127e6dfa51e8c8ae3aa646d8b2672bc4ac901972a338a9e1cd249e030564fb9d" +dependencies = [ + "serde", + "serde_derive", + "solana-account", + "solana-clock", + "solana-epoch-schedule", + "solana-genesis-config", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", +] + +[[package]] +name = "solana-rent-debits" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f6f9113c6003492e74438d1288e30cffa8ccfdc2ef7b49b9e816d8034da18cd" +dependencies = [ + "solana-pubkey", + "solana-reward-info", +] + +[[package]] +name = "solana-reserved-account-keys" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4b22ea19ca2a3f28af7cd047c914abf833486bf7a7c4a10fc652fff09b385b1" +dependencies = [ + "lazy_static", + "solana-feature-set", + "solana-pubkey", + "solana-sdk-ids", +] + +[[package]] +name = "solana-reward-info" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18205b69139b1ae0ab8f6e11cdcb627328c0814422ad2482000fa2ca54ae4a2f" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "solana-sanitize" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61f1bc1357b8188d9c4a3af3fc55276e56987265eb7ad073ae6f8180ee54cecf" + +[[package]] +name = "solana-sdk" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cc0e4a7635b902791c44b6581bfb82f3ada32c5bc0929a64f39fe4bb384c86a" +dependencies = [ + "bincode", + "bs58", + "getrandom 0.1.16", + "js-sys", + "serde", + "serde_json", + "solana-account", + "solana-bn254", + "solana-client-traits", + "solana-cluster-type", + "solana-commitment-config", + "solana-compute-budget-interface", + "solana-decode-error", + "solana-derivation-path", + "solana-ed25519-program", + "solana-epoch-info", + "solana-epoch-rewards-hasher", + "solana-feature-set", + "solana-fee-structure", + "solana-genesis-config", + "solana-hard-forks", + "solana-inflation", + "solana-instruction", + "solana-keypair", + "solana-message", + "solana-native-token", + "solana-nonce-account", + "solana-offchain-message", + "solana-packet", + "solana-poh-config", + "solana-precompile-error", + "solana-precompiles", + "solana-presigner", + "solana-program", + "solana-program-memory", + "solana-pubkey", + "solana-quic-definitions", + "solana-rent-collector", + "solana-rent-debits", + "solana-reserved-account-keys", + "solana-reward-info", + "solana-sanitize", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-secp256k1-program", + "solana-secp256k1-recover", + "solana-secp256r1-program", + "solana-seed-derivable", + "solana-seed-phrase", + "solana-serde", + "solana-serde-varint", + "solana-short-vec", + "solana-shred-version", + "solana-signature", + "solana-signer", + "solana-system-transaction", + "solana-time-utils", + "solana-transaction", + "solana-transaction-context", + "solana-transaction-error", + "solana-validator-exit", + "thiserror 2.0.16", + "wasm-bindgen", +] + +[[package]] +name = "solana-sdk-ids" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5d8b9cc68d5c88b062a33e23a6466722467dde0035152d8fb1afbcdf350a5f" +dependencies = [ + "solana-pubkey", +] + +[[package]] +name = "solana-sdk-macro" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86280da8b99d03560f6ab5aca9de2e38805681df34e0bb8f238e69b29433b9df" +dependencies = [ + "bs58", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "solana-secp256k1-program" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f19833e4bc21558fe9ec61f239553abe7d05224347b57d65c2218aeeb82d6149" +dependencies = [ + "bincode", + "digest 0.10.7", + "libsecp256k1", + "serde", + "serde_derive", + "sha3", + "solana-feature-set", + "solana-instruction", + "solana-precompile-error", + "solana-sdk-ids", + "solana-signature", +] + +[[package]] +name = "solana-secp256k1-recover" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baa3120b6cdaa270f39444f5093a90a7b03d296d362878f7a6991d6de3bbe496" +dependencies = [ + "borsh 1.5.7", + "libsecp256k1", + "solana-define-syscall", + "thiserror 2.0.16", +] + +[[package]] +name = "solana-secp256r1-program" +version = "2.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce0ae46da3071a900f02d367d99b2f3058fe2e90c5062ac50c4f20cfedad8f0f" +dependencies = [ + "bytemuck", + "openssl", + "solana-feature-set", + "solana-instruction", + "solana-precompile-error", + "solana-sdk-ids", +] + +[[package]] +name = "solana-security-txt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "468aa43b7edb1f9b7b7b686d5c3aeb6630dc1708e86e31343499dd5c4d775183" + +[[package]] +name = "solana-seed-derivable" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beb82b5adb266c6ea90e5cf3967235644848eac476c5a1f2f9283a143b7c97f" +dependencies = [ + "solana-derivation-path", +] + +[[package]] +name = "solana-seed-phrase" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36187af2324f079f65a675ec22b31c24919cb4ac22c79472e85d819db9bbbc15" +dependencies = [ + "hmac 0.12.1", + "pbkdf2", + "sha2 0.10.9", +] + +[[package]] +name = "solana-serde" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1931484a408af466e14171556a47adaa215953c7f48b24e5f6b0282763818b04" +dependencies = [ + "serde", +] + +[[package]] +name = "solana-serde-varint" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a7e155eba458ecfb0107b98236088c3764a09ddf0201ec29e52a0be40857113" +dependencies = [ + "serde", +] + +[[package]] +name = "solana-serialize-utils" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "817a284b63197d2b27afdba829c5ab34231da4a9b4e763466a003c40ca4f535e" +dependencies = [ + "solana-instruction", + "solana-pubkey", + "solana-sanitize", +] + +[[package]] +name = "solana-sha256-hasher" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa3feb32c28765f6aa1ce8f3feac30936f16c5c3f7eb73d63a5b8f6f8ecdc44" +dependencies = [ + "sha2 0.10.9", + "solana-define-syscall", + "solana-hash", +] + +[[package]] +name = "solana-short-vec" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c54c66f19b9766a56fa0057d060de8378676cb64987533fa088861858fc5a69" +dependencies = [ + "serde", +] + +[[package]] +name = "solana-shred-version" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afd3db0461089d1ad1a78d9ba3f15b563899ca2386351d38428faa5350c60a98" +dependencies = [ + "solana-hard-forks", + "solana-hash", + "solana-sha256-hasher", +] + +[[package]] +name = "solana-signature" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c8ec8e657aecfc187522fc67495142c12f35e55ddeca8698edbb738b8dbd8c" +dependencies = [ + "ed25519-dalek", + "five8", + "rand 0.8.5", + "serde", + "serde-big-array", + "serde_derive", + "solana-sanitize", +] + +[[package]] +name = "solana-signer" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c41991508a4b02f021c1342ba00bcfa098630b213726ceadc7cb032e051975b" +dependencies = [ + "solana-pubkey", + "solana-signature", + "solana-transaction-error", +] + +[[package]] +name = "solana-slot-hashes" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8691982114513763e88d04094c9caa0376b867a29577939011331134c301ce" +dependencies = [ + "serde", + "serde_derive", + "solana-hash", + "solana-sdk-ids", + "solana-sysvar-id", +] + +[[package]] +name = "solana-slot-history" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97ccc1b2067ca22754d5283afb2b0126d61eae734fc616d23871b0943b0d935e" +dependencies = [ + "bv", + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sysvar-id", +] + +[[package]] +name = "solana-stable-layout" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f14f7d02af8f2bc1b5efeeae71bc1c2b7f0f65cd75bcc7d8180f2c762a57f54" +dependencies = [ + "solana-instruction", + "solana-pubkey", +] + +[[package]] +name = "solana-stake-interface" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5269e89fde216b4d7e1d1739cf5303f8398a1ff372a81232abbee80e554a838c" +dependencies = [ + "borsh 0.10.4", + "borsh 1.5.7", + "num-traits", + "serde", + "serde_derive", + "solana-clock", + "solana-cpi", + "solana-decode-error", + "solana-instruction", + "solana-program-error", + "solana-pubkey", + "solana-system-interface", + "solana-sysvar-id", +] + +[[package]] +name = "solana-system-interface" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d7c18cb1a91c6be5f5a8ac9276a1d7c737e39a21beba9ea710ab4b9c63bc90" +dependencies = [ + "js-sys", + "num-traits", + "serde", + "serde_derive", + "solana-decode-error", + "solana-instruction", + "solana-pubkey", + "wasm-bindgen", +] + +[[package]] +name = "solana-system-transaction" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd98a25e5bcba8b6be8bcbb7b84b24c2a6a8178d7fb0e3077a916855ceba91a" +dependencies = [ + "solana-hash", + "solana-keypair", + "solana-message", + "solana-pubkey", + "solana-signer", + "solana-system-interface", + "solana-transaction", +] + +[[package]] +name = "solana-sysvar" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8c3595f95069f3d90f275bb9bd235a1973c4d059028b0a7f81baca2703815db" +dependencies = [ + "base64 0.22.1", + "bincode", + "bytemuck", + "bytemuck_derive", + "lazy_static", + "serde", + "serde_derive", + "solana-account-info", + "solana-clock", + "solana-define-syscall", + "solana-epoch-rewards", + "solana-epoch-schedule", + "solana-fee-calculator", + "solana-hash", + "solana-instruction", + "solana-instructions-sysvar", + "solana-last-restart-slot", + "solana-program-entrypoint", + "solana-program-error", + "solana-program-memory", + "solana-pubkey", + "solana-rent", + "solana-sanitize", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-slot-hashes", + "solana-slot-history", + "solana-stake-interface", + "solana-sysvar-id", +] + +[[package]] +name = "solana-sysvar-id" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5762b273d3325b047cfda250787f8d796d781746860d5d0a746ee29f3e8812c1" +dependencies = [ + "solana-pubkey", + "solana-sdk-ids", +] + +[[package]] +name = "solana-time-utils" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af261afb0e8c39252a04d026e3ea9c405342b08c871a2ad8aa5448e068c784c" + +[[package]] +name = "solana-transaction" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80657d6088f721148f5d889c828ca60c7daeedac9a8679f9ec215e0c42bcbf41" +dependencies = [ + "bincode", + "serde", + "serde_derive", + "solana-bincode", + "solana-feature-set", + "solana-hash", + "solana-instruction", + "solana-keypair", + "solana-message", + "solana-precompiles", + "solana-pubkey", + "solana-sanitize", + "solana-sdk-ids", + "solana-short-vec", + "solana-signature", + "solana-signer", + "solana-system-interface", + "solana-transaction-error", + "wasm-bindgen", +] + +[[package]] +name = "solana-transaction-context" +version = "2.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aefd75e49dd990f7fdbe562a539a7b046a839aadf43843845d766a2a6a2adfef" +dependencies = [ + "bincode", + "serde", + "serde_derive", + "solana-account", + "solana-instruction", + "solana-instructions-sysvar", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", +] + +[[package]] +name = "solana-transaction-error" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a9dc8fdb61c6088baab34fc3a8b8473a03a7a5fd404ed8dd502fa79b67cb1" +dependencies = [ + "serde", + "serde_derive", + "solana-instruction", + "solana-sanitize", +] + +[[package]] +name = "solana-validator-exit" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bbf6d7a3c0b28dd5335c52c0e9eae49d0ae489a8f324917faf0ded65a812c1d" + +[[package]] +name = "solana-vote-interface" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b80d57478d6599d30acc31cc5ae7f93ec2361a06aefe8ea79bc81739a08af4c3" +dependencies = [ + "bincode", + "num-derive", + "num-traits", + "serde", + "serde_derive", + "solana-clock", + "solana-decode-error", + "solana-hash", + "solana-instruction", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-serde-varint", + "solana-serialize-utils", + "solana-short-vec", + "solana-system-interface", +] + +[[package]] +name = "solana-zk-sdk" +version = "2.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bb171c0f76c420a7cb6aabbe5fa85a1a009d5bb4009189c43e1a03aff9446d7" +dependencies = [ + "aes-gcm-siv", + "base64 0.22.1", + "bincode", + "bytemuck", + "bytemuck_derive", + "curve25519-dalek 4.1.3", + "itertools 0.12.1", + "js-sys", + "merlin", + "num-derive", + "num-traits", + "rand 0.8.5", + "serde", + "serde_derive", + "serde_json", + "sha3", + "solana-derivation-path", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-seed-derivable", + "solana-seed-phrase", + "solana-signature", + "solana-signer", + "subtle", + "thiserror 2.0.16", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "spl-discriminator" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7398da23554a31660f17718164e31d31900956054f54f52d5ec1be51cb4f4b3" +dependencies = [ + "bytemuck", + "solana-program-error", + "solana-sha256-hasher", + "spl-discriminator-derive", +] + +[[package]] +name = "spl-discriminator-derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9e8418ea6269dcfb01c712f0444d2c75542c04448b480e87de59d2865edc750" +dependencies = [ + "quote", + "spl-discriminator-syn", + "syn 2.0.106", +] + +[[package]] +name = "spl-discriminator-syn" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d1dbc82ab91422345b6df40a79e2b78c7bce1ebb366da323572dd60b7076b67" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.9", + "syn 2.0.106", + "thiserror 1.0.69", +] + +[[package]] +name = "spl-elgamal-registry" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56cc66fe64651a48c8deb4793d8a5deec8f8faf19f355b9df294387bc5a36b5f" +dependencies = [ + "bytemuck", + "solana-account-info", + "solana-cpi", + "solana-instruction", + "solana-msg", + "solana-program-entrypoint", + "solana-program-error", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-security-txt", + "solana-system-interface", + "solana-sysvar", + "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.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d994afaf86b779104b4a95ba9ca75b8ced3fdb17ee934e38cb69e72afbe17799" +dependencies = [ + "borsh 1.5.7", + "bytemuck", + "bytemuck_derive", + "num-derive", + "num-traits", + "solana-decode-error", + "solana-msg", + "solana-program-error", + "solana-program-option", + "solana-pubkey", + "solana-zk-sdk", + "thiserror 2.0.16", +] + +[[package]] +name = "spl-program-error" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdebc8b42553070b75aa5106f071fef2eb798c64a7ec63375da4b1f058688c6" +dependencies = [ + "num-derive", + "num-traits", + "solana-decode-error", + "solana-msg", + "solana-program-error", + "spl-program-error-derive", + "thiserror 2.0.16", +] + +[[package]] +name = "spl-program-error-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2539e259c66910d78593475540e8072f0b10f0f61d7607bbf7593899ed52d0" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.9", + "syn 2.0.106", +] + +[[package]] +name = "spl-tlv-account-resolution" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1408e961215688715d5a1063cbdcf982de225c45f99c82b4f7d7e1dd22b998d7" +dependencies = [ + "bytemuck", + "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 2.0.16", +] + +[[package]] +name = "spl-token" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053067c6a82c705004f91dae058b11b4780407e9ccd6799dc9e7d0fab5f242da" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "num_enum", + "solana-account-info", + "solana-cpi", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-entrypoint", + "solana-program-error", + "solana-program-memory", + "solana-program-option", + "solana-program-pack", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-sysvar", + "thiserror 2.0.16", +] + +[[package]] +name = "spl-token-2022" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707d8237d17d857246b189d0fb278797dcd7cf6219374547791b231fd35a8cc8" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "num_enum", + "solana-account-info", + "solana-clock", + "solana-cpi", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-native-token", + "solana-program-entrypoint", + "solana-program-error", + "solana-program-memory", + "solana-program-option", + "solana-program-pack", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-security-txt", + "solana-system-interface", + "solana-sysvar", + "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", + "spl-type-length-value", + "thiserror 2.0.16", +] + +[[package]] +name = "spl-token-confidential-transfer-ciphertext-arithmetic" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cddd52bfc0f1c677b41493dafa3f2dbbb4b47cf0990f08905429e19dc8289b35" +dependencies = [ + "base64 0.22.1", + "bytemuck", + "solana-curve25519", + "solana-zk-sdk", +] + +[[package]] +name = "spl-token-confidential-transfer-proof-extraction" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512c85bdbbb4cbcc2038849a9e164c958b16541f252b53ea1a3933191c0a4a1a" +dependencies = [ + "bytemuck", + "solana-account-info", + "solana-curve25519", + "solana-instruction", + "solana-instructions-sysvar", + "solana-msg", + "solana-program-error", + "solana-pubkey", + "solana-sdk-ids", + "solana-zk-sdk", + "spl-pod", + "thiserror 2.0.16", +] + +[[package]] +name = "spl-token-confidential-transfer-proof-generation" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa27b9174bea869a7ebf31e0be6890bce90b1a4288bc2bbf24bd413f80ae3fde" +dependencies = [ + "curve25519-dalek 4.1.3", + "solana-zk-sdk", + "thiserror 2.0.16", +] + +[[package]] +name = "spl-token-group-interface" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5597b4cd76f85ce7cd206045b7dc22da8c25516573d42d267c8d1fd128db5129" +dependencies = [ + "bytemuck", + "num-derive", + "num-traits", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", + "spl-discriminator", + "spl-pod", + "thiserror 2.0.16", +] + +[[package]] +name = "spl-token-metadata-interface" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "304d6e06f0de0c13a621464b1fd5d4b1bebf60d15ca71a44d3839958e0da16ee" +dependencies = [ + "borsh 1.5.7", + "num-derive", + "num-traits", + "solana-borsh", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", + "spl-discriminator", + "spl-pod", + "spl-type-length-value", + "thiserror 2.0.16", +] + +[[package]] +name = "spl-transfer-hook-interface" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7e905b849b6aba63bde8c4badac944ebb6c8e6e14817029cbe1bc16829133bd" +dependencies = [ + "arrayref", + "bytemuck", + "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 2.0.16", +] + +[[package]] +name = "spl-type-length-value" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d417eb548214fa822d93f84444024b4e57c13ed6719d4dcc68eec24fb481e9f5" +dependencies = [ + "bytemuck", + "num-derive", + "num-traits", + "solana-account-info", + "solana-decode-error", + "solana-msg", + "solana-program-error", + "spl-discriminator", + "spl-pod", + "thiserror 2.0.16", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +dependencies = [ + "thiserror-impl 2.0.16", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "token_metadata_parser" +version = "0.1.0" +dependencies = [ + "hex", + "solana-program", + "solana-sdk", + "spl-pod", + "spl-token-2022", + "spl-token-metadata-interface", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "uriparse" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0200d0fc04d809396c2ad43f3c95da3582a2556eba8d453c1087f4120ee352ff" +dependencies = [ + "fnv", + "lazy_static", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.106", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] diff --git a/solana/modules/token_bridge/token_metadata_parser/Cargo.toml b/solana/modules/token_bridge/token_metadata_parser/Cargo.toml new file mode 100644 index 0000000000..890158e8fa --- /dev/null +++ b/solana/modules/token_bridge/token_metadata_parser/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "token-metadata-parser" +version = "0.1.0" +edition = "2018" + +[lib] +crate-type = ["cdylib", "lib"] +name = "token_metadata_parser" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[dev-dependencies] +spl-token-2022 = "9.0" +spl-token-metadata-interface = "0.7" +spl-pod = "0.5" +solana-program = "2.1" +solana-sdk = "2.1" +hex = "0.4" diff --git a/solana/modules/token_bridge/token_metadata_parser/README.md b/solana/modules/token_bridge/token_metadata_parser/README.md new file mode 100644 index 0000000000..19c3c7788c --- /dev/null +++ b/solana/modules/token_bridge/token_metadata_parser/README.md @@ -0,0 +1,13 @@ +# What is this + +This crate is a 0-dependency parser for the token2022 [metadata pointer and metadata extensions](https://solana.com/developers/courses/token-extensions/token-extensions-metadata). + +# Why + +In order for the [token bridge program](../program) to support token2022 metadata, it needs to be able to parse the extensions out of the mint account. +The official parser is implemented in the [spl-token-2022](https://crates.io/crates/spl-token-2022) crate. That crate has a *massive* dependency tree, and is incompatible with the token bridge's dependencies. +Resolving the dependency issues would require upgrading the token bridge dependencies beyond several major versions, which is risky. Instead, we re-implement the parsing logic from scratch without any external dependencies. + +# Why is it excluded from the workspace + +In order to verify our parser works, we construct real token2022 mint accounts with the `spl-token-2022` crate. As such, this crate has a *dev* dependency on that crate. If it were included in the workspace, cargo would try to reconcile the dev dependencies with the regular dependencies of the other crates, which would defeat the purpose. diff --git a/solana/modules/token_bridge/token_metadata_parser/src/lib.rs b/solana/modules/token_bridge/token_metadata_parser/src/lib.rs new file mode 100644 index 0000000000..3b9d46aff9 --- /dev/null +++ b/solana/modules/token_bridge/token_metadata_parser/src/lib.rs @@ -0,0 +1,544 @@ +// when I say 0 dependencies, I mean 0 dependencies +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] +pub struct Pubkey(pub [u8; 32]); + +impl Pubkey { + pub fn new(bytes: [u8; 32]) -> Self { + Self(bytes) + } + pub fn from_slice(s: &[u8]) -> Option { + if s.len() == 32 { + let mut a = [0u8; 32]; + a.copy_from_slice(s); + Some(Self(a)) + } else { + None + } + } + pub fn is_zero(&self) -> bool { + self.0.iter().all(|&b| b == 0) + } +} + +#[derive(Debug, PartialEq)] +pub enum MintMetadata { + None, + External(MetadataPointer), + Embedded(TokenMetadata), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MetadataPointer { + pub authority: Option, + /// Where metadata lives. If equals the mint pubkey, metadata is embedded in the mint (TokenMetadata extension). + pub metadata_address: Pubkey, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TokenMetadata { + pub update_authority: Option, + /// Mint this metadata belongs to (usually the mint itself) + pub mint: Pubkey, + pub name: String, + pub symbol: String, + pub uri: String, + /// Arbitrary additional (key, value) pairs. + pub additional_metadata: Vec<(String, String)>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ParseError { + NotAMintAccount, // data < 82 + NotMintAccountType, // account-type byte != Mint(1) + NoEmbeddedMetadata, // pointer points to self but no embedded metadata found + UnexpectedEnd, + UnexpectedLength, + LengthMismatch, + InvalidUtf8, +} + +const BASE_MINT_LEN: usize = 82; +const BASE_MINT_LEN_V2: usize = 165; +const ACCOUNT_TYPE_OFFSET: usize = BASE_MINT_LEN; +const ACCOUNT_TYPE_OFFSET_V2: usize = BASE_MINT_LEN_V2; +const ACCOUNT_TYPE_MINT: u8 = 1; + +// TLV header sizes +const TLV_TYPE_SIZE: usize = 2; // u16 LE +const TLV_LEN_SIZE: usize = 2; // u16 LE + +// Extension type discriminants we care about: +const EXT_METADATA_POINTER: u16 = 18; +const EXT_TOKEN_METADATA: u16 = 19; +const EXT_UNINITIALIZED: u16 = 0; + +/// Given the address and contents of a mint account, this function tells us what kind of metadata the account has. +/// It supports both spl-token and spl-token-2022 account layouts. +pub fn parse_token2022_metadata( + mint_account: Pubkey, + mint_account_data: &[u8], +) -> Result { + if mint_account_data.len() < BASE_MINT_LEN { + return Err(ParseError::NotAMintAccount); + } + if mint_account_data.len() == BASE_MINT_LEN { + return Ok(MintMetadata::None); // no extensions + } + + let (account_type_offset, start_offset) = if mint_account_data.len() > BASE_MINT_LEN_V2 { + (ACCOUNT_TYPE_OFFSET_V2, ACCOUNT_TYPE_OFFSET_V2 + 1) + } else { + // Older layout + (ACCOUNT_TYPE_OFFSET, ACCOUNT_TYPE_OFFSET + 1) + }; + + // AccountType byte (present when extensions are initialized) + let acct_type = *mint_account_data + .get(account_type_offset) + .ok_or(ParseError::UnexpectedEnd)?; + if acct_type != ACCOUNT_TYPE_MINT { + return Err(ParseError::NotMintAccountType); + } + + let mut offset = start_offset; + let mut pointer: Option = None; + let mut meta: Option = None; + + while offset + TLV_TYPE_SIZE + TLV_LEN_SIZE <= mint_account_data.len() { + let t = u16::from_le_bytes([mint_account_data[offset], mint_account_data[offset + 1]]); + let l = u16::from_le_bytes([mint_account_data[offset + 2], mint_account_data[offset + 3]]); + offset += TLV_TYPE_SIZE + TLV_LEN_SIZE; + + // Bounds check for value + let end = offset + .checked_add(l as usize) + .ok_or(ParseError::UnexpectedEnd)?; + if end > mint_account_data.len() { + return Err(ParseError::UnexpectedEnd); + } + let val = &mint_account_data[offset..end]; + offset = end; + + if t == EXT_UNINITIALIZED { + break; // padding + } + + match t { + EXT_METADATA_POINTER => { + if val.len() != 64 { + return Err(ParseError::UnexpectedLength); + } + let authority = { + let a = Pubkey::from_slice(&val[0..32]).unwrap(); + if a.is_zero() { + None + } else { + Some(a) + } + }; + let metadata_address = Pubkey::from_slice(&val[32..64]).unwrap(); + pointer = Some(MetadataPointer { + authority, + metadata_address, + }); + } + EXT_TOKEN_METADATA => { + // value layout: + // [0..32) update_authority (zero = None) + // [32..64) mint + // then: name: len(u32 LE) + bytes + // symbol: len + bytes + // uri: len + bytes + // kv_count: u32 + // kv_count times: key(len+bytes), value(len+bytes) + if val.len() < 64 { + return Err(ParseError::UnexpectedLength); + } + let update_authority = { + let a = Pubkey::from_slice(&val[0..32]).unwrap(); + if a.is_zero() { + None + } else { + Some(a) + } + }; + let mint = Pubkey::from_slice(&val[32..64]).unwrap(); + let mut cur = 64; + + fn read_u32_le(buf: &[u8], cur: &mut usize) -> Result { + if *cur + 4 > buf.len() { + return Err(ParseError::UnexpectedEnd); + } + let n = u32::from_le_bytes([ + buf[*cur], + buf[*cur + 1], + buf[*cur + 2], + buf[*cur + 3], + ]); + *cur += 4; + Ok(n) + } + fn read_string(buf: &[u8], cur: &mut usize) -> Result { + let len = read_u32_le(buf, cur)? as usize; + if *cur + len > buf.len() { + return Err(ParseError::UnexpectedEnd); + } + let s = std::str::from_utf8(&buf[*cur..*cur + len]) + .map_err(|_| ParseError::InvalidUtf8)?; + *cur += len; + Ok(s.to_owned()) + } + + let name = read_string(val, &mut cur)?; + let symbol = read_string(val, &mut cur)?; + let uri = read_string(val, &mut cur)?; + + let kv_count = read_u32_le(val, &mut cur)? as usize; + let mut additional_metadata = Vec::with_capacity(kv_count); + for _ in 0..kv_count { + let k = read_string(val, &mut cur)?; + let v = read_string(val, &mut cur)?; + additional_metadata.push((k, v)); + } + if cur != val.len() { + return Err(ParseError::LengthMismatch); + } + + meta = Some(TokenMetadata { + update_authority, + mint, + name, + symbol, + uri, + additional_metadata, + }); + } + _ => { + // Unknown extension; skip (we already advanced by its length) + } + } + } + + match pointer { + None => Ok(MintMetadata::None), + Some(ptr) => { + // Both pointer and embedded metadata exist + if ptr.metadata_address == mint_account { + if let Some(metadata) = meta { + // Pointer points to self, return embedded metadata + Ok(MintMetadata::Embedded(metadata)) + } else { + Err(ParseError::NoEmbeddedMetadata) + } + } else { + // Pointer points elsewhere, return external pointer + // NOTE: metadata may still be Some, but we ignore it (it's valid to have embedded metadata that's ignored) + Ok(MintMetadata::External(ptr)) + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use solana_program::{ + program_option::COption, + pubkey::Pubkey as SolanaPubkey, + }; + use spl_pod::optional_keys::OptionalNonZeroPubkey; + use spl_token_2022::{ + extension::{ + metadata_pointer::MetadataPointer, + transfer_fee::TransferFeeConfig, + BaseStateWithExtensionsMut, + ExtensionType, + StateWithExtensionsMut, + }, + state::Mint, + }; + use spl_token_metadata_interface::state::TokenMetadata; + use std::str::FromStr; + + fn convert_pubkey(pk: SolanaPubkey) -> Pubkey { + Pubkey::new(pk.to_bytes()) + } + + #[test] + fn test_mint_with_metadata_pointer_and_metadata() { + let mint_address = SolanaPubkey::new_unique(); + let authority = SolanaPubkey::new_unique(); + + // Create TokenMetadata + let token_metadata = TokenMetadata { + update_authority: OptionalNonZeroPubkey(authority), + mint: mint_address, + name: "Test Token".to_string(), + symbol: "TEST".to_string(), + uri: "https://example.com/token.json".to_string(), + additional_metadata: vec![], + }; + + // Calculate account size needed for both extensions + let extension_types = vec![ExtensionType::MetadataPointer]; + let mut account_size = + ExtensionType::try_calculate_account_len::(&extension_types).unwrap(); + // Add space for variable-length TokenMetadata + account_size += token_metadata.tlv_size_of().unwrap(); + + // Create account data buffer + let mut mint_data = vec![0; account_size]; + + // Initialize the mint account with extensions + let mut state = + StateWithExtensionsMut::::unpack_uninitialized(&mut mint_data).unwrap(); + + // Set up base mint data + state.base = Mint { + mint_authority: COption::Some(authority), + supply: 1000000, + decimals: 6, + is_initialized: true, + freeze_authority: COption::None, + }; + + // Initialize MetadataPointer extension (pointing to self) + let metadata_pointer_extension = state.init_extension::(true).unwrap(); + metadata_pointer_extension.authority = OptionalNonZeroPubkey(authority); + metadata_pointer_extension.metadata_address = OptionalNonZeroPubkey(mint_address); + + // Initialize TokenMetadata as a variable-length extension + state + .init_variable_len_extension(&token_metadata, true) + .unwrap(); + + // Pack the base state and initialize account type + state.pack_base(); + state.init_account_type().unwrap(); + + // Now test our parser against this properly serialized SPL Token-2022 data + let result = parse_token2022_metadata(convert_pubkey(mint_address), &mint_data).unwrap(); + + // Since metadata pointer points to self, we should get embedded metadata + match &result { + MintMetadata::Embedded(token_metadata) => { + assert_eq!( + token_metadata.update_authority, + Some(convert_pubkey(authority)) + ); + assert_eq!(token_metadata.mint, convert_pubkey(mint_address)); + assert_eq!(token_metadata.name, "Test Token"); + assert_eq!(token_metadata.symbol, "TEST"); + assert_eq!(token_metadata.uri, "https://example.com/token.json"); + assert_eq!(token_metadata.additional_metadata.len(), 0); + } + _ => panic!("Expected embedded metadata"), + } + } + + #[test] + fn test_basic_mint_no_extensions() { + // Test basic 82-byte mint with no extensions + let data = [0u8; 82]; + let dummy_mint = Pubkey::new([0u8; 32]); + + let result = parse_token2022_metadata(dummy_mint, &data).unwrap(); + match result { + MintMetadata::None => {} // Expected + _ => panic!("Expected no metadata"), + } + } + + #[test] + fn test_error_cases() { + let dummy_mint = Pubkey::new([0u8; 32]); + + // Test too short data + let short_data = vec![0u8; 50]; + assert_eq!( + parse_token2022_metadata(dummy_mint, &short_data), + Err(ParseError::NotAMintAccount) + ); + + // Test wrong account type + let mut wrong_type_data = vec![0u8; 100]; + wrong_type_data[82] = 2; // Wrong account type (should be 1 for Mint) + assert_eq!( + parse_token2022_metadata(dummy_mint, &wrong_type_data), + Err(ParseError::NotMintAccountType) + ); + } + + #[test] + fn test_mint_with_multiple_extensions() { + let mint_address = SolanaPubkey::new_unique(); + let authority = SolanaPubkey::new_unique(); + + // Create TokenMetadata + let token_metadata = TokenMetadata { + update_authority: OptionalNonZeroPubkey(authority), + mint: mint_address, + name: "Multi-Extension Token".to_string(), + symbol: "MULTI".to_string(), + uri: "https://example.com/multi.json".to_string(), + additional_metadata: vec![ + ("category".to_string(), "test".to_string()), + ("version".to_string(), "1.0".to_string()), + ], + }; + + // Calculate account size needed for multiple extensions + let extension_types = vec![ + ExtensionType::MetadataPointer, + ExtensionType::TransferFeeConfig, + ]; + let mut account_size = + ExtensionType::try_calculate_account_len::(&extension_types).unwrap(); + // Add space for variable-length TokenMetadata + account_size += token_metadata.tlv_size_of().unwrap(); + + // Create account data buffer + let mut mint_data = vec![0; account_size]; + + // Initialize the mint account with extensions + let mut state = + StateWithExtensionsMut::::unpack_uninitialized(&mut mint_data).unwrap(); + + // Set up base mint data + state.base = Mint { + mint_authority: COption::Some(authority), + supply: 5000000, + decimals: 9, + is_initialized: true, + freeze_authority: COption::Some(authority), + }; + + // Initialize TransferFeeConfig extension (unrelated to metadata) + let transfer_fee_extension = state.init_extension::(true).unwrap(); + transfer_fee_extension.transfer_fee_config_authority = OptionalNonZeroPubkey(authority); + transfer_fee_extension.withdraw_withheld_authority = OptionalNonZeroPubkey(authority); + transfer_fee_extension.withheld_amount = 0.into(); + transfer_fee_extension + .older_transfer_fee + .transfer_fee_basis_points = 50.into(); // 0.5% + transfer_fee_extension.older_transfer_fee.maximum_fee = 1000000.into(); // 1 token + transfer_fee_extension + .newer_transfer_fee + .transfer_fee_basis_points = 25.into(); // 0.25% + transfer_fee_extension.newer_transfer_fee.maximum_fee = 500000.into(); // 0.5 token + + // Initialize MetadataPointer extension (pointing to self) + let metadata_pointer_extension = state.init_extension::(true).unwrap(); + metadata_pointer_extension.authority = OptionalNonZeroPubkey(authority); + metadata_pointer_extension.metadata_address = OptionalNonZeroPubkey(mint_address); + + // Initialize TokenMetadata as a variable-length extension + state + .init_variable_len_extension(&token_metadata, true) + .unwrap(); + + // Pack the base state and initialize account type + state.pack_base(); + state.init_account_type().unwrap(); + + // Now test our parser against this multi-extension data + let result = parse_token2022_metadata(convert_pubkey(mint_address), &mint_data).unwrap(); + + // Since metadata pointer points to self, we should get embedded metadata (should work despite other extensions) + match &result { + MintMetadata::Embedded(token_metadata) => { + assert_eq!( + token_metadata.update_authority, + Some(convert_pubkey(authority)) + ); + assert_eq!(token_metadata.mint, convert_pubkey(mint_address)); + assert_eq!(token_metadata.name, "Multi-Extension Token"); + assert_eq!(token_metadata.symbol, "MULTI"); + assert_eq!(token_metadata.uri, "https://example.com/multi.json"); + assert_eq!(token_metadata.additional_metadata.len(), 2); + assert_eq!( + token_metadata.additional_metadata[0], + ("category".to_string(), "test".to_string()) + ); + assert_eq!( + token_metadata.additional_metadata[1], + ("version".to_string(), "1.0".to_string()) + ); + } + _ => panic!("Expected embedded metadata despite multiple extensions"), + } + } + + #[test] + fn test_real_world_pyusd_mint_data() { + // Real-world PYUSD mint account data from mainnet: 2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo + // This mint has both a metadata pointer and embedded TokenMetadata + let hex_data = "01000000dd4c486c90f8b6f007c304ef2481f805186be8fd5f52acd1025cb79b9f67ff216c9d1390e1cd000006010100000017853261ef6ab8532a67f053865aad31293fcf07cf120ab5b9a15706548dc02b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010300200017853261ef6ab8532a67f053865aad31293fcf07cf120ab5b9a15706548dc02b0c00200017853261ef6ab8532a67f053865aad31293fcf07cf120ab5b9a15706548dc02b01006c0017853261ef6ab8532a67f053865aad31293fcf07cf120ab5b9a15706548dc02b17853261ef6ab8532a67f053865aad31293fcf07cf120ab5b9a15706548dc02b00000000000000005d02000000000000000000000000000000005d02000000000000000000000000000000000400410017853261ef6ab8532a67f053865aad31293fcf07cf120ab5b9a15706548dc02b0000000000000000000000000000000000000000000000000000000000000000001000810017853261ef6ab8532a67f053865aad31293fcf07cf120ab5b9a15706548dc02b1c37e6433b7304dd82737ae40d9b8bf3c49f5b0e6c49a8d53328b3e506901c5701000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00400017853261ef6ab8532a67f053865aad31293fcf07cf120ab5b9a15706548dc02b00000000000000000000000000000000000000000000000000000000000000001200400017853261ef6ab8532a67f053865aad31293fcf07cf120ab5b9a15706548dc02b1792483b6c8a2a87b7471d814f9591f9395c840a9ce3d9f4d5ba7d3a4b8a749e1300ae0017853261ef6ab8532a67f053865aad31293fcf07cf120ab5b9a15706548dc02b1792483b6c8a2a87b7471d814f9591f9395c840a9ce3d9f4d5ba7d3a4b8a749e0a00000050617950616c205553440500000050595553444f00000068747470733a2f2f746f6b656e2d6d657461646174612e7061786f732e636f6d2f70797573645f6d657461646174612f70726f642f736f6c616e612f70797573645f6d657461646174612e6a736f6e00000000"; + + let raw_data = hex::decode(hex_data).expect("Valid hex string"); + + let pyusd_mint = convert_pubkey( + SolanaPubkey::from_str("2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo").unwrap(), + ); + + // Parse using our custom parser + let result = parse_token2022_metadata(pyusd_mint, &raw_data).unwrap(); + + let metadata_address = convert_pubkey( + SolanaPubkey::from_str("2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo").unwrap(), + ); + + match &result { + MintMetadata::Embedded(token_metadata) => { + // PYUSD has embedded metadata (metadata pointer points to self) + assert_eq!(token_metadata.mint, metadata_address); + assert_eq!(token_metadata.name, "PayPal USD"); + assert_eq!(token_metadata.symbol, "PYUSD"); + assert_eq!( + token_metadata.uri, + "https://token-metadata.paxos.com/pyusd_metadata/prod/solana/pyusd_metadata.json" + ); + assert_eq!(token_metadata.additional_metadata.len(), 0); + } + _ => panic!("Expected embedded metadata for PYUSD (pointer points to mint)"), + } + } + + #[test] + fn test_real_world_usdc_mint_data() { + // Real-world USDC mint account data from mainnet: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v + // This is a regular spl token. We want to make sure we can parse it still. + let hex_data = "0100000098fe86e88d9be2ea8bc1cca4878b2988c240f52b8424bfb40ed1a2ddcb5e199b3e8ef2b0d02020000601010000006270aa8a59c59405b45286c86772e6cd126e9b8a5d3a38536d37f7b414e8b667"; + + let raw_data = hex::decode(hex_data).expect("Valid hex string"); + let dummy_mint = Pubkey::new([0u8; 32]); // USDC mint address not needed for this test + + // Parse using our custom parser + let result = parse_token2022_metadata(dummy_mint, &raw_data).unwrap(); + + // Should return None since this is a basic SPL token with no extensions + match result { + MintMetadata::None => {} // Expected + _ => panic!("Expected no metadata for basic SPL token"), + } + } + + #[test] + fn test_real_world_wsol_token2022_mint_data() { + // This is a token2022 token with no extensions: 9pan9bMn5HatX4EJdBwg9VgCa7Uz5HL8N1m5D3NdXejP + // We make sure we can parse its mint account the same + let hex_data = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000901000000000000000000000000000000000000000000000000000000000000000000000000"; + + let raw_data = hex::decode(hex_data).expect("Valid hex string"); + let dummy_mint = Pubkey::new([0u8; 32]); // Mint address not needed for this test + + // Parse using our custom parser + let result = parse_token2022_metadata(dummy_mint, &raw_data).unwrap(); + + // Should return None since this Token-2022 mint has no extensions + match result { + MintMetadata::None => {} // Expected + _ => panic!("Expected no metadata for Token-2022 mint with no extensions"), + } + } +} From d708cd13e845a8c6cb6ce913b2b30e2c311bd6ae Mon Sep 17 00:00:00 2001 From: Csongor Kiss Date: Wed, 27 Aug 2025 20:52:02 +0100 Subject: [PATCH 6/9] solana/token_bridge: add support for token2022 metadata --- solana/Cargo.lock | 5 + .../modules/token_bridge/program/Cargo.toml | 1 + .../token_bridge/program/src/accounts.rs | 109 ++++++++++++++++-- .../token_bridge/program/src/api/attest.rs | 3 +- .../program/src/api/create_wrapped.rs | 1 + .../modules/token_bridge/program/src/lib.rs | 1 + 6 files changed, 111 insertions(+), 9 deletions(-) diff --git a/solana/Cargo.lock b/solana/Cargo.lock index 654949defe..a61b5aceb3 100644 --- a/solana/Cargo.lock +++ b/solana/Cargo.lock @@ -4283,10 +4283,15 @@ dependencies = [ "solana-sdk", "solitaire", "spl-token", + "token-metadata-parser", "wasm-bindgen", "wormhole-bridge-solana", ] +[[package]] +name = "token-metadata-parser" +version = "0.1.0" + [[package]] name = "token_bridge_client" version = "0.1.0" diff --git a/solana/modules/token_bridge/program/Cargo.toml b/solana/modules/token_bridge/program/Cargo.toml index 2fca2b8e9a..5d13aba620 100644 --- a/solana/modules/token_bridge/program/Cargo.toml +++ b/solana/modules/token_bridge/program/Cargo.toml @@ -19,6 +19,7 @@ instructions = [] [dependencies] wormhole-bridge-solana = { path = "../../../bridge/program", features = ["no-entrypoint", "cpi"] } +token-metadata-parser = { path = "../token_metadata_parser" } borsh = "=0.9.3" bstr = "0.2.16" byteorder = "1.4.3" diff --git a/solana/modules/token_bridge/program/src/accounts.rs b/solana/modules/token_bridge/program/src/accounts.rs index 1069e24f0d..2a30d083d5 100644 --- a/solana/modules/token_bridge/program/src/accounts.rs +++ b/solana/modules/token_bridge/program/src/accounts.rs @@ -90,6 +90,9 @@ impl<'b, const STATE: AccountState> Seeded<&EndpointDerivationData> for Endpoint } } +/// Metadata account for the token. This used to be exclusively the Metaplex +/// metadata account, but with token2022's metadata pointer extension, this may be any account. +/// `deserialize_and_verify_metadata` verifies that this account is what the token specifies (or falls back to Metaplex). pub type SplTokenMeta<'b> = Info<'b>; pub struct SplTokenMetaDerivationData { @@ -109,35 +112,125 @@ impl<'b> Seeded<&SplTokenMetaDerivationData> for SplTokenMeta<'b> { //New data length of spl token metadata account pub const NEW_MAX_METADATA_LEN: usize = 607; +/// Converts Token-2022 metadata to Metaplex metadata format for compatibility +fn convert_token2022_to_metaplex_metadata( + token_metadata: &token_metadata_parser::TokenMetadata, +) -> spl_token_metadata::state::Metadata { + use spl_token_metadata::state::{ + Data, + Key, + Metadata, + }; + + let data = Data { + name: token_metadata.name.clone(), + symbol: token_metadata.symbol.clone(), + uri: token_metadata.uri.clone(), + seller_fee_basis_points: 0, // Token-2022 doesn't have this concept + creators: None, // Token-2022 doesn't have creators + }; + + Metadata { + key: Key::MetadataV1, + update_authority: token_metadata + .update_authority + .map(|pubkey| Pubkey::new(&pubkey.0)) + .unwrap_or_default(), + mint: Pubkey::new(&token_metadata.mint.0), + data, + primary_sale_happened: false, // Default for Token-2022 + is_mutable: token_metadata.update_authority.is_some(), + edition_nonce: None, + token_standard: None, + collection: None, + uses: None, + collection_details: None, + programmable_config: None, + } +} + /// This method removes code duplication when checking token metadata. When metadata is read for /// attestation and transfers, Token Bridge does not invoke Metaplex's Token Metadata program, so /// it must validate the account the same way Token Metadata program does to ensure the correct /// account is passed into Token Bridge's instruction context. pub fn deserialize_and_verify_metadata( - info: &Info, + mint: &Info, + metadata: &Info, derivation_data: SplTokenMetaDerivationData, ) -> Result { - // Verify pda. - info.verify_derivation(&spl_token_metadata::id(), &derivation_data)?; + let mint_metadata = token_metadata_parser::parse_token2022_metadata( + token_metadata_parser::Pubkey::new(mint.key.to_bytes()), + &mint.data.borrow(), + ) + .map_err(|_| TokenBridgeError::InvalidMetadata)?; + + // we constrain the `metadata` account in every case. + // 1. if mint is token-2022 with embedded metadata, we return that metadata (in this case, `metadata` == `mint`. `token_metadata_parser` ensures this) + // 2. if mint is token-2022 with external metadata pointer, we verify `metadata` matches the pointer + // a. if `metadata` is owned by spl-token-metadata, we verify the pda and deserialise it as standard Metaplex metadata + // b. if `metadata` is not owned by spl-token-metadata, we don't verify that it's a pda (we know it matches the pointer already) + // 3. if mint doesn't include a metadata pointer, we ensure `metadata` is the metaplex pda. + // this is the legacy case, but it applies to token2022 tokens as well (that have no metadata pointer extension) + // + // Note that in every case other than 1 (which is a well-defined spec via + // the token metadata extension), we parse the `metadata` account following + // the standard metaplex format. + // + // In case of 2b, this is a best-effort guess, because the metadata pointer + // extension makes no guarantees about the shape of the metadata account. However, a common practice is to just follow the metaplex format. + // What this means is that if the metadata account is not owned by the metaplex program, and is not in the metaplex format, the deserialisation will fail. + // We just don't support these tokens. + + match mint_metadata { + // 1. + token_metadata_parser::MintMetadata::Embedded(token_metadata) => { + // token-2022 mint with embedded metadata + return Ok(convert_token2022_to_metaplex_metadata(&token_metadata)); + } + // 2. + token_metadata_parser::MintMetadata::External(pointer) => { + if pointer.metadata_address + != token_metadata_parser::Pubkey::new(metadata.key.to_bytes()) + { + return Err(TokenBridgeError::WrongMetadataAccount.into()); + } + + // 2a. + if *metadata.owner == spl_token_metadata::id() { + // Standard Metaplex metadata verification and parsing + // Verify pda. + metadata.verify_derivation(&spl_token_metadata::id(), &derivation_data)?; + // 2b. + } else { + // fall through + } + } + // 3. + token_metadata_parser::MintMetadata::None => { + // Standard Metaplex metadata verification and parsing + // Verify pda. + metadata.verify_derivation(&spl_token_metadata::id(), &derivation_data)?; + } + } // There must be account data for token's metadata. - if info.data_is_empty() { + if metadata.data_is_empty() { return Err(TokenBridgeError::NonexistentTokenMetadataAccount.into()); } // Account must belong to Metaplex Token Metadata program. - if *info.owner != spl_token_metadata::id() { + if *metadata.owner != spl_token_metadata::id() { return Err(TokenBridgeError::WrongAccountOwner.into()); } // Account must be the expected Metadata length. - if info.data_len() != spl_token_metadata::state::MAX_METADATA_LEN - && info.data_len() != NEW_MAX_METADATA_LEN + if metadata.data_len() != spl_token_metadata::state::MAX_METADATA_LEN + && metadata.data_len() != NEW_MAX_METADATA_LEN { return Err(TokenBridgeError::InvalidMetadata.into()); } - let mut data: &[u8] = &info.data.borrow_mut(); + let mut data: &[u8] = &metadata.data.borrow_mut(); // Unfortunately we cannot use `map_err` easily, so we will match certain deserialization conditions. match spl_token_metadata::utils::meta_deser_unchecked(&mut data) { diff --git a/solana/modules/token_bridge/program/src/api/attest.rs b/solana/modules/token_bridge/program/src/api/attest.rs index 319e0fca85..e4afc38a26 100644 --- a/solana/modules/token_bridge/program/src/api/attest.rs +++ b/solana/modules/token_bridge/program/src/api/attest.rs @@ -117,7 +117,8 @@ pub fn attest_token( // Assign metadata if an SPL Metadata account exists for the SPL token in question. if !accs.spl_metadata.data_is_empty() { - let metadata = deserialize_and_verify_metadata(&accs.spl_metadata, (&*accs).into())?; + let metadata = + deserialize_and_verify_metadata(accs.mint.info(), &accs.spl_metadata, (&*accs).into())?; payload.name = metadata.data.name.clone(); payload.symbol = metadata.data.symbol; } diff --git a/solana/modules/token_bridge/program/src/api/create_wrapped.rs b/solana/modules/token_bridge/program/src/api/create_wrapped.rs index 561220875b..a459efd5ee 100644 --- a/solana/modules/token_bridge/program/src/api/create_wrapped.rs +++ b/solana/modules/token_bridge/program/src/api/create_wrapped.rs @@ -205,6 +205,7 @@ pub fn update_accounts( // Checks in this method are redundant with what occurs in `update_metadata_accounts_v2`, but we want to make // sure that the account we are deserializing is legitimate. let metadata = deserialize_and_verify_metadata( + accs.mint.info(), &accs.spl_metadata, SplTokenMetaDerivationData { mint: *accs.mint.info().key, diff --git a/solana/modules/token_bridge/program/src/lib.rs b/solana/modules/token_bridge/program/src/lib.rs index 6ccb82f5de..4c00575502 100644 --- a/solana/modules/token_bridge/program/src/lib.rs +++ b/solana/modules/token_bridge/program/src/lib.rs @@ -91,6 +91,7 @@ pub enum TokenBridgeError { InvalidVAA, NonexistentTokenMetadataAccount, NotMetadataV1Account, + WrongMetadataAccount, } impl From for SolitaireError { From d5da6e0d1616b308eb5fdb12c02e1935977ccbbc Mon Sep 17 00:00:00 2001 From: Csongor Kiss Date: Wed, 10 Sep 2025 16:43:24 +0100 Subject: [PATCH 7/9] solana/token_bridge: factor out metadata parsing --- .../token_bridge/program/src/accounts.rs | 32 +++++++++++-------- .../token_bridge/program/src/api/attest.rs | 6 ++-- .../program/src/api/create_wrapped.rs | 3 +- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/solana/modules/token_bridge/program/src/accounts.rs b/solana/modules/token_bridge/program/src/accounts.rs index 2a30d083d5..141da8264c 100644 --- a/solana/modules/token_bridge/program/src/accounts.rs +++ b/solana/modules/token_bridge/program/src/accounts.rs @@ -157,7 +157,7 @@ pub fn deserialize_and_verify_metadata( mint: &Info, metadata: &Info, derivation_data: SplTokenMetaDerivationData, -) -> Result { +) -> Result> { let mint_metadata = token_metadata_parser::parse_token2022_metadata( token_metadata_parser::Pubkey::new(mint.key.to_bytes()), &mint.data.borrow(), @@ -185,7 +185,9 @@ pub fn deserialize_and_verify_metadata( // 1. token_metadata_parser::MintMetadata::Embedded(token_metadata) => { // token-2022 mint with embedded metadata - return Ok(convert_token2022_to_metaplex_metadata(&token_metadata)); + Ok(Some(convert_token2022_to_metaplex_metadata( + &token_metadata, + ))) } // 2. token_metadata_parser::MintMetadata::External(pointer) => { @@ -200,27 +202,31 @@ pub fn deserialize_and_verify_metadata( // Standard Metaplex metadata verification and parsing // Verify pda. metadata.verify_derivation(&spl_token_metadata::id(), &derivation_data)?; - // 2b. - } else { - // fall through } + deserialize_metaplex_formatted_metadata_account(metadata) } // 3. token_metadata_parser::MintMetadata::None => { + // Account must belong to Metaplex Token Metadata program. + if *metadata.owner != Pubkey::default() && *metadata.owner != spl_token_metadata::id() { + return Err(TokenBridgeError::WrongAccountOwner.into()); + } // Standard Metaplex metadata verification and parsing // Verify pda. metadata.verify_derivation(&spl_token_metadata::id(), &derivation_data)?; + deserialize_metaplex_formatted_metadata_account(metadata) } } +} - // There must be account data for token's metadata. +/// Deserialises a Metaplex formatted metadata account. Here we assume that the +/// metadata account has been checked to be the correct account for the mint. +/// As such, if it's empty, it just means there is no metadata. +fn deserialize_metaplex_formatted_metadata_account( + metadata: &Info, +) -> Result> { if metadata.data_is_empty() { - return Err(TokenBridgeError::NonexistentTokenMetadataAccount.into()); - } - - // Account must belong to Metaplex Token Metadata program. - if *metadata.owner != spl_token_metadata::id() { - return Err(TokenBridgeError::WrongAccountOwner.into()); + return Ok(None); } // Account must be the expected Metadata length. @@ -236,7 +242,7 @@ pub fn deserialize_and_verify_metadata( match spl_token_metadata::utils::meta_deser_unchecked(&mut data) { Ok(deserialized) => { if deserialized.key == MetadataV1 { - Ok(deserialized) + Ok(Some(deserialized)) } else { Err(TokenBridgeError::NotMetadataV1Account.into()) } diff --git a/solana/modules/token_bridge/program/src/api/attest.rs b/solana/modules/token_bridge/program/src/api/attest.rs index e4afc38a26..b3292dd41a 100644 --- a/solana/modules/token_bridge/program/src/api/attest.rs +++ b/solana/modules/token_bridge/program/src/api/attest.rs @@ -116,9 +116,9 @@ pub fn attest_token( }; // Assign metadata if an SPL Metadata account exists for the SPL token in question. - if !accs.spl_metadata.data_is_empty() { - let metadata = - deserialize_and_verify_metadata(accs.mint.info(), &accs.spl_metadata, (&*accs).into())?; + if let Some(metadata) = + deserialize_and_verify_metadata(accs.mint.info(), &accs.spl_metadata, (&*accs).into())? + { payload.name = metadata.data.name.clone(); payload.symbol = metadata.data.symbol; } diff --git a/solana/modules/token_bridge/program/src/api/create_wrapped.rs b/solana/modules/token_bridge/program/src/api/create_wrapped.rs index a459efd5ee..29a11a552d 100644 --- a/solana/modules/token_bridge/program/src/api/create_wrapped.rs +++ b/solana/modules/token_bridge/program/src/api/create_wrapped.rs @@ -210,7 +210,8 @@ pub fn update_accounts( SplTokenMetaDerivationData { mint: *accs.mint.info().key, }, - )?; + )? + .unwrap(); // Normalize token metadata's name and symbol. let new_data_v2 = spl_token_metadata::state::DataV2 { From 8d4ac4ea7aceee184107ca5106b767d8b7a24776 Mon Sep 17 00:00:00 2001 From: Csongor Kiss Date: Mon, 22 Sep 2025 18:30:47 +0200 Subject: [PATCH 8/9] solana/token_bridge: relax length requirement in unpacking token2022 mints to allow for extensions --- .../modules/token_bridge/program/src/types.rs | 96 ++++++++++++++++++- solana/solitaire/program/src/macros.rs | 31 +++++- 2 files changed, 121 insertions(+), 6 deletions(-) diff --git a/solana/modules/token_bridge/program/src/types.rs b/solana/modules/token_bridge/program/src/types.rs index 8219f571ba..715315561f 100644 --- a/solana/modules/token_bridge/program/src/types.rs +++ b/solana/modules/token_bridge/program/src/types.rs @@ -6,7 +6,14 @@ use serde::{ Deserialize, Serialize, }; -use solana_program::pubkey::Pubkey; +use solana_program::{ + program_error::ProgramError, + program_pack::{ + IsInitialized, + Pack, + }, + pubkey::Pubkey, +}; use solitaire::{ pack_type, processors::seeded::{ @@ -101,13 +108,98 @@ pub mod spl_token_2022 { } } +trait MintExtensionPack: Pack { + fn unpack(input: &[u8]) -> Result; +} + +// from: https://github.com/solana-program/token-2022/blob/9cbf7a1e9bab57aabb71b4b84fc84e3670108573/interface/src/extension/mod.rs#L262-L268 +// Since there is no account discriminator in these accounts, it's possible to confuse a multisig account for a mint account. +// This check prevents that by ensuring the length is not equal to a multisig account length. +// Mint accounts that happen to be 355 bytes long are out of luck (but this won't concern us, if it's even possible). +fn check_min_len_and_not_multisig(input: &[u8], minimum_len: usize) -> Result<(), ProgramError> { + const MULTISIG_LEN: usize = 355; // spl_token::state::Multisig::LEN; + if input.len() == MULTISIG_LEN || input.len() < minimum_len { + Err(ProgramError::InvalidAccountData) + } else { + Ok(()) + } +} + +impl MintExtensionPack for Mint { + // this implementation is almost identical to the default Pack::unpack, + // except for the length check. Instead of requiring exact length, we require + // a minimum length, to allow for extensions. + fn unpack(input: &[u8]) -> Result { + check_min_len_and_not_multisig(input, Self::LEN)?; + let value: Mint = solana_program::program_pack::Pack::unpack_from_slice(input)?; + if value.is_initialized() { + Ok(value) + } else { + Err(ProgramError::UninitializedAccount) + } + } +} + pack_type!( SplMint, Mint, - AccountOwner::OneOf(vec![spl_token::id(), spl_token_2022::id()]) + AccountOwner::OneOf(vec![spl_token::id(), spl_token_2022::id()]), + MintExtensionPack ); pack_type!( SplAccount, Account, AccountOwner::OneOf(vec![spl_token::id(), spl_token_2022::id()]) ); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_mint_unpack_from_slice_old_token() { + let src: [u8; 82] = [ + 0x01, 0x00, 0x00, 0x00, 0x64, 0xf1, 0x33, 0x5f, 0xe8, 0x35, 0x98, 0x99, 0x99, 0xfb, + 0xd2, 0x84, 0x35, 0xc9, 0x0b, 0x89, 0x47, 0xfb, 0x25, 0x8f, 0x7a, 0xea, 0xcb, 0x19, + 0xc8, 0x8f, 0x9b, 0x09, 0x7a, 0xe2, 0xc7, 0xe7, 0x00, 0xc0, 0x57, 0x73, 0xa5, 0x7c, + 0x02, 0x00, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + let mint = ::unpack(&src).unwrap(); + assert!(mint.is_initialized); + } + + #[test] + fn test_mint_unpack_from_slice_new_token() { + let src: [u8; 344] = [ + 0x01, 0x00, 0x00, 0x00, 0x67, 0x94, 0x7e, 0xf1, 0x3a, 0x15, 0x8c, 0xb9, 0xbf, 0xca, + 0xbe, 0xa0, 0x18, 0xb3, 0xf8, 0xd2, 0xe5, 0x5b, 0x22, 0x81, 0xa7, 0x63, 0x62, 0x62, + 0x42, 0x73, 0x97, 0x1d, 0xba, 0xfa, 0x1e, 0x99, 0x00, 0xe8, 0x76, 0x48, 0x17, 0x00, + 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x12, 0x00, + 0x40, 0x00, 0x67, 0x94, 0x7e, 0xf1, 0x3a, 0x15, 0x8c, 0xb9, 0xbf, 0xca, 0xbe, 0xa0, + 0x18, 0xb3, 0xf8, 0xd2, 0xe5, 0x5b, 0x22, 0x81, 0xa7, 0x63, 0x62, 0x62, 0x42, 0x73, + 0x97, 0x1d, 0xba, 0xfa, 0x1e, 0x99, 0x94, 0xf5, 0xf0, 0x2e, 0xe1, 0x66, 0xcd, 0x5e, + 0x14, 0xa4, 0x1e, 0x22, 0xeb, 0x6d, 0x93, 0xda, 0x79, 0xd7, 0x50, 0xda, 0x9d, 0xbc, + 0xa4, 0x84, 0xf1, 0x88, 0x3d, 0x47, 0x55, 0xbc, 0x6f, 0x5b, 0x13, 0x00, 0x6a, 0x00, + 0x67, 0x94, 0x7e, 0xf1, 0x3a, 0x15, 0x8c, 0xb9, 0xbf, 0xca, 0xbe, 0xa0, 0x18, 0xb3, + 0xf8, 0xd2, 0xe5, 0x5b, 0x22, 0x81, 0xa7, 0x63, 0x62, 0x62, 0x42, 0x73, 0x97, 0x1d, + 0xba, 0xfa, 0x1e, 0x99, 0x94, 0xf5, 0xf0, 0x2e, 0xe1, 0x66, 0xcd, 0x5e, 0x14, 0xa4, + 0x1e, 0x22, 0xeb, 0x6d, 0x93, 0xda, 0x79, 0xd7, 0x50, 0xda, 0x9d, 0xbc, 0xa4, 0x84, + 0xf1, 0x88, 0x3d, 0x47, 0x55, 0xbc, 0x6f, 0x5b, 0x04, 0x00, 0x00, 0x00, 0x54, 0x65, + 0x73, 0x74, 0x04, 0x00, 0x00, 0x00, 0x54, 0x65, 0x73, 0x74, 0x12, 0x00, 0x00, 0x00, + 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, + ]; + let mint = ::unpack(&src).unwrap(); + assert!(mint.is_initialized); + } +} diff --git a/solana/solitaire/program/src/macros.rs b/solana/solitaire/program/src/macros.rs index a98db2249d..8d2a3e2c8b 100644 --- a/solana/solitaire/program/src/macros.rs +++ b/solana/solitaire/program/src/macros.rs @@ -111,14 +111,26 @@ macro_rules! solitaire { #[macro_export] macro_rules! pack_type_impl { - ($name:ident, $embed:ty, $owner:expr) => { + // We take a "unpacker" as an input, which specifies how to unpack the embedded type. + // In most cases, this should be just be + // `solana_program::program_pack::Pack`, but in some cases (like token-2022 + // mints) it may be a custom trait that provides an `unpack` method. This is + // because `Pack` does a strict length check on the account, whereas + // token-2022 mints with extensions might be longer. + // + // NOTE: we only use this on the deserialisation side, but we keep the call for serialisation + // as solana_program::program_pack::Pack::pack_into_slice. We could generalise that side too, but in + // reality, that code is never invoked, because solitaire will persist (and + // thus serialise) accounts that are owned by the current program. + // `pack_type!` on the other hands is only used for solitaire-ising external accounts. + ($name:ident, $embed:ty, $owner:expr, $unpacker:path) => { #[repr(transparent)] pub struct $name(pub $embed); impl BorshDeserialize for $name { fn deserialize(buf: &mut &[u8]) -> std::io::Result { let acc = $name( - solana_program::program_pack::Pack::unpack(buf) + <$embed as $unpacker>::unpack(buf) .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?, ); // We need to clear the buf to show to Borsh that we've read all data @@ -162,15 +174,26 @@ macro_rules! pack_type_impl { #[macro_export] macro_rules! pack_type { ($name:ident, $embed:ty, AccountOwner::OneOf($owner:expr)) => { - solitaire::pack_type_impl!($name, $embed, AccountOwner::OneOf($owner)); + solitaire::pack_type_impl!( + $name, + $embed, + AccountOwner::OneOf($owner), + solana_program::program_pack::Pack + ); impl solitaire::processors::seeded::MultiOwned for $name { } }; ($name:ident, $embed:ty, $owner:expr) => { - solitaire::pack_type_impl!($name, $embed, $owner); + solitaire::pack_type_impl!($name, $embed, $owner, solana_program::program_pack::Pack); impl solitaire::processors::seeded::SingleOwned for $name { } }; + ($name:ident, $embed:ty, AccountOwner::OneOf($owner:expr), $unpacker:ident) => { + solitaire::pack_type_impl!($name, $embed, AccountOwner::OneOf($owner), $unpacker); + + impl solitaire::processors::seeded::MultiOwned for $name { + } + }; } From dd693c44be0ce46c9809c99527482740eef6784e Mon Sep 17 00:00:00 2001 From: Csongor Kiss Date: Thu, 25 Sep 2025 13:01:07 +0200 Subject: [PATCH 9/9] solana/token_bridge: mint account can't be between 82..=165 bytes long --- .../token_metadata_parser/Cargo.lock | 32 +++- .../token_metadata_parser/Cargo.toml | 1 + .../token_metadata_parser/src/lib.rs | 145 ++++++++++++++++-- 3 files changed, 162 insertions(+), 16 deletions(-) diff --git a/solana/modules/token_bridge/token_metadata_parser/Cargo.lock b/solana/modules/token_bridge/token_metadata_parser/Cargo.lock index e6c753d4ab..a79a6144b0 100644 --- a/solana/modules/token_bridge/token_metadata_parser/Cargo.lock +++ b/solana/modules/token_bridge/token_metadata_parser/Cargo.lock @@ -1291,6 +1291,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -1311,6 +1321,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + [[package]] name = "rand_core" version = "0.5.1" @@ -1329,6 +1349,15 @@ dependencies = [ "getrandom 0.2.16", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + [[package]] name = "rand_hc" version = "0.2.0" @@ -3471,10 +3500,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] -name = "token_metadata_parser" +name = "token-metadata-parser" version = "0.1.0" dependencies = [ "hex", + "rand 0.9.2", "solana-program", "solana-sdk", "spl-pod", diff --git a/solana/modules/token_bridge/token_metadata_parser/Cargo.toml b/solana/modules/token_bridge/token_metadata_parser/Cargo.toml index 890158e8fa..b33b018c91 100644 --- a/solana/modules/token_bridge/token_metadata_parser/Cargo.toml +++ b/solana/modules/token_bridge/token_metadata_parser/Cargo.toml @@ -18,3 +18,4 @@ spl-pod = "0.5" solana-program = "2.1" solana-sdk = "2.1" hex = "0.4" +rand = "0.9.2" diff --git a/solana/modules/token_bridge/token_metadata_parser/src/lib.rs b/solana/modules/token_bridge/token_metadata_parser/src/lib.rs index 3b9d46aff9..690401d39f 100644 --- a/solana/modules/token_bridge/token_metadata_parser/src/lib.rs +++ b/solana/modules/token_bridge/token_metadata_parser/src/lib.rs @@ -59,8 +59,7 @@ pub enum ParseError { const BASE_MINT_LEN: usize = 82; const BASE_MINT_LEN_V2: usize = 165; -const ACCOUNT_TYPE_OFFSET: usize = BASE_MINT_LEN; -const ACCOUNT_TYPE_OFFSET_V2: usize = BASE_MINT_LEN_V2; +const ACCOUNT_TYPE_OFFSET: usize = BASE_MINT_LEN_V2; const ACCOUNT_TYPE_MINT: u8 = 1; // TLV header sizes @@ -78,29 +77,40 @@ pub fn parse_token2022_metadata( mint_account: Pubkey, mint_account_data: &[u8], ) -> Result { + // mint accounts are at least 82 bytes long, so we reject if mint_account_data.len() < BASE_MINT_LEN { return Err(ParseError::NotAMintAccount); } + + // if exactly 82 bytes, it's either an spl token, or a token2022 with no extensions. this is valid if mint_account_data.len() == BASE_MINT_LEN { return Ok(MintMetadata::None); // no extensions } - let (account_type_offset, start_offset) = if mint_account_data.len() > BASE_MINT_LEN_V2 { - (ACCOUNT_TYPE_OFFSET_V2, ACCOUNT_TYPE_OFFSET_V2 + 1) - } else { - // Older layout - (ACCOUNT_TYPE_OFFSET, ACCOUNT_TYPE_OFFSET + 1) - }; + // if the mint has extensions, it's at least 166 bytes long. Anything in between is invalid. + if mint_account_data.len() <= BASE_MINT_LEN_V2 { + return Err(ParseError::NotAMintAccount); + } - // AccountType byte (present when extensions are initialized) + // by this point, we know the mint has extensions. the layout here is: + // 83..=165 padded with zeros (we verify here) + if !mint_account_data[BASE_MINT_LEN..ACCOUNT_TYPE_OFFSET] + .iter() + .all(|&b| b == 0) + { + return Err(ParseError::NotAMintAccount); + } + + // 166th byte is 1. let acct_type = *mint_account_data - .get(account_type_offset) + .get(ACCOUNT_TYPE_OFFSET) .ok_or(ParseError::UnexpectedEnd)?; if acct_type != ACCOUNT_TYPE_MINT { return Err(ParseError::NotMintAccountType); } - let mut offset = start_offset; + // the rest is the extensions in TLV format + let mut offset = ACCOUNT_TYPE_OFFSET + 1; let mut pointer: Option = None; let mut meta: Option = None; @@ -336,6 +346,112 @@ mod tests { } } + /// Generate a random ExtensionType that has a known size. + /// The only variable-length extension is TokenMetadata, which we skip. + fn random_sized_extension() -> ExtensionType { + use rand::Rng; + use std::convert::TryFrom; + loop { + let ext_u16: u16 = rand::rng().random_range(0..=27); + if let Ok(ext_type) = ExtensionType::try_from(ext_u16) { + if ext_type != ExtensionType::TokenMetadata { + return ext_type; + } + } + } + } + + // In a previous version of this code, I misintepreted the mint account + // extension layout, and thought that mint accounts can be _between_ 82 + // bytes (no extension) and 165 bytes (token account). + // + // It turns out that if the mint account has *any* extensions, it will + // be padded out with 0s to 165, then a single 1 byte account type + // discriminator is inserted, then the extensions follow. + // + // To verify this, we perform two tests: + // 1. generate a random set of extensions and verify the calculated size is either 82 or > 165. + // 2. create a mint account with a single small extension (MetadataPointer) and verify its size + the account discriminator. + #[test] + fn test_fuzz_mint_size() { + use rand::Rng; + + let mut rng = rand::rng(); + for _ in 0..1000 { + let len: usize = rng.random_range(0..=3); + // now generate `len` random extensions from the ExtensionType enum. + // ExtensionType is repr(u16), so we can generate a random u16 and + // cast it to ExtensionType, then filter out invalid values. + let mut extensions = Vec::new(); + for _ in 0..len { + extensions.push(random_sized_extension()); + } + let account_size = + ExtensionType::try_calculate_account_len::(&extensions).unwrap(); + assert!(account_size == 82 || account_size > 165); + } + } + + #[test] + fn test_mint_with_small_extension() { + // |------------------+--------------| + // | field | size (bytes) | + // |------------------+--------------| + // | base | 165 | + // | account type | 1 | + // | extension type | 2 | + // | extension length | 2 | + // | metadata pointer | 64 | + // |------------------+--------------| + // | total | 234 | + + let mint_address = SolanaPubkey::new_unique(); + let metadata_address = SolanaPubkey::new_unique(); + let authority = SolanaPubkey::new_unique(); + + // Calculate account size needed for the extension + let extension_types = vec![ExtensionType::MetadataPointer]; + let account_size = + ExtensionType::try_calculate_account_len::(&extension_types).unwrap(); + + assert_eq!(account_size, 234); + + // Create account data buffer + let mut mint_data = vec![0; account_size]; + + // Initialize the mint account with extensions + let mut state = + StateWithExtensionsMut::::unpack_uninitialized(&mut mint_data).unwrap(); + + // Set up base mint data + state.base = Mint { + mint_authority: COption::Some(authority), + supply: 1000000, + decimals: 6, + is_initialized: true, + freeze_authority: COption::None, + }; + + // Initialize MetadataPointer extension (pointing to self) + let metadata_pointer_extension = state.init_extension::(true).unwrap(); + metadata_pointer_extension.authority = OptionalNonZeroPubkey(authority); + metadata_pointer_extension.metadata_address = OptionalNonZeroPubkey(metadata_address); + + // Pack the base state and initialize account type + state.pack_base(); + state.init_account_type().unwrap(); + + let result = parse_token2022_metadata(convert_pubkey(mint_address), &mint_data).unwrap(); + + // Since metadata pointer points elsewhere, we should get external metadata + match &result { + MintMetadata::External(addr) => { + assert_eq!(addr.metadata_address, convert_pubkey(metadata_address)); + } + _ => panic!("Expected external metadata"), + } + } + #[test] fn test_basic_mint_no_extensions() { // Test basic 82-byte mint with no extensions @@ -360,12 +476,11 @@ mod tests { Err(ParseError::NotAMintAccount) ); - // Test wrong account type - let mut wrong_type_data = vec![0u8; 100]; - wrong_type_data[82] = 2; // Wrong account type (should be 1 for Mint) + // Test wrong account length + let wrong_type_data = vec![0u8; 100]; assert_eq!( parse_token2022_metadata(dummy_mint, &wrong_type_data), - Err(ParseError::NotMintAccountType) + Err(ParseError::NotAMintAccount) ); }