use mpl_token_metadata::{
accounts::{MasterEdition, Metadata},
instructions::{
CreateMasterEditionV3Cpi, CreateMasterEditionV3CpiAccounts,
CreateMasterEditionV3InstructionArgs, CreateMetadataAccountV3Cpi,
CreateMetadataAccountV3CpiAccounts, CreateMetadataAccountV3InstructionArgs,
},
types::DataV2,
};
use crate::{
cpi::Cpi,
state::{COLLECTION_NAME, COLLECTION_PREFIX, COLLECTION_URI, META_SYMBOL},
};
use {
bonfida_utils::{
checks::{check_account_key, check_account_owner, check_signer},
BorshSize, InstructionsAccount,
},
borsh::{BorshDeserialize, BorshSerialize},
mpl_token_metadata::types::Creator,
solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint::ProgramResult,
msg,
program::{invoke, invoke_signed},
program_error::ProgramError,
program_pack::Pack,
pubkey::Pubkey,
system_program, sysvar,
},
spl_associated_token_account::instruction::create_associated_token_account,
spl_token::{
instruction::{initialize_mint, mint_to},
state::Mint,
},
};
#[derive(BorshDeserialize, BorshSerialize, BorshSize)]
pub struct Params {}
#[derive(InstructionsAccount)]
pub struct Accounts<'a, T> {
#[cons(writable)]
pub collection_mint: &'a T,
#[cons(writable)]
pub edition: &'a T,
#[cons(writable)]
pub metadata_account: &'a T,
pub central_state: &'a T,
#[cons(writable)]
pub central_state_nft_ata: &'a T,
pub fee_payer: &'a T,
pub spl_token_program: &'a T,
pub metadata_program: &'a T,
pub system_program: &'a T,
pub spl_name_service_program: &'a T,
pub ata_program: &'a T,
pub rent_account: &'a T,
}
impl<'a, 'b: 'a> Accounts<'a, AccountInfo<'b>> {
pub fn parse(
accounts: &'a [AccountInfo<'b>],
_program_id: &Pubkey,
) -> Result<Self, ProgramError> {
let accounts_iter = &mut accounts.iter();
let accounts = Accounts {
collection_mint: next_account_info(accounts_iter)?,
edition: next_account_info(accounts_iter)?,
metadata_account: next_account_info(accounts_iter)?,
central_state: next_account_info(accounts_iter)?,
central_state_nft_ata: next_account_info(accounts_iter)?,
fee_payer: next_account_info(accounts_iter)?,
spl_token_program: next_account_info(accounts_iter)?,
metadata_program: next_account_info(accounts_iter)?,
system_program: next_account_info(accounts_iter)?,
spl_name_service_program: next_account_info(accounts_iter)?,
ata_program: next_account_info(accounts_iter)?,
rent_account: next_account_info(accounts_iter)?,
};
check_account_key(accounts.central_state, &crate::central_state::KEY)?;
check_account_key(accounts.spl_token_program, &spl_token::ID)?;
check_account_key(accounts.metadata_program, &mpl_token_metadata::ID)?;
check_account_key(accounts.system_program, &system_program::ID)?;
check_account_key(accounts.spl_name_service_program, &spl_name_service::ID)?;
check_account_key(accounts.ata_program, &spl_associated_token_account::ID)?;
check_account_key(accounts.rent_account, &sysvar::rent::ID)?;
check_account_owner(accounts.collection_mint, &system_program::ID)?;
check_account_owner(accounts.edition, &system_program::ID)?;
check_account_owner(accounts.metadata_account, &system_program::ID)?;
check_account_owner(accounts.central_state_nft_ata, &system_program::ID)?;
check_signer(accounts.fee_payer)?;
Ok(accounts)
}
}
pub fn process(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
let accounts = Accounts::parse(accounts, program_id)?;
let (collection_mint, collection_mint_nonce) =
Pubkey::find_program_address(&[COLLECTION_PREFIX, &program_id.to_bytes()], program_id);
check_account_key(accounts.collection_mint, &collection_mint)?;
let (metadata_key, _) = Metadata::find_pda(&collection_mint);
check_account_key(accounts.metadata_account, &metadata_key)?;
let (edition_key, _) = MasterEdition::find_pda(&collection_mint);
check_account_key(accounts.edition, &edition_key)?;
msg!("+ Creating mint");
let seeds: &[&[u8]] = &[
COLLECTION_PREFIX,
&program_id.to_bytes(),
&[collection_mint_nonce],
];
Cpi::create_account(
&spl_token::ID,
accounts.system_program,
accounts.fee_payer,
&accounts.collection_mint.clone(),
seeds,
Mint::LEN,
)?;
msg!("+ Initialize mint");
let ix = initialize_mint(
&spl_token::ID,
&collection_mint,
&crate::central_state::KEY,
Some(&crate::central_state::KEY),
0,
)?;
invoke_signed(
&ix,
&[
accounts.spl_token_program.clone(),
accounts.collection_mint.clone(),
accounts.rent_account.clone(),
],
&[seeds],
)?;
msg!("+ Creating central state ATA");
let ix = create_associated_token_account(
accounts.fee_payer.key,
&crate::central_state::KEY,
&collection_mint,
&spl_token::ID,
);
invoke(
&ix,
&[
accounts.ata_program.clone(),
accounts.fee_payer.clone(),
accounts.central_state_nft_ata.clone(),
accounts.central_state.clone(),
accounts.collection_mint.clone(),
accounts.system_program.clone(),
accounts.spl_token_program.clone(),
accounts.rent_account.clone(),
],
)?;
msg!("+ Minting NFT");
let seeds: &[&[u8]] = &[&program_id.to_bytes(), &[crate::central_state::NONCE]];
let ix = mint_to(
&spl_token::ID,
&collection_mint,
accounts.central_state_nft_ata.key,
&crate::central_state::KEY,
&[],
1,
)?;
invoke_signed(
&ix,
&[
accounts.spl_token_program.clone(),
accounts.collection_mint.clone(),
accounts.central_state_nft_ata.clone(),
accounts.central_state.clone(),
],
&[seeds],
)?;
msg!("+ Creating collection");
let central_creator = Creator {
address: crate::central_state::KEY,
verified: true,
share: 100,
};
CreateMetadataAccountV3Cpi::new(
accounts.metadata_program,
CreateMetadataAccountV3CpiAccounts {
metadata: accounts.metadata_account,
mint: accounts.collection_mint,
mint_authority: accounts.central_state,
payer: accounts.fee_payer,
update_authority: (accounts.central_state, true),
system_program: accounts.system_program,
rent: Some(accounts.rent_account),
},
CreateMetadataAccountV3InstructionArgs {
data: DataV2 {
name: COLLECTION_NAME.to_string(),
uri: COLLECTION_URI.to_string(),
symbol: META_SYMBOL.to_string(),
seller_fee_basis_points: 0,
creators: Some(vec![central_creator]),
uses: None,
collection: None,
},
is_mutable: true,
collection_details: None,
},
)
.invoke_signed(&[seeds])?;
msg!("+ Creating master edition");
CreateMasterEditionV3Cpi::new(
accounts.metadata_program,
CreateMasterEditionV3CpiAccounts {
edition: accounts.edition,
mint: accounts.collection_mint,
update_authority: accounts.central_state,
token_program: accounts.spl_token_program,
system_program: accounts.system_program,
rent: Some(accounts.rent_account),
mint_authority: accounts.central_state,
metadata: accounts.metadata_account,
payer: accounts.fee_payer,
},
CreateMasterEditionV3InstructionArgs {
max_supply: Some(0),
},
)
.invoke_signed(&[seeds])?;
Ok(())
}