diff --git a/programs/mmm/src/instructions/cnft/metadata_args.rs b/programs/mmm/src/instructions/cnft/metadata_args.rs new file mode 100644 index 0000000..877c376 --- /dev/null +++ b/programs/mmm/src/instructions/cnft/metadata_args.rs @@ -0,0 +1,63 @@ +use anchor_lang::{prelude::*, AnchorDeserialize, AnchorSerialize}; + +// Define the TokenStandard enum +#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] +pub enum TokenStandard { + NonFungible, + FungibleAsset, + Fungible, + NonFungibleEdition, +} + +#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct Collection { + pub verified: bool, + pub key: Pubkey, +} + +#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] +pub enum UseMethod { + Burn, + Multiple, + Single, +} + +#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct Uses { + pub use_method: UseMethod, + pub remaining: u64, + pub total: u64, +} + +#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] +pub enum TokenProgramVersion { + Original, + Token2022, +} + +#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Eq, PartialEq)] +pub struct Creator { + pub address: Pubkey, + pub verified: bool, + /// The percentage share. + /// + /// The value is a percentage, not basis points. + pub share: u8, +} + +// Define the MetadataArgs struct +#[derive(AnchorSerialize, AnchorDeserialize, Clone)] +pub struct MetadataArgs { + pub name: String, + pub symbol: String, // Changed from Option to String + pub uri: String, + pub seller_fee_basis_points: u16, + pub primary_sale_happened: bool, // Changed from Option to bool + pub is_mutable: bool, // Changed from Option to bool + pub edition_nonce: Option, + pub token_standard: Option, // Changed from Option to Option + pub collection: Option, + pub uses: Option, + pub token_program_version: TokenProgramVersion, // Assuming TokenProgramVersion is a simple u8 + pub creators: Vec, +} diff --git a/programs/mmm/src/instructions/cnft/mod.rs b/programs/mmm/src/instructions/cnft/mod.rs index 51651c0..3dd87ea 100644 --- a/programs/mmm/src/instructions/cnft/mod.rs +++ b/programs/mmm/src/instructions/cnft/mod.rs @@ -1,3 +1,5 @@ pub mod sol_cnft_fulfill_buy; +pub mod metadata_args; -pub use sol_cnft_fulfill_buy::*; \ No newline at end of file +pub use sol_cnft_fulfill_buy::*; +pub use metadata_args::*; \ No newline at end of file diff --git a/programs/mmm/src/instructions/cnft/sol_cnft_fulfill_buy.rs b/programs/mmm/src/instructions/cnft/sol_cnft_fulfill_buy.rs index e1aa6f4..6c0dfba 100644 --- a/programs/mmm/src/instructions/cnft/sol_cnft_fulfill_buy.rs +++ b/programs/mmm/src/instructions/cnft/sol_cnft_fulfill_buy.rs @@ -6,71 +6,11 @@ use crate::{ constants::*, errors::MMMErrorCode, state::{BubblegumProgram, Pool, SellState, TreeConfigAnchor}, - util::{log_pool, transfer_compressed_nft, try_close_pool}, + util::{hash_metadata, log_pool, transfer_compressed_nft, try_close_pool}, verify_referral::verify_referral, }; -// Define the TokenStandard enum -#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] -pub enum TokenStandard { - NonFungible, - FungibleAsset, - Fungible, - NonFungibleEdition, -} - -#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Eq, PartialEq)] -pub struct Collection { - pub verified: bool, - pub key: Pubkey, -} - -#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] -pub enum UseMethod { - Burn, - Multiple, - Single, -} - -#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Eq, PartialEq)] -pub struct Uses { - pub use_method: UseMethod, - pub remaining: u64, - pub total: u64, -} - -#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] -pub enum TokenProgramVersion { - Original, - Token2022, -} - -#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Eq, PartialEq)] -pub struct Creator { - pub address: Pubkey, - pub verified: bool, - /// The percentage share. - /// - /// The value is a percentage, not basis points. - pub share: u8, -} - -// Define the MetadataArgs struct -#[derive(AnchorSerialize, AnchorDeserialize, Clone)] -pub struct MetadataArgs { - pub name: String, - pub symbol: String, // Changed from Option to String - pub uri: String, - pub seller_fee_basis_points: u16, - pub primary_sale_happened: bool, // Changed from Option to bool - pub is_mutable: bool, // Changed from Option to bool - pub edition_nonce: Option, - pub token_standard: Option, // Changed from Option to Option - pub collection: Option, - pub uses: Option, - pub token_program_version: TokenProgramVersion, // Assuming TokenProgramVersion is a simple u8 - pub creators: Vec, -} +use super::MetadataArgs; #[derive(AnchorSerialize, AnchorDeserialize)] pub struct SolCnftFulfillBuyArgs { @@ -210,6 +150,8 @@ pub fn handler<'info>( return Err(MMMErrorCode::InvalidAccountState.into()); } + let data_hash = hash_metadata(&args.metadata_args)?; + msg!("seller fee basis points: {}", args.seller_fee_basis_points); // Create data_hash from metadata_hash + seller_fee_basis_points (secures creator royalties) // let data_hash = hash_metadata_data(args.metadata_hash, args.seller_fee_basis_points)?; @@ -228,7 +170,7 @@ pub fn handler<'info>( proof_path, ctx.accounts.bubblegum_program.key(), args.root, - args.metadata_hash, + data_hash, args.creator_hash, args.nonce, args.index, diff --git a/programs/mmm/src/util.rs b/programs/mmm/src/util.rs index 71d10a3..88b8971 100644 --- a/programs/mmm/src/util.rs +++ b/programs/mmm/src/util.rs @@ -7,7 +7,7 @@ use crate::{ errors::MMMErrorCode, get_creators_from_royalties, state::*, - IndexableAsset, + IndexableAsset, MetadataArgs, }; use anchor_lang::{prelude::*, solana_program::log::sol_log_data}; use anchor_spl::token_interface::Mint; @@ -1214,13 +1214,18 @@ pub fn transfer_compressed_nft<'info>( Ok(()) } -// Taken from Bubblegum's hash_metadata: hashes seller_fee_basis_points to the final data_hash that Bubblegum expects. -// This way we can use the seller_fee_basis_points while still guaranteeing validity. -pub fn hash_metadata_data( - metadata_args_hash: [u8; 32], - seller_fee_basis_points: u16, -) -> Result<[u8; 32]> { - Ok(keccak::hashv(&[&metadata_args_hash, &seller_fee_basis_points.to_le_bytes()]).to_bytes()) +/// Computes the hash of the metadata. +/// +/// The hash is computed as the keccak256 hash of the metadata bytes, which is +/// then hashed with the `seller_fee_basis_points`. +pub fn hash_metadata(metadata: &MetadataArgs) -> Result<[u8; 32]> { + let hash = keccak::hashv(&[metadata.try_to_vec()?.as_slice()]); + // Calculate new data hash. + Ok(keccak::hashv(&[ + &hash.to_bytes(), + &metadata.seller_fee_basis_points.to_le_bytes(), + ]) + .to_bytes()) } #[cfg(test)]