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..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 @@ -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)] @@ -49,99 +49,64 @@ 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<()> { - // 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> = ctx.accounts.into(); + let bumps: FinalizeTreeWithRootBumps = ctx.bumps.into(); + 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()), +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(), + } } +} - if edition.max_supply != Some(0) { - return Err(BubblegumError::CollectionMustBeAUniqueMasterEdition.into()); +impl From for FinalizeTreeWithRootBumps { + fn from(value: FinalizeTreeWithRootAndCollectionBumps) -> Self { + Self { + tree_authority: value.tree_authority, + } } - - 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, )?;