diff --git a/Cargo.lock b/Cargo.lock index 1a9e759..a1cd9b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2594,7 +2594,7 @@ dependencies = [ [[package]] name = "tensor-toolbox" -version = "0.7.1" +version = "0.8.0" dependencies = [ "anchor-lang", "anchor-spl", diff --git a/toolbox/Cargo.toml b/toolbox/Cargo.toml index 6117c9f..a367c3e 100644 --- a/toolbox/Cargo.toml +++ b/toolbox/Cargo.toml @@ -3,7 +3,7 @@ name = "tensor-toolbox" description = "Toolbox of useful Rust utilities for Tensor Foundation's Solana programs" repository = "https://github.com/tensor-foundation/toolbox" homepage = "https://github.com/tensor-foundation/toolbox" -version = "0.7.1" +version = "0.8.0" edition = "2021" readme = "../README.md" license = "Apache-2.0" diff --git a/toolbox/src/error.rs b/toolbox/src/error.rs index 83ed71f..d54fa97 100644 --- a/toolbox/src/error.rs +++ b/toolbox/src/error.rs @@ -31,4 +31,10 @@ pub enum TensorError { #[msg("invalid whitelist")] InvalidWhitelist = 9010, + + #[msg("invalid program owner")] + InvalidProgramOwner = 9011, + + #[msg("invalid edition")] + InvalidEdition = 9012, } diff --git a/toolbox/src/metaplex_core.rs b/toolbox/src/metaplex_core.rs index 719da5b..29c8685 100644 --- a/toolbox/src/metaplex_core.rs +++ b/toolbox/src/metaplex_core.rs @@ -50,9 +50,9 @@ pub fn validate_core_asset( assert_ownership(asset_info, Key::AssetV1)?; // validates the collection is owned by the MPL Core program - maybe_collection_info - .as_ref() - .map(|c| assert_ownership(c, Key::CollectionV1)); + if let Some(collection_info) = maybe_collection_info { + assert_ownership(collection_info, Key::CollectionV1)?; + } let asset = BaseAssetV1::try_from(asset_info)?; diff --git a/toolbox/src/token_metadata.rs b/toolbox/src/token_metadata.rs index 3819194..3e30824 100644 --- a/toolbox/src/token_metadata.rs +++ b/toolbox/src/token_metadata.rs @@ -7,9 +7,9 @@ use anchor_spl::{ token_interface::{self, Mint, TokenAccount, TokenInterface, TransferChecked}, }; use mpl_token_metadata::{ - accounts::Metadata, + accounts::{Edition, MasterEdition, Metadata}, instructions::{DelegateTransferV1CpiBuilder, TransferV1CpiBuilder}, - types::{AuthorizationData, TokenStandard}, + types::{AuthorizationData, Key as MplKey, TokenStandard}, }; use tensor_vipers::{throw_err, unwrap_opt}; @@ -20,7 +20,7 @@ pub use mpl_token_metadata::ID; #[inline(never)] pub fn assert_decode_metadata(mint: &Pubkey, metadata: &AccountInfo) -> Result { if *metadata.owner != mpl_token_metadata::ID { - throw_err!(TensorError::BadMetadata); + throw_err!(TensorError::InvalidProgramOwner); } // We must use `safe_deserialize` since there are variations on the metadata struct @@ -36,6 +36,36 @@ pub fn assert_decode_metadata(mint: &Pubkey, metadata: &AccountInfo) -> Result Result { + if *edition.owner != mpl_token_metadata::ID { + throw_err!(TensorError::InvalidProgramOwner); + } + + let edition = MasterEdition::safe_deserialize(&edition.try_borrow_data()?) + .map_err(|_error| TensorError::InvalidEdition)?; + + Ok(edition) +} + +#[inline(never)] +pub fn assert_decode_edition(edition: &AccountInfo) -> Result { + if *edition.owner != mpl_token_metadata::ID { + throw_err!(TensorError::InvalidProgramOwner); + } + + let data = edition.try_borrow_data()?; + + if data.is_empty() || data[0] != MplKey::EditionV1 as u8 { + throw_err!(TensorError::InvalidEdition); + } + + let edition = Edition::from_bytes(&edition.try_borrow_data()?) + .map_err(|_error| TensorError::InvalidEdition)?; + + Ok(edition) +} + /// Transfer Args using AccountInfo types to be more generic. pub struct TransferArgsAi<'a, 'info> { /// Account that will pay for any associated fees.