1

Whats going wrong here between the local test and instruction that is causing this error? Its really making me stuck and it doesn't return a transaction Id so I can investigate further on the solana explorer.

I'm using a program example here https://github.com/solana-developers/program-examples/blob/main/tokens/token-2022/nft-meta-data-pointer/anchor-example/anchor/programs/extension_nft/src/instructions/mint_nft.rs

and trying to add the requirement of 50USDC (or any token) before minting the NFT to the payer's account.

nft_minting test:

import * as anchor from '@coral-xyz/anchor';
import { Program } from '@coral-xyz/anchor';
import { ASSOCIATED_PROGRAM_ID } from '@coral-xyz/anchor/dist/cjs/utils/token';
import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID, getAssociatedTokenAddressSync, createMint, mintTo, createAccount } from '@solana/spl-token';
import { Keypair, PublicKey } from '@solana/web3.js';
import { NftMinting } from '../target/types/nft_minting';

describe('nft_minting', () => {
  const provider = anchor.AnchorProvider.env();
  anchor.setProvider(provider);
  const program = anchor.workspace.NftMinting as Program<NftMinting>;
  const payer = provider.wallet as anchor.Wallet;


  const USDC_MINT_ADDRESS = new PublicKey("BE6XeKV4AYUoo785XwUeyBUePUs6rqQpCAfgaRLUiLnN");

  it('Mint nft!', async () => {
    const balance = await anchor.getProvider().connection.getBalance(payer.publicKey);

    if (balance < 1e8) {
      const res = await anchor.getProvider().connection.requestAirdrop(payer.publicKey, 1e9);
      await anchor.getProvider().connection.confirmTransaction(res, 'confirmed');
    }
    const usdcMint = USDC_MINT_ADDRESS;

    // Create payer's USDC token account
    const payerUsdcTokenAccount = await createAccount(
      anchor.getProvider().connection,
      payer.payer,
      usdcMint,
      payer.publicKey,
      undefined,
      undefined,
      TOKEN_2022_PROGRAM_ID
    );

    // Mint 100 USDC to payer's USDC token account
    await mintTo(
      anchor.getProvider().connection,
      payer.payer,
      usdcMint,
      payerUsdcTokenAccount,
      payer.payer,
      100_000_000,
      [],
      undefined,
      TOKEN_2022_PROGRAM_ID
    );

    // Create bank's USDC token account
    const bank = Keypair.generate();
    const bankPublicKey = bank.publicKey;

    const bankUsdcTokenAccount = await createAccount(
      anchor.getProvider().connection,
      payer.payer,
      usdcMint,
      bankPublicKey,
      undefined,
      undefined,
      TOKEN_2022_PROGRAM_ID
    );

    const mint = new Keypair();
    console.log('Mint public key', mint.publicKey.toBase58());

    const destinationTokenAccount = getAssociatedTokenAddressSync(
      mint.publicKey,
      payer.publicKey,
      false,
      TOKEN_2022_PROGRAM_ID,
      ASSOCIATED_TOKEN_PROGRAM_ID,
    );

    const [nft_authority, nft_bump] = await PublicKey.findProgramAddress([Buffer.from('nft_authority')], program.programId);

    const tx = await program.methods
      .mintNft()
      .accounts({
        signer: payer.publicKey,
        systemProgram: anchor.web3.SystemProgram.programId,
        tokenProgram: TOKEN_2022_PROGRAM_ID,
        associatedTokenProgram: ASSOCIATED_PROGRAM_ID,
        tokenAccount: destinationTokenAccount,
        mint: mint.publicKey,
        nftAuthority: nft_authority,
        rent: anchor.web3.SYSVAR_RENT_PUBKEY,
        usdcFrom: payerUsdcTokenAccount,
        usdcMint: usdcMint,
        bankAta: bankUsdcTokenAccount,
      })
      .signers([mint])
      .rpc({ skipPreflight: true });

    console.log('Mint nft tx', tx);
    await anchor.getProvider().connection.confirmTransaction(tx, 'confirmed');
  });
});

mint_nft instruction:

pub use crate::errors::GameErrorCode;
pub use crate::errors::ProgramErrorCode;
pub use crate::state::game_data::GameData;
use crate::constants::USDC_MINT;
use anchor_lang::{prelude::*, system_program};
use anchor_spl::{
    associated_token::{self, AssociatedToken}, token_2022, token_interface::{spl_token_2022::instruction::AuthorityType, Token2022},
};

use solana_program::program::{invoke, invoke_signed};
use spl_token_2022::{extension::ExtensionType, state::Mint};
use anchor_spl::token_interface::{
    self, TokenAccount, TransferChecked,
};

pub fn mint_nft(ctx: Context<MintNft>) -> Result<()> {
    msg!("Mint nft with meta data extension and additional meta data");

    // Constants
    const USDC_AMOUNT: u64 = 50_000_000;
    let usdc_mint_address = Pubkey::try_from(USDC_MINT).unwrap();

    // Check the USDC mint address
    if ctx.accounts.usdc_mint.key() != usdc_mint_address {
        return err!(ProgramErrorCode::InvalidMintAddress);
    }

    let cpi_accounts = TransferChecked {
        from: ctx.accounts.usdc_from.to_account_info().clone(),
        mint: ctx.accounts.usdc_mint.to_account_info().clone(),
        to: ctx.accounts.bank_ata.to_account_info().clone(),
        authority: ctx.accounts.signer.to_account_info(),
    };
    let cpi_program = ctx.accounts.token_program.to_account_info();
    let cpi_context = CpiContext::new(cpi_program, cpi_accounts);
    token_interface::transfer_checked(cpi_context, USDC_AMOUNT, 6)?;

    msg!("Transferred 50 USDC to the bank");

    // Continue with the existing mint_nft logic
    let space = match ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::MetadataPointer]) {
        Ok(space) => space,
        Err(_) => return err!(ProgramErrorCode::InvalidMintAccountSpace),
    };

    let meta_data_space = 250;
    let lamports_required = (Rent::get()?).minimum_balance(space + meta_data_space);

    msg!(
        "Create Mint and metadata account size and cost: {} lamports: {}",
        space as u64,
        lamports_required
    );

    system_program::create_account(
        CpiContext::new(
            ctx.accounts.token_program.to_account_info(),
            system_program::CreateAccount {
                from: ctx.accounts.signer.to_account_info(),
                to: ctx.accounts.mint.to_account_info(),
            },
        ),
        lamports_required,
        space as u64,
        &ctx.accounts.token_program.key(),
    )?;

    system_program::assign(
        CpiContext::new(
            ctx.accounts.token_program.to_account_info(),
            system_program::Assign {
                account_to_assign: ctx.accounts.mint.to_account_info(),
            },
        ),
        &token_2022::ID,
    )?;


    msg!("Before initializing metadata pointer");
    msg!("Token program ID: {}", &Token2022::id());
    msg!("Mint account: {}", &ctx.accounts.mint.key());
    msg!("NFT Authority: {}", &ctx.accounts.nft_authority.key());

    let init_meta_data_pointer_ix = match spl_token_2022::extension::metadata_pointer::instruction::initialize(
        &Token2022::id(),
        &ctx.accounts.mint.key(),
        Some(ctx.accounts.nft_authority.key()),
        Some(ctx.accounts.mint.key()),
    ) {
        Ok(ix) => ix,
        Err(e) => {
            msg!("Error initializing metadata pointer: {:?}", e);
            return err!(ProgramErrorCode::CantInitializeMetadataPointer);
        }
    };
    
    invoke(
        &init_meta_data_pointer_ix,
        &[
            ctx.accounts.mint.to_account_info(),
            ctx.accounts.nft_authority.to_account_info(),
        ],
    )?;
    
    let mint_cpi_ix = CpiContext::new(
        ctx.accounts.token_program.to_account_info(),
        token_2022::InitializeMint2 {
            mint: ctx.accounts.mint.to_account_info(),
        },
    );

    token_2022::initialize_mint2(
        mint_cpi_ix,
        0,
        &ctx.accounts.nft_authority.key(),
        None,
    )?;
    
    let seeds = b"nft_authority";
    let bump = ctx.bumps.nft_authority;
    let signer: &[&[&[u8]]] = &[&[seeds, &[bump]]];

    msg!("Init metadata {0}", ctx.accounts.nft_authority.to_account_info().key);

    let init_token_meta_data_ix = &spl_token_metadata_interface::instruction::initialize(
        &spl_token_2022::id(),
        ctx.accounts.mint.key,
        ctx.accounts.nft_authority.to_account_info().key,
        ctx.accounts.mint.key,
        ctx.accounts.nft_authority.to_account_info().key,
        "Beaver".to_string(),
        "BVA".to_string(),
        "https://arweave.net/MHK3Iopy0GgvDoM7LkkiAdg7pQqExuuWvedApCnzfj0".to_string(),
    );

    invoke_signed(
        init_token_meta_data_ix,
        &[
            ctx.accounts.mint.to_account_info().clone(),
            ctx.accounts.nft_authority.to_account_info().clone(),
        ],
        signer,
    )?;

    invoke_signed(
        &spl_token_metadata_interface::instruction::update_field(
            &spl_token_2022::id(),
            ctx.accounts.mint.key,
            ctx.accounts.nft_authority.to_account_info().key,
            spl_token_metadata_interface::state::Field::Key("level".to_string()),
            "1".to_string(),
        ),
        &[
            ctx.accounts.mint.to_account_info().clone(),
            ctx.accounts.nft_authority.to_account_info().clone(),
        ],
        signer,
    )?;

    associated_token::create(
        CpiContext::new(
            ctx.accounts.associated_token_program.to_account_info(),
            associated_token::Create {
                payer: ctx.accounts.signer.to_account_info(),
                associated_token: ctx.accounts.token_account.to_account_info(),
                authority: ctx.accounts.signer.to_account_info(),
                mint: ctx.accounts.mint.to_account_info(),
                system_program: ctx.accounts.system_program.to_account_info(),
                token_program: ctx.accounts.token_program.to_account_info(),
            },
        ),
    )?;

    token_2022::mint_to(
        CpiContext::new_with_signer(
            ctx.accounts.token_program.to_account_info(),
            token_2022::MintTo {
                mint: ctx.accounts.mint.to_account_info(),
                to: ctx.accounts.token_account.to_account_info(),
                authority: ctx.accounts.nft_authority.to_account_info(),
            },
            signer,
        ),
        1,
    )?;

    token_2022::set_authority(
        CpiContext::new_with_signer(
            ctx.accounts.token_program.to_account_info(),
            token_2022::SetAuthority {
                current_authority: ctx.accounts.nft_authority.to_account_info(),
                account_or_mint: ctx.accounts.mint.to_account_info(),
            },
            signer,
        ),
        AuthorityType::MintTokens,
        None,
    )?;

    Ok(())
}

