这个操作也是非常非常常用的
最基本的就是我们的程序需要存储
那么就要把数据以某种格式存到账户里面
我们可以参考mpl的两个方法
一是create_metadata_account
二是create_master_edition
我们来看一下这2个方法
看看mpl是如何创建账户并存储的
首先是create_metadata_account
pub fn process_create_metadata_accounts_logic(
program_id: &Pubkey,
accounts: CreateMetadataAccountsLogicArgs,
data: DataV2,
allow_direct_creator_writes: bool,
mut is_mutable: bool,
is_edition: bool,
add_token_standard: bool,
) -> ProgramResult {
let CreateMetadataAccountsLogicArgs {
metadata_account_info,
mint_info,
mint_authority_info,
payer_account_info,
update_authority_info,
system_account_info,
rent_info,
} = accounts;
let mut update_authority_key = *update_authority_info.key;
let existing_mint_authority = get_mint_authority(mint_info)?;
// IMPORTANT NOTE
// This allows the Metaplex Foundation to Create but not update metadata for SPL tokens that have not populated their metadata.
assert_mint_authority_matches_mint(&existing_mint_authority, mint_authority_info).or_else(
|e| {
// Allow seeding by the authority seed populator
if mint_authority_info.key == &SEED_AUTHORITY && mint_authority_info.is_signer {
// When metadata is seeded, the mint authority should be able to change it
if let COption::Some(auth) = existing_mint_authority {
update_authority_key = auth;
is_mutable = true;
}
Ok(())
} else {
Err(e)
}
},
)?;
assert_owned_by(mint_info, &spl_token::id())?;
let metadata_seeds = &[
PREFIX.as_bytes(),
program_id.as_ref(),
mint_info.key.as_ref(),
];
let (metadata_key, metadata_bump_seed) =
Pubkey::find_program_address(metadata_seeds, program_id);
let metadata_authority_signer_seeds = &[
PREFIX.as_bytes(),
program_id.as_ref(),
mint_info.key.as_ref(),
&[metadata_bump_seed],
];
if metadata_account_info.key != &metadata_key {
return Err(MetadataError::InvalidMetadataKey.into());
}
create_or_allocate_account_raw(
*program_id,
metadata_account_info,
rent_info,
system_account_info,
payer_account_info,
MAX_METADATA_LEN,
metadata_authority_signer_seeds,
)?;
let mut metadata = Metadata::from_account_info(metadata_account_info)?;
let compatible_data = data.to_v1();
assert_data_valid(
&compatible_data,
&update_authority_key,
&metadata,
allow_direct_creator_writes,
update_authority_info.is_signer,
false,
)?;
let mint_decimals = get_mint_decimals(mint_info)?;
metadata.mint = *mint_info.key;
metadata.key = Key::MetadataV1;
metadata.data = data.to_v1();
metadata.is_mutable = is_mutable;
metadata.update_authority = update_authority_key;
assert_valid_use(&data.uses, &None)?;
metadata.uses = data.uses;
assert_collection_update_is_valid(is_edition, &None, &data.collection)?;
metadata.collection = data.collection;
if add_token_standard {
let token_standard = if is_edition {
TokenStandard::NonFungibleEdition
} else if mint_decimals == 0 {
TokenStandard::FungibleAsset
} else {
TokenStandard::Fungible
};
metadata.token_standard = Some(token_standard);
} else {
metadata.token_standard = None;
}
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(())
}
然后是create_master_edition
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.try_borrow_mut_data()?)?;
if metadata_account_info.is_writable {
let mut metadata_mut = Metadata::from_account_info(metadata_account_info)?;
metadata_mut.token_standard = Some(TokenStandard::NonFungible);
metadata_mut.serialize(&mut *metadata_account_info.try_borrow_mut_data()?)?;
}
// While you can't mint any more of your master record, you can
// mint as many limited editions as you like within your max supply.
transfer_mint_authority(
edition_account_info.key,
edition_account_info,
mint_info,
mint_authority_info,
token_program_info,
)?;
Ok(())
}
我们来对比一下这2个方法
然后参考这2个方法来创建我们自己的pda账户然后存储数据
msg!("Create Monster Info");
let bump_seed = assert_derivation(
program_id,
monster_info,
&[
SEED_MONSTER.as_bytes(),
program_id.as_ref(),
&mint_info.key.as_ref(),
],
)?;
let monster_seeds = &[
SEED_MONSTER.as_bytes(),
program_id.as_ref(),
&mint_info.key.as_ref(),
&[bump_seed],
];
create_or_allocate_account_raw(
*program_id,
monster_info,
rent_info,
system_info,
signer_info,
MAX_MONSTER_LENGTH,
monster_seeds,
)?;
let mut monster = Monster::from_account_info(monster_info)?;
monster.level = 1;
monster.gender = 1;
monster.race = 1;
monster.breed = 1;
monster.hp = 100;
monster.attack = 100;
monster.defense = 100;
monster.agility = 100;
monster.luck = 100;
monster.serialize(&mut *monster_info.try_borrow_mut_data()?)?;
先是找到bump_seed
然后把所有的seed合在一起
然后创建pda账户
然后serialize一下
然后我们再写一个upgrade方法
use borsh::BorshSerialize;
use mpl_token_metadata::instruction::{create_master_edition, create_master_edition_v3, create_metadata_accounts_v2};
use mpl_token_metadata::state::Edition;
use mpl_token_metadata::state::Key::EditionV1;
use solana_program::{
account_info::{AccountInfo, next_account_info},
entrypoint::ProgramResult,
msg,
program::{invoke, invoke_signed},
program_error::ProgramError,
pubkey::Pubkey,
system_instruction,
sysvar::{clock::Clock, rent::Rent, Sysvar},
};
use spl_associated_token_account::instruction::create_associated_token_account;
use spl_token::instruction::{initialize_mint, mint_to};
use crate::{ferror, state::*, utils::*};
pub fn process_upgrade(
program_id: &Pubkey,
accounts: &[AccountInfo],
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let authority_info = next_account_info(account_info_iter)?;
let monster_info = next_account_info(account_info_iter)?;
msg!("Upgrade Monster");
let mut monster = Monster::from_account_info(monster_info)?;
monster.level += 1;
monster.gender += 1;
monster.race += 1;
monster.breed += 1;
monster.hp += 100;
monster.attack += 100;
monster.defense += 100;
monster.agility += 100;
monster.luck += 100;
monster.serialize(&mut *monster_info.try_borrow_mut_data()?)?;
Ok(())
}
这个方法第一次调用成功了
但是第二次调用的时候就报错了
我要看一下哪里出了问题
我们来看一下mpl的update方法
pub fn process_update_metadata_accounts_v2(
program_id: &Pubkey,
accounts: &[AccountInfo],
optional_data: Option<DataV2>,
update_authority: Option<Pubkey>,
primary_sale_happened: Option<bool>,
is_mutable: 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 {
let compatible_data = data.to_v1();
assert_data_valid(
&compatible_data,
update_authority_info.key,
&metadata,
false,
update_authority_info.is_signer,
true,
)?;
metadata.data = compatible_data;
assert_collection_update_is_valid(false, &metadata.collection, &data.collection)?;
metadata.collection = data.collection;
} 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());
}
}
if let Some(val) = is_mutable {
if !val {
metadata.is_mutable = val
} else {
return Err(MetadataError::IsMutableCanOnlyBeFlippedToFalse.into());
}
}
puff_out_data_fields(&mut metadata);
metadata.serialize(&mut *metadata_account_info.try_borrow_mut_data()?)?;
Ok(())
}
似乎也是serialize然后修改数据
我不知道问题出在哪里