From 90e3aa39824ab51fd339af560041cee5810117ee Mon Sep 17 00:00:00 2001 From: Stanislav Cherviakov Date: Thu, 11 Jul 2024 10:34:23 +0100 Subject: [PATCH 1/2] reusing the existing code for the finalize tree with collection --- .../finalize_tree_with_root_and_collection.rs | 121 ++++++------------ .../src/processor/mint_to_collection.rs | 2 +- .../bubblegum/program/src/processor/mod.rs | 8 +- 3 files changed, 42 insertions(+), 89 deletions(-) diff --git a/programs/bubblegum/program/src/processor/finalize_tree_with_root_and_collection.rs b/programs/bubblegum/program/src/processor/finalize_tree_with_root_and_collection.rs index 3896f57..e1fab68 100644 --- a/programs/bubblegum/program/src/processor/finalize_tree_with_root_and_collection.rs +++ b/programs/bubblegum/program/src/processor/finalize_tree_with_root_and_collection.rs @@ -1,9 +1,9 @@ -use crate::asserts::assert_has_collection_authority; -use crate::processor::{check_stake, finalize_tree}; +use crate::processor::process_collection_verification_mpl_only; +use crate::state::metaplex_adapter::Collection; use crate::state::metaplex_anchor::TokenMetadata; -use crate::{error::BubblegumError, state::TreeConfig}; +use crate::state::TreeConfig; +use crate::{FinalizeTreeWithRoot, FinalizeTreeWithRootBumps}; use anchor_lang::{prelude::*, system_program::System}; -use mpl_token_metadata::types::TokenStandard; use spl_account_compression::{program::SplAccountCompression, Noop}; #[derive(Accounts)] @@ -52,96 +52,49 @@ pub(crate) fn finalize_tree_with_root_and_collection<'info>( _metadata_url: String, _metadata_hash: String, ) -> Result<()> { - // TODO: charge protocol fees - - check_stake( - &ctx.accounts.staker.to_account_info(), - &ctx.accounts.registrar.to_account_info(), - &ctx.accounts.voter.to_account_info(), - )?; - - let merkle_tree = ctx.accounts.merkle_tree.to_account_info(); - let seed = merkle_tree.key(); - let seeds = &[seed.as_ref(), &[ctx.bumps.tree_authority]]; - - let authority = &mut ctx.accounts.tree_authority; - authority.set_inner(TreeConfig { - tree_delegate: authority.tree_creator, - num_minted: (rightmost_index + 1) as u64, - ..**authority + let mut collection = Some(Collection { + verified: false, + key: ctx.accounts.collection_mint.key(), }); - let authority_pda_signer = &[&seeds[..]]; - - validate_collection( + process_collection_verification_mpl_only( &ctx.accounts.collection_metadata, - &ctx.accounts.collection_authority.to_account_info(), &ctx.accounts.collection_mint.to_account_info(), + &ctx.accounts.collection_authority.to_account_info(), &ctx.accounts .collection_authority_record_pda .to_account_info(), &ctx.accounts.edition_account.to_account_info(), + &mut collection, + true, )?; - - finalize_tree( + let mut accs: FinalizeTreeWithRoot<'info> = FinalizeTreeWithRoot { + tree_authority: ctx.accounts.tree_authority.to_owned(), + merkle_tree: ctx.accounts.merkle_tree.to_owned(), + payer: ctx.accounts.payer.to_owned(), + tree_delegate: ctx.accounts.tree_delegate.to_owned(), + staker: ctx.accounts.staker.to_owned(), + registrar: ctx.accounts.registrar.to_owned(), + voter: ctx.accounts.voter.to_owned(), + fee_receiver: ctx.accounts.fee_receiver.to_owned(), + log_wrapper: ctx.accounts.log_wrapper.to_owned(), + compression_program: ctx.accounts.compression_program.to_owned(), + system_program: ctx.accounts.system_program.to_owned(), + }; + let bumps = FinalizeTreeWithRootBumps { + tree_authority: ctx.bumps.tree_authority, + }; + let ctx = Context::<'_, '_, '_, 'info, FinalizeTreeWithRoot<'info>>::new( + ctx.program_id, + &mut accs, + ctx.remaining_accounts, + bumps, + ); + crate::processor::finalize_tree_with_root( + ctx, root, rightmost_leaf, rightmost_index, - &ctx.accounts.compression_program.to_account_info(), - &ctx.accounts.tree_authority.to_account_info(), - &merkle_tree, - &ctx.accounts.log_wrapper.to_account_info(), - authority_pda_signer, - ctx.remaining_accounts, + _metadata_url, + _metadata_hash, ) } - -fn validate_collection( - collection_metadata: &Account, - collection_authority: &AccountInfo, - collection_mint: &AccountInfo, - collection_authority_record_pda: &AccountInfo, - edition_account: &AccountInfo, -) -> Result<()> { - let collection_authority_record = if collection_authority_record_pda.key() == crate::id() { - None - } else { - Some(collection_authority_record_pda) - }; - - // Verify correct account ownerships. - require!( - *collection_metadata.to_account_info().owner == mpl_token_metadata::ID, - BubblegumError::IncorrectOwner - ); - require!( - *collection_mint.owner == spl_token::id(), - BubblegumError::IncorrectOwner - ); - - assert_has_collection_authority( - collection_metadata, - collection_mint.key, - collection_authority.key, - collection_authority_record, - )?; - - let (expected, _) = mpl_token_metadata::accounts::MasterEdition::find_pda(collection_mint.key); - - if edition_account.key != &expected { - return Err(BubblegumError::CollectionMasterEditionAccountInvalid.into()); - } - - let edition = mpl_token_metadata::accounts::MasterEdition::try_from(edition_account) - .map_err(|_err| BubblegumError::CollectionMustBeAUniqueMasterEdition)?; - - match collection_metadata.token_standard { - Some(TokenStandard::NonFungible) | Some(TokenStandard::ProgrammableNonFungible) => (), - _ => return Err(BubblegumError::CollectionMustBeAUniqueMasterEdition.into()), - } - - if edition.max_supply != Some(0) { - return Err(BubblegumError::CollectionMustBeAUniqueMasterEdition.into()); - } - - Ok(()) -} diff --git a/programs/bubblegum/program/src/processor/mint_to_collection.rs b/programs/bubblegum/program/src/processor/mint_to_collection.rs index 3f59d0d..bb89ae5 100644 --- a/programs/bubblegum/program/src/processor/mint_to_collection.rs +++ b/programs/bubblegum/program/src/processor/mint_to_collection.rs @@ -105,7 +105,7 @@ pub(crate) fn mint_to_collection_v1( &collection_authority, &collection_authority_record_pda, &edition_account, - &mut message, + &mut message.collection, true, )?; diff --git a/programs/bubblegum/program/src/processor/mod.rs b/programs/bubblegum/program/src/processor/mod.rs index 500e35f..fad2fb7 100644 --- a/programs/bubblegum/program/src/processor/mod.rs +++ b/programs/bubblegum/program/src/processor/mod.rs @@ -167,11 +167,11 @@ fn process_collection_verification_mpl_only<'info>( collection_authority: &AccountInfo<'info>, collection_authority_record_pda: &AccountInfo<'info>, edition_account: &AccountInfo<'info>, - message: &mut MetadataArgs, + mut message_collection: &mut Option, verify: bool, ) -> Result<()> { // See if a collection authority record PDA was provided. - let collection_authority_record = if collection_authority_record_pda.key() == crate::id() { + let collection_authority_record: Option<&AccountInfo<'info>> = if collection_authority_record_pda.key() == crate::id() { None } else { Some(collection_authority_record_pda) @@ -192,7 +192,7 @@ fn process_collection_verification_mpl_only<'info>( ); // If the NFT has collection data, we set it to the correct value after doing some validation. - if let Some(collection) = &mut message.collection { + if let Some(collection) = &mut message_collection { assert_collection_membership( &Some(collection.adapt()), collection_metadata, @@ -273,7 +273,7 @@ fn process_collection_verification<'info>( &collection_authority, &collection_authority_record_pda, &edition_account, - &mut message, + &mut message.collection, verify, )?; From f041a3c9c7b9e1487a44c96ac1829220a159e982 Mon Sep 17 00:00:00 2001 From: Stanislav Cherviakov Date: Thu, 11 Jul 2024 12:39:03 +0100 Subject: [PATCH 2/2] nit: using more of From and marking varable as used as it's passed down the call stack --- .../finalize_tree_with_root_and_collection.rs | 52 ++++++++++++------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/programs/bubblegum/program/src/processor/finalize_tree_with_root_and_collection.rs b/programs/bubblegum/program/src/processor/finalize_tree_with_root_and_collection.rs index e1fab68..2e4d344 100644 --- a/programs/bubblegum/program/src/processor/finalize_tree_with_root_and_collection.rs +++ b/programs/bubblegum/program/src/processor/finalize_tree_with_root_and_collection.rs @@ -49,8 +49,8 @@ pub(crate) fn finalize_tree_with_root_and_collection<'info>( root: [u8; 32], rightmost_leaf: [u8; 32], rightmost_index: u32, - _metadata_url: String, - _metadata_hash: String, + metadata_url: String, + metadata_hash: String, ) -> Result<()> { let mut collection = Some(Collection { verified: false, @@ -67,22 +67,8 @@ pub(crate) fn finalize_tree_with_root_and_collection<'info>( &mut collection, true, )?; - let mut accs: FinalizeTreeWithRoot<'info> = FinalizeTreeWithRoot { - tree_authority: ctx.accounts.tree_authority.to_owned(), - merkle_tree: ctx.accounts.merkle_tree.to_owned(), - payer: ctx.accounts.payer.to_owned(), - tree_delegate: ctx.accounts.tree_delegate.to_owned(), - staker: ctx.accounts.staker.to_owned(), - registrar: ctx.accounts.registrar.to_owned(), - voter: ctx.accounts.voter.to_owned(), - fee_receiver: ctx.accounts.fee_receiver.to_owned(), - log_wrapper: ctx.accounts.log_wrapper.to_owned(), - compression_program: ctx.accounts.compression_program.to_owned(), - system_program: ctx.accounts.system_program.to_owned(), - }; - let bumps = FinalizeTreeWithRootBumps { - tree_authority: ctx.bumps.tree_authority, - }; + let mut accs: FinalizeTreeWithRoot<'info> = ctx.accounts.into(); + let bumps: FinalizeTreeWithRootBumps = ctx.bumps.into(); let ctx = Context::<'_, '_, '_, 'info, FinalizeTreeWithRoot<'info>>::new( ctx.program_id, &mut accs, @@ -94,7 +80,33 @@ pub(crate) fn finalize_tree_with_root_and_collection<'info>( root, rightmost_leaf, rightmost_index, - _metadata_url, - _metadata_hash, + metadata_url, + metadata_hash, ) } + +impl<'info> From<&mut FinalizeTreeWithRootAndCollection<'info>> for FinalizeTreeWithRoot<'info> { + fn from(value: &mut FinalizeTreeWithRootAndCollection<'info>) -> Self { + Self { + tree_authority: value.tree_authority.to_owned(), + merkle_tree: value.merkle_tree.to_owned(), + payer: value.payer.to_owned(), + tree_delegate: value.tree_delegate.to_owned(), + staker: value.staker.to_owned(), + registrar: value.registrar.to_owned(), + voter: value.voter.to_owned(), + fee_receiver: value.fee_receiver.to_owned(), + log_wrapper: value.log_wrapper.to_owned(), + compression_program: value.compression_program.to_owned(), + system_program: value.system_program.to_owned(), + } + } +} + +impl From for FinalizeTreeWithRootBumps { + fn from(value: FinalizeTreeWithRootAndCollectionBumps) -> Self { + Self { + tree_authority: value.tree_authority, + } + } +}