use {
crate::{
deprecated_processor::{
process_deprecated_create_master_edition, process_deprecated_create_reservation_list,
process_deprecated_mint_new_edition_from_master_edition_via_printing_token,
process_deprecated_mint_printing_tokens,
process_deprecated_mint_printing_tokens_via_token,
process_deprecated_set_reservation_list,
},
error::MetadataError,
instruction::MetadataInstruction,
state::{
Data, Key, MasterEditionV1, MasterEditionV2, Metadata, EDITION, MAX_MASTER_EDITION_LEN,
PREFIX,
},
utils::{
assert_data_valid, assert_derivation, assert_initialized,
assert_mint_authority_matches_mint, assert_owned_by, assert_signer,
assert_token_program_matches_package, assert_update_authority_is_correct,
create_or_allocate_account_raw, get_owner_from_token_account,
process_create_metadata_accounts_logic,
process_mint_new_edition_from_master_edition_via_token_logic, transfer_mint_authority,
puff_out_data_fields,
CreateMetadataAccountsLogicArgs, MintNewEditionFromMasterEditionViaTokenLogicArgs,
},
},
arrayref::array_ref,
borsh::{BorshDeserialize, BorshSerialize},
solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint::ProgramResult,
msg,
program_error::ProgramError,
pubkey::Pubkey,
},
spl_token::state::{Account, Mint},
spl_token_vault::{error::VaultError, state::VaultState},
};
pub fn process_instruction<'a>(
program_id: &'a Pubkey,
accounts: &'a [AccountInfo<'a>],
input: &[u8],
) -> ProgramResult {
let instruction = MetadataInstruction::try_from_slice(input)?;
match instruction {
MetadataInstruction::CreateMetadataAccount(args) => {
msg!("Instruction: Create Metadata Accounts");
process_create_metadata_accounts(
program_id,
accounts,
args.data,
false,
args.is_mutable,
)
}
MetadataInstruction::UpdateMetadataAccount(args) => {
msg!("Instruction: Update Metadata Accounts");
process_update_metadata_accounts(
program_id,
accounts,
args.data,
args.update_authority,
args.primary_sale_happened,
)
}
MetadataInstruction::DeprecatedCreateMasterEdition(args) => {
msg!("Instruction: Deprecated Create Master Edition");
process_deprecated_create_master_edition(program_id, accounts, args.max_supply)
}
MetadataInstruction::DeprecatedMintNewEditionFromMasterEditionViaPrintingToken => {
msg!("Instruction: Deprecated Mint New Edition from Master Edition Via Token");
process_deprecated_mint_new_edition_from_master_edition_via_printing_token(
program_id, accounts,
)
}
MetadataInstruction::UpdatePrimarySaleHappenedViaToken => {
msg!("Instruction: Update primary sale via token");
process_update_primary_sale_happened_via_token(program_id, accounts)
}
MetadataInstruction::DeprecatedSetReservationList(args) => {
msg!("Instruction: Deprecated Set Reservation List");
process_deprecated_set_reservation_list(
program_id,
accounts,
args.reservations,
args.total_reservation_spots,
args.offset,
args.total_spot_offset,
)
}
MetadataInstruction::DeprecatedCreateReservationList => {
msg!("Instruction: Deprecated Create Reservation List");
process_deprecated_create_reservation_list(program_id, accounts)
}
MetadataInstruction::SignMetadata => {
msg!("Instruction: Sign Metadata");
process_sign_metadata(program_id, accounts)
}
MetadataInstruction::DeprecatedMintPrintingTokensViaToken(args) => {
msg!("Instruction: Deprecated Mint Printing Tokens Via Token");
process_deprecated_mint_printing_tokens_via_token(program_id, accounts, args.supply)
}
MetadataInstruction::DeprecatedMintPrintingTokens(args) => {
msg!("Instruction: Deprecated Mint Printing Tokens");
process_deprecated_mint_printing_tokens(program_id, accounts, args.supply)
}
MetadataInstruction::CreateMasterEdition(args) => {
msg!("Instruction: Create Master Edition");
process_create_master_edition(program_id, accounts, args.max_supply)
}
MetadataInstruction::MintNewEditionFromMasterEditionViaToken(args) => {
msg!("Instruction: Mint New Edition from Master Edition Via Token");
process_mint_new_edition_from_master_edition_via_token(
program_id,
accounts,
args.edition,
false,
)
}
MetadataInstruction::ConvertMasterEditionV1ToV2 => {
msg!("Instruction: Convert Master Edition V1 to V2");
process_convert_master_edition_v1_to_v2(program_id, accounts)
}
MetadataInstruction::MintNewEditionFromMasterEditionViaVaultProxy(args) => {
msg!("Instruction: Mint New Edition from Master Edition Via Vault Proxy");
process_mint_new_edition_from_master_edition_via_vault_proxy(
program_id,
accounts,
args.edition,
)
}
MetadataInstruction::PuffMetadata => {
msg!("Instruction: Puff Metadata");
process_puff_metadata_account(
program_id,
accounts
)
}
}
}
pub fn process_create_metadata_accounts<'a>(
program_id: &'a Pubkey,
accounts: &'a [AccountInfo<'a>],
data: Data,
allow_direct_creator_writes: bool,
is_mutable: bool,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let metadata_account_info = next_account_info(account_info_iter)?;
let mint_info = next_account_info(account_info_iter)?;
let mint_authority_info = next_account_info(account_info_iter)?;
let payer_account_info = next_account_info(account_info_iter)?;
let update_authority_info = next_account_info(account_info_iter)?;
let system_account_info = next_account_info(account_info_iter)?;
let rent_info = next_account_info(account_info_iter)?;
process_create_metadata_accounts_logic(
&program_id,
CreateMetadataAccountsLogicArgs {
metadata_account_info,
mint_info,
mint_authority_info,
payer_account_info,
update_authority_info,
system_account_info,
rent_info,
},
data,
allow_direct_creator_writes,
is_mutable,
)
}
pub fn process_update_metadata_accounts(
program_id: &Pubkey,
accounts: &[AccountInfo],
optional_data: Option<Data>,
update_authority: Option<Pubkey>,
primary_sale_happened: Option<bool>,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let metadata_account_info = next_account_info(account_info_iter)?;
let update_authority_info = next_account_info(account_info_iter)?;
let mut metadata = Metadata::from_account_info(metadata_account_info)?;
assert_owned_by(metadata_account_info, program_id)?;
assert_update_authority_is_correct(&metadata, update_authority_info)?;
if let Some(data) = optional_data {
if metadata.is_mutable {
assert_data_valid(&data, update_authority_info.key, &metadata, false)?;
metadata.data = data;
} else {
return Err(MetadataError::DataIsImmutable.into());
}
}
if let Some(val) = update_authority {
metadata.update_authority = val;
}
if let Some(val) = primary_sale_happened {
if val {
metadata.primary_sale_happened = val
} else {
return Err(MetadataError::PrimarySaleCanOnlyBeFlippedToTrue.into());
}
}
puff_out_data_fields(&mut metadata);
metadata.serialize(&mut *metadata_account_info.data.borrow_mut())?;
Ok(())
}
pub fn process_update_primary_sale_happened_via_token(
program_id: &Pubkey,
accounts: &[AccountInfo],
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let metadata_account_info = next_account_info(account_info_iter)?;
let owner_info = next_account_info(account_info_iter)?;
let token_account_info = next_account_info(account_info_iter)?;
let token_account: Account = assert_initialized(token_account_info)?;
let mut metadata = Metadata::from_account_info(metadata_account_info)?;
assert_owned_by(metadata_account_info, program_id)?;
assert_owned_by(token_account_info, &spl_token::id())?;
if !owner_info.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
if token_account.owner != *owner_info.key {
return Err(MetadataError::OwnerMismatch.into());
}
if token_account.amount == 0 {
return Err(MetadataError::NoBalanceInAccountForAuthorization.into());
}
if token_account.mint != metadata.mint {
return Err(MetadataError::MintMismatch.into());
}
metadata.primary_sale_happened = true;
metadata.serialize(&mut *metadata_account_info.data.borrow_mut())?;
Ok(())
}
pub fn process_sign_metadata(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let metadata_info = next_account_info(account_info_iter)?;
let creator_info = next_account_info(account_info_iter)?;
assert_signer(creator_info)?;
assert_owned_by(metadata_info, program_id)?;
let mut metadata = Metadata::from_account_info(metadata_info)?;
if let Some(creators) = &mut metadata.data.creators {
let mut found = false;
for creator in creators {
if creator.address == *creator_info.key {
creator.verified = true;
found = true;
break;
}
}
if !found {
return Err(MetadataError::CreatorNotFound.into());
}
} else {
return Err(MetadataError::NoCreatorsPresentOnMetadata.into());
}
metadata.serialize(&mut *metadata_info.data.borrow_mut())?;
Ok(())
}
pub fn process_create_master_edition(
program_id: &Pubkey,
accounts: &[AccountInfo],
max_supply: Option<u64>,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let edition_account_info = next_account_info(account_info_iter)?;
let mint_info = next_account_info(account_info_iter)?;
let update_authority_info = next_account_info(account_info_iter)?;
let mint_authority_info = next_account_info(account_info_iter)?;
let payer_account_info = next_account_info(account_info_iter)?;
let metadata_account_info = next_account_info(account_info_iter)?;
let token_program_info = next_account_info(account_info_iter)?;
let system_account_info = next_account_info(account_info_iter)?;
let rent_info = next_account_info(account_info_iter)?;
let metadata = Metadata::from_account_info(metadata_account_info)?;
let mint: Mint = assert_initialized(mint_info)?;
let bump_seed = assert_derivation(
program_id,
edition_account_info,
&[
PREFIX.as_bytes(),
program_id.as_ref(),
&mint_info.key.as_ref(),
EDITION.as_bytes(),
],
)?;
assert_token_program_matches_package(token_program_info)?;
assert_mint_authority_matches_mint(&mint.mint_authority, mint_authority_info)?;
assert_owned_by(metadata_account_info, program_id)?;
assert_owned_by(mint_info, &spl_token::id())?;
if metadata.mint != *mint_info.key {
return Err(MetadataError::MintMismatch.into());
}
if mint.decimals != 0 {
return Err(MetadataError::EditionMintDecimalsShouldBeZero.into());
}
assert_update_authority_is_correct(&metadata, update_authority_info)?;
if mint.supply != 1 {
return Err(MetadataError::EditionsMustHaveExactlyOneToken.into());
}
let edition_authority_seeds = &[
PREFIX.as_bytes(),
program_id.as_ref(),
&mint_info.key.as_ref(),
EDITION.as_bytes(),
&[bump_seed],
];
create_or_allocate_account_raw(
*program_id,
edition_account_info,
rent_info,
system_account_info,
payer_account_info,
MAX_MASTER_EDITION_LEN,
edition_authority_seeds,
)?;
let mut edition = MasterEditionV2::from_account_info(edition_account_info)?;
edition.key = Key::MasterEditionV2;
edition.supply = 0;
edition.max_supply = max_supply;
edition.serialize(&mut *edition_account_info.data.borrow_mut())?;
transfer_mint_authority(
edition_account_info.key,
edition_account_info,
mint_info,
mint_authority_info,
token_program_info,
)?;
Ok(())
}
pub fn process_mint_new_edition_from_master_edition_via_token<'a>(
program_id: &'a Pubkey,
accounts: &'a [AccountInfo<'a>],
edition: u64,
ignore_owner_signer: bool,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let new_metadata_account_info = next_account_info(account_info_iter)?;
let new_edition_account_info = next_account_info(account_info_iter)?;
let master_edition_account_info = next_account_info(account_info_iter)?;
let mint_info = next_account_info(account_info_iter)?;
let edition_marker_info = next_account_info(account_info_iter)?;
let mint_authority_info = next_account_info(account_info_iter)?;
let payer_account_info = next_account_info(account_info_iter)?;
let owner_account_info = next_account_info(account_info_iter)?;
let token_account_info = next_account_info(account_info_iter)?;
let update_authority_info = next_account_info(account_info_iter)?;
let master_metadata_account_info = next_account_info(account_info_iter)?;
let token_program_account_info = next_account_info(account_info_iter)?;
let system_account_info = next_account_info(account_info_iter)?;
let rent_info = next_account_info(account_info_iter)?;
process_mint_new_edition_from_master_edition_via_token_logic(
&program_id,
MintNewEditionFromMasterEditionViaTokenLogicArgs {
new_metadata_account_info,
new_edition_account_info,
master_edition_account_info,
mint_info,
edition_marker_info,
mint_authority_info,
payer_account_info,
owner_account_info,
token_account_info,
update_authority_info,
master_metadata_account_info,
token_program_account_info,
system_account_info,
rent_info,
},
edition,
ignore_owner_signer,
)
}
pub fn process_convert_master_edition_v1_to_v2(
program_id: &Pubkey,
accounts: &[AccountInfo],
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let master_edition_info = next_account_info(account_info_iter)?;
let one_time_printing_auth_mint_info = next_account_info(account_info_iter)?;
let printing_mint_info = next_account_info(account_info_iter)?;
assert_owned_by(master_edition_info, program_id)?;
assert_owned_by(one_time_printing_auth_mint_info, &spl_token::id())?;
assert_owned_by(printing_mint_info, &spl_token::id())?;
let master_edition: MasterEditionV1 = MasterEditionV1::from_account_info(master_edition_info)?;
let printing_mint: Mint = assert_initialized(printing_mint_info)?;
let auth_mint: Mint = assert_initialized(one_time_printing_auth_mint_info)?;
if master_edition.one_time_printing_authorization_mint != *one_time_printing_auth_mint_info.key
{
return Err(MetadataError::OneTimePrintingAuthMintMismatch.into());
}
if master_edition.printing_mint != *printing_mint_info.key {
return Err(MetadataError::PrintingMintMismatch.into());
}
if printing_mint.supply != 0 {
return Err(MetadataError::PrintingMintSupplyMustBeZeroForConversion.into());
}
if auth_mint.supply != 0 {
return Err(MetadataError::OneTimeAuthMintSupplyMustBeZeroForConversion.into());
}
MasterEditionV2 {
key: Key::MasterEditionV2,
supply: master_edition.supply,
max_supply: master_edition.max_supply,
}
.serialize(&mut *master_edition_info.data.borrow_mut())?;
Ok(())
}
pub fn process_mint_new_edition_from_master_edition_via_vault_proxy<'a>(
program_id: &'a Pubkey,
accounts: &'a [AccountInfo<'a>],
edition: u64,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let new_metadata_account_info = next_account_info(account_info_iter)?;
let new_edition_account_info = next_account_info(account_info_iter)?;
let master_edition_account_info = next_account_info(account_info_iter)?;
let mint_info = next_account_info(account_info_iter)?;
let edition_marker_info = next_account_info(account_info_iter)?;
let mint_authority_info = next_account_info(account_info_iter)?;
let payer_info = next_account_info(account_info_iter)?;
let vault_authority_info = next_account_info(account_info_iter)?;
let store_info = next_account_info(account_info_iter)?;
let safety_deposit_info = next_account_info(account_info_iter)?;
let vault_info = next_account_info(account_info_iter)?;
let update_authority_info = next_account_info(account_info_iter)?;
let master_metadata_account_info = next_account_info(account_info_iter)?;
let token_program_account_info = next_account_info(account_info_iter)?;
let token_vault_program_info = next_account_info(account_info_iter)?;
let system_account_info = next_account_info(account_info_iter)?;
let rent_info = next_account_info(account_info_iter)?;
let vault_data = vault_info.data.borrow();
let safety_deposit_data = safety_deposit_info.data.borrow();
if vault_data[0] != spl_token_vault::state::Key::VaultV1 as u8 {
return Err(VaultError::DataTypeMismatch.into());
}
if safety_deposit_data[0] != spl_token_vault::state::Key::SafetyDepositBoxV1 as u8 {
return Err(VaultError::DataTypeMismatch.into());
}
let token_program = Pubkey::new_from_array(*array_ref![vault_data, 1, 32]);
let vault_authority = Pubkey::new_from_array(*array_ref![vault_data, 65, 32]);
let store_on_sd = Pubkey::new_from_array(*array_ref![safety_deposit_data, 65, 32]);
let vault_on_sd = Pubkey::new_from_array(*array_ref![safety_deposit_data, 1, 32]);
let owner = get_owner_from_token_account(store_info)?;
let seeds = &[
spl_token_vault::state::PREFIX.as_bytes(),
token_vault_program_info.key.as_ref(),
vault_info.key.as_ref(),
];
let (authority, _) = Pubkey::find_program_address(seeds, token_vault_program_info.key);
if owner != authority {
return Err(MetadataError::InvalidOwner.into());
}
assert_signer(vault_authority_info)?;
assert_owned_by(vault_info, token_vault_program_info.key)?;
assert_owned_by(safety_deposit_info, token_vault_program_info.key)?;
assert_owned_by(store_info, token_program_account_info.key)?;
if &token_program != token_program_account_info.key {
return Err(VaultError::TokenProgramProvidedDoesNotMatchVault.into());
}
if !vault_authority_info.is_signer {
return Err(VaultError::AuthorityIsNotSigner.into());
}
if *vault_authority_info.key != vault_authority {
return Err(VaultError::AuthorityDoesNotMatch.into());
}
if vault_data[195] != VaultState::Combined as u8 {
return Err(VaultError::VaultShouldBeCombined.into());
}
if vault_on_sd != *vault_info.key {
return Err(VaultError::SafetyDepositBoxVaultMismatch.into());
}
if *store_info.key != store_on_sd {
return Err(VaultError::StoreDoesNotMatchSafetyDepositBox.into());
}
let args = MintNewEditionFromMasterEditionViaTokenLogicArgs {
new_metadata_account_info,
new_edition_account_info,
master_edition_account_info,
mint_info,
edition_marker_info,
mint_authority_info,
payer_account_info: payer_info,
owner_account_info: vault_authority_info,
token_account_info: store_info,
update_authority_info,
master_metadata_account_info,
token_program_account_info,
system_account_info,
rent_info,
};
process_mint_new_edition_from_master_edition_via_token_logic(program_id, args, edition, true)
}
pub fn process_puff_metadata_account(
program_id: &Pubkey,
accounts: &[AccountInfo],
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let metadata_account_info = next_account_info(account_info_iter)?;
let mut metadata = Metadata::from_account_info(metadata_account_info)?;
assert_owned_by(metadata_account_info, program_id)?;
puff_out_data_fields(&mut metadata);
let edition_seeds = &[
PREFIX.as_bytes(),
program_id.as_ref(),
metadata.mint.as_ref(),
EDITION.as_bytes()
];
let (_, edition_bump_seed) =
Pubkey::find_program_address(edition_seeds, program_id);
metadata.edition_nonce = Some(edition_bump_seed);
metadata.serialize(&mut *metadata_account_info.data.borrow_mut())?;
Ok(())
}