Jacky Gu

Solana开发笔记: Token2022 - 更新代币metadata

01 Jan 2025 Share to

Token2022 - 更新代币metadata

创建指令

instructions目录中添加update_token_metadata.rs,并粘贴以下代码:

use anchor_lang::solana_program::program::invoke;
use anchor_lang::prelude::*;
use anchor_lang::solana_program::system_instruction;
use crate::constants::MINT_SEEDS;
use crate::errors::ErrorCode;
use crate::states::TokenMetadata;
use anchor_spl::token_2022::spl_token_2022;
use anchor_spl::token_interface::{token_metadata_update_field, Mint, Token2022, TokenMetadataUpdateField};
use spl_token_metadata_interface::state::Field;

#[derive(Accounts)]
#[instruction(metadata_params: TokenMetadata)]
pub struct UpdateTokenMetadata<'info> {
  /// CHECK: Must be same as mint
  #[account(mut)]
  pub metadata: UncheckedAccount<'info>,
  #[account(
    seeds = [MINT_SEEDS.as_bytes(), metadata_params.name.as_bytes(), metadata_params.symbol.as_bytes()],
    bump,
  )]
  pub mint: Box<InterfaceAccount<'info, Mint>>,
  #[account(mut)]
  pub payer: Signer<'info>,
  pub system_program: Program<'info, System>,
  pub token_program: Program<'info, Token2022>,
}

impl<'info> UpdateTokenMetadata<'info> {
  pub fn update_account_lamports_to_minimum_balance<'a>(
    account: AccountInfo<'a>,
    payer: AccountInfo<'a>,
    system_program: AccountInfo<'a>,
  ) -> Result<()> {
      let minimum_balance = Rent::get()?.minimum_balance(account.data_len());
      let current_balance = account.get_lamports();

      if minimum_balance > current_balance {
          let extra_lamports = minimum_balance - current_balance;
          invoke(
              &system_instruction::transfer(payer.key, account.key, extra_lamports),
              &[payer, account, system_program],
          )?;
      }
      Ok(())
  }
  pub fn update_token_metadata(ctx: Context<UpdateTokenMetadata>, metadata_params: Box<TokenMetadata>) -> Result<()> {
    require!(spl_token_2022::ID == ctx.accounts.token_program.key(), ErrorCode::WrongTokenProgram);

    let seeds = &[MINT_SEEDS.as_bytes(), metadata_params.name.as_bytes(), metadata_params.symbol.as_bytes(), &[ctx.bumps.mint]];
    let signer = [&seeds[..]];

    let mint_account_info = ctx.accounts.mint.to_account_info();
    let token_program_account_info = ctx.accounts.token_program.to_account_info();
    let cpi_accounts = TokenMetadataUpdateField {
      token_program_id: token_program_account_info.clone(),
      metadata: mint_account_info.clone(), // metadata is same as mint
      update_authority: mint_account_info.clone(),
    };
    let cpi_ctx = CpiContext::new_with_signer(token_program_account_info, cpi_accounts, &signer);
    token_metadata_update_field(
      cpi_ctx, 
      Field::Uri,
      metadata_params.uri.clone()
    )?;

    Self::update_account_lamports_to_minimum_balance(
      mint_account_info.clone(),
      ctx.accounts.payer.to_account_info(),
      ctx.accounts.system_program.to_account_info(),
    )?;
    Ok(())
  }
}

在lib.rs中调用上面的指令

pub fn update_token_metadata(ctx: Context<UpdateTokenMetadata>, metadata_params: Box<TokenMetadata>) -> Result<()> {
    UpdateTokenMetadata::update_token_metadata(ctx, metadata_params)
}

编译部署

anchor build && anchor deploy

编写测试代码