Solana Metaplex铸造新版NFT时metadata与metadata_mint传值疑问
参数说明
你当前代码中两个参数重复传入同一个metadata公钥的写法是错误的,两个字段的正确取值如下:
metadata:传入主版本(Master Edition)NFT对应的元数据(Metadata)PDA账户公钥,也就是官方文档中提到的主版本元数据账户metadata_mint:传入主版本NFT对应的Mint铸币账户公钥,即主版NFT本身的铸币地址,不是你新铸造的子版NFT的new_mint地址
传值依据
这个参数设计来自Metaplex Token Metadata程序的底层校验逻辑:
- Solana的PDA派生是单向规则:Metadata账户只能通过Mint地址派生得到,派生路径为
["metadata", 元数据程序ID, Mint地址],程序无法从传入的Metadata账户反向推导其关联的Mint地址,因此必须单独传入metadata_mint作为校验基准 - 程序执行时会首先校验你传入的
metadata账户,是否确实是metadata_mint派生出的合法Metadata账户,再校验该Metadata关联的Master Edition账户、持有主版NFT的代币账户是否匹配,从根源上避免恶意构造的账户传入。
你当前代码的其他问题
你现在的实现还有两个会直接导致调用失败的错误:
- 账户列表漏传了
edition_mark_pda:你在账户结构体中定义了该字段,但没有加入传给invoke的账户列表,这个账户是打印子版NFT必须的版次标记PDA,缺失会直接报账户不匹配错误 - 账户列表漏传了主版本Mint账户:你当前的结构体没有定义主版本对应的Mint字段,也就是
metadata_mint需要的取值来源,之前重复传metadata地址的写法会直接触发程序的PDA校验失败。
修正后的核心代码
首先补充账户结构体的缺失字段:
#[derive(Accounts)] pub struct CreateNewEdition<'info> { /// CHECK: 由Metaplex元数据程序做合法性校验 #[account(mut)] pub new_metadata: UncheckedAccount<'info>, /// CHECK: 由Metaplex元数据程序做合法性校验 #[account(mut)] pub new_edition: UncheckedAccount<'info>, /// CHECK: 由Metaplex元数据程序做合法性校验 #[account(mut)] pub master_edition: UncheckedAccount<'info>, /// CHECK: 由Metaplex元数据程序做合法性校验 #[account(mut)] pub new_mint: UncheckedAccount<'info>, /// CHECK: 由Metaplex元数据程序做合法性校验 #[account(mut)] pub edition_mark_pda: UncheckedAccount<'info>, /// 主版本NFT对应的Mint铸币账户,即metadata_mint参数的取值来源 /// CHECK: 由Metaplex元数据程序做合法性校验 pub master_mint: UncheckedAccount<'info>, #[account(mut)] pub new_mint_authority: Signer<'info>, #[account(mut)] pub payer: AccountInfo<'info>, #[account(mut)] pub token_account_owner: UncheckedAccount<'info>, /// 持有主版本NFT的代币账户,必须归属master_mint /// CHECK: 由Metaplex元数据程序做合法性校验 #[account(mut)] pub token_account: UncheckedAccount<'info>, /// CHECK: 由Metaplex元数据程序做合法性校验 #[account(mut)] pub new_metadata_update_authority: UncheckedAccount<'info>, /// 主版本NFT对应的Metadata账户,即metadata参数的取值来源 /// CHECK: 由Metaplex元数据程序做合法性校验 #[account(mut)] pub master_metadata: UncheckedAccount<'info>, pub token_program: Program<'info, Token>, pub system_program: Program<'info, System>, /// CHECK: 由Metaplex元数据程序做合法性校验 pub rent: AccountInfo<'info>, }
然后修正调用逻辑,补全缺失账户、传入正确参数:
pub fn create_new_edition_nft( ctx: Context<CreateNewEdition>, edition: u64, ) -> Result<()> { let edition_infos = vec![ ctx.accounts.token_program.to_account_info(), ctx.accounts.new_metadata.to_account_info(), ctx.accounts.new_edition.to_account_info(), ctx.accounts.master_edition.to_account_info(), ctx.accounts.new_mint.to_account_info(), ctx.accounts.edition_mark_pda.to_account_info(), ctx.accounts.new_mint_authority.to_account_info(), ctx.accounts.payer.to_account_info(), ctx.accounts.token_account_owner.to_account_info(), ctx.accounts.token_account.to_account_info(), ctx.accounts.new_metadata_update_authority.to_account_info(), ctx.accounts.master_metadata.to_account_info(), ctx.accounts.master_mint.to_account_info(), ctx.accounts.system_program.to_account_info(), ctx.accounts.rent.to_account_info(), ]; msg!("Edition Account Infos Assigned"); invoke( &mint_new_edition_from_master_edition_via_token( ctx.accounts.token_program.key(), ctx.accounts.new_metadata.key(), ctx.accounts.new_edition.key(), ctx.accounts.master_edition.key(), ctx.accounts.new_mint.key(), ctx.accounts.edition_mark_pda.key(), ctx.accounts.new_mint_authority.key(), ctx.accounts.payer.key(), ctx.accounts.token_account_owner.key(), ctx.accounts.token_account.key(), ctx.accounts.new_metadata_update_authority.key(), ctx.accounts.master_metadata.key(), ctx.accounts.master_mint.key(), edition ), edition_infos.as_slice() )?; msg!("A New Edition Nft Minted !!!"); Ok(()) }
注:官方文档只提到主版本元数据账户,是从业务逻辑层面对必填关联账户做说明,没有展开讲指令底层为了做校验需要的冗余参数,属于文档表述的简化,不是参数设计矛盾。
内容的提问来源于stack exchange,提问作者Chansol Lim
相关产品推荐
相关产品推荐