#[derive(Accounts)]
pub struct MintNft<'info> {
    #[account(mut)]
    pub signer: Signer<'info>,
    pub system_program: Program<'info, System>,
    pub token_program: Program<'info, Token2022>,

    /// CHECK: This is an associated token account for the NFT mint.
    #[account(mut)]
    pub token_account: AccountInfo<'info>,
    #[account(mut)]
    pub mint: Signer<'info>,
    pub rent: Sysvar<'info, Rent>,
    pub associated_token_program: Program<'info, AssociatedToken>,
    #[account(  
        init_if_needed,
        seeds = [b"nft_authority".as_ref()],
        bump,
        space = 8,
        payer = signer,
    )]
    pub nft_authority: Account<'info, NftAuthority>,

    #[account(mut)]
    pub usdc_from: InterfaceAccount<'info, TokenAccount>,

    /// CHECK: This is validated in the instruction logic.
    #[account(mut)]
    pub usdc_mint: AccountInfo<'info>,
    #[account(mut)]
    pub bank_ata: InterfaceAccount<'info, TokenAccount>,
}

#[account]
pub struct NftAuthority {}
1
  • Can you call yarn install and make sure you are on web3js version 1.93.0. This should give you a better error than 0x2. Can you add that to your question please?
    – Jonas H.
    Commented Jun 14 at 10:40

1 Answer 1

1

The error means invalid mint as you can see here: https://github.com/solana-labs/solana-program-library/blob/master/token/program/src/error.rs

So im not sure how you created the USDC mint, but maybe it not a token 22 token? Then you would need to use the old token program to derive the old token program. You probably will want to use await getOrCreateAssociatedTokenAccountin your test with the old token program if you created the token with the old token program.

When you update your web3js version to 1.93.0 via yarn install you will be able to see the actually error that is happening on chain.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.