1

I'm not the first to ask a question about this problem, but none of the questions asked were answered. The problem is that I'm trying to create an associated token account to mint an NFT, but the spl_associated_token_account::instruction::create_associated_token_account fails, when invoked. It seems like I've tried everything and read all the related sources, but nothing works for me.

Test Snippet

Here's the snippet of the test that fails:

/* Another test code... */
    #[tokio::test]
    async fn test_mint_created_nft_token() {
        msg!("Running `test_mint_created_nft_token`...");

        // Setting up the testing environment
        let (program_id, program_test) = initialize_program_test();
        msg!("Program ID: {}", program_id);
        let ctx = program_test.start_with_context().await;
        let mint_authority_id = ctx.payer.pubkey();
        let payer_id = ctx.payer.pubkey();

        let mint_account = Keypair::new();
        let mint_account_id = mint_account.pubkey();
        let wallet = Keypair::new();
        let wallet_id = wallet.pubkey();
        let (edition_account_id, _) = Pubkey::find_program_address(
            &[
                "metadata".as_bytes(),
                mpl_token_metadata::ID.as_ref(),
                &mint_account_id.to_bytes(),
                "edition".as_bytes(),
            ],
            &mpl_token_metadata::ID,
        );
        let (metadata_account_id, _) = Pubkey::find_program_address(
            &[
                "metadata".as_bytes(),
                mpl_token_metadata::ID.as_ref(),
                &mint_account_id.to_bytes(),
            ],
            &mpl_token_metadata::ID,
        );
        let associated_token_account_id =
            get_associated_token_address_with_program_id(
                &payer_id,
                &mint_account_id,
                &spl_token::ID,
            );

        // Create NFT
        msg!("Creating NFT...");

        // Initializing instruction to create NFT
        let ix = Instruction::new_with_borsh(
            program_id,
            &SplNftMinterIntstruction::Create(CreateNftTokenArgs {
                name: "Ballet Dancers BTC".to_string(),
                symbol: "BDB".to_string(),
                uri: "https://storage.yandexcloud.net/lab-bucket/item.json"
                    .to_string(),
            }),
            vec![
                AccountMeta::new(metadata_account_id, false),
                AccountMeta::new(mint_account_id, true),
                AccountMeta::new(mint_authority_id, true),
                AccountMeta::new(payer_id, true),
                AccountMeta::new_readonly(rent::ID, false),
                AccountMeta::new_readonly(system_program::ID, false),
                AccountMeta::new_readonly(mpl_token_metadata::ID, false),
                AccountMeta::new_readonly(spl_token::ID, false),
            ],
        );

        // Initializing transaction with the instruction above
        let tx = Transaction::new_signed_with_payer(
            &[ix],
            Some(&payer_id),
            &[&ctx.payer, &mint_account],
            ctx.last_blockhash,
        );

        // Executing the transaction above
        let tx_result = ctx.banks_client.process_transaction(tx).await;

        assert!(tx_result.is_ok());
        msg!("NFT created successfully!");

        // Mint the created NFT
        msg!("Minting NFT...");

        // Initializing instruction to mint the created NFT
        let ix = Instruction::new_with_borsh(
            program_id,
            &SplNftMinterIntstruction::Mint,
            vec![
                AccountMeta::new(associated_token_account_id, false),
                AccountMeta::new_readonly(
                    spl_associated_token_account::ID,
                    false,
                ),
                AccountMeta::new(edition_account_id, false),
                AccountMeta::new(metadata_account_id, false),
                AccountMeta::new(mint_account_id, true),
                AccountMeta::new(mint_authority_id, false),
                AccountMeta::new(payer_id, true),
                AccountMeta::new_readonly(rent::ID, false),
                AccountMeta::new_readonly(system_program::ID, false),
                AccountMeta::new_readonly(spl_token::ID, false),
                AccountMeta::new_readonly(mpl_token_metadata::ID, false),
                AccountMeta::new_readonly(wallet_id, false),
            ],
        );

        // Initializing transaction with the instruction above
        let tx = Transaction::new_signed_with_payer(
            &[ix],
            Some(&payer_id),
            &[&ctx.payer, &mint_account],
            ctx.last_blockhash,
        );

        // Executing the transaction above
        let tx_result = ctx.banks_client.process_transaction(tx).await;

        assert!(tx_result.is_ok());
        msg!("NFT minted successfully!");
    }
/* Another test code... */

Instruction Snippet

It basically tries to call the following instruction:

pub fn mint_to(accounts: &[AccountInfo]) -> ProgramResult {
    let accounts_iter = &mut accounts.iter();

    let associated_token_account = next_account_info(accounts_iter)?;
    let _associated_token_program = next_account_info(accounts_iter)?;
    let edition_account = next_account_info(accounts_iter)?;
    let metadata_account = next_account_info(accounts_iter)?;
    let mint_account = next_account_info(accounts_iter)?;
    let mint_authority = next_account_info(accounts_iter)?;
    let payer = next_account_info(accounts_iter)?;
    let rent = next_account_info(accounts_iter)?;
    let system_program = next_account_info(accounts_iter)?;
    let token_metadata_program = next_account_info(accounts_iter)?;
    let token_program = next_account_info(accounts_iter)?;
    let wallet = next_account_info(accounts_iter)?;

    if associated_token_account.lamports() != 0 {
        msg!("Associated token account exists.");
    } else {
        msg!("Creating associated token account...");

        // TODO: Resolve the issue with `create_associated_token_account()`
        // invocation
        invoke(
            &ata_instruction::create_associated_token_account(
                payer.key,
                wallet.key,
                mint_account.key,
                token_program.key,
            ),
            &[
                payer.clone(),
                associated_token_account.clone(),
                wallet.clone(),
                mint_account.clone(),
                system_program.clone(),
                token_program.clone(),
            ],
        )?;
    }
    msg!("Associated Token Address: {}", associated_token_account.key);

    // Mint the NFT to the user's wallet
    msg!("Minting NFT to associated token account...");
    invoke(
        &token_instruction::mint_to(
            token_program.key,
            mint_account.key,
            associated_token_account.key,
            mint_authority.key,
            &[mint_authority.key],
            1,
        )?,
        &[
            mint_account.clone(),
            mint_authority.clone(),
            associated_token_account.clone(),
            token_program.clone(),
        ],
    )?;

    // We can make this a Limited Edition NFT through Metaplex,
    // which will disable minting by setting the Mint & Freeze Authorities to
    // the Edition Account
    msg!("Creating edition account...");
    msg!("Edition account address: {}", edition_account.key);
    invoke(
        &CreateMasterEditionV3Builder::new()
            .edition(*edition_account.key)
            .mint(*mint_account.key)
            .update_authority(*mint_authority.key)
            .mint_authority(*mint_authority.key)
            .payer(*payer.key)
            .metadata(*metadata_account.key)
            .token_program(*token_program.key)
            .system_program(*system_program.key)
            .rent(Some(*rent.key))
            .max_supply(1)
            .instruction(),
        &[
            edition_account.clone(),
            metadata_account.clone(),
            mint_account.clone(),
            mint_authority.clone(),
            payer.clone(),
            token_metadata_program.clone(),
            rent.clone(),
        ],
    )?;

    msg!("NFT minted successfully!");

    Ok(())
}

Test Logs

There're testing logs:

...
[2024-12-01T23:25:37.444637117Z DEBUG solana_runtime::message_processor::stable_log] Program log: Creating associated token account...
[2024-12-01T23:25:37.444838705Z DEBUG solana_runtime::message_processor::stable_log] Instruction references an unknown account GiPqmxsWPwCYUW2yu68ZZovJwxk9ZVGsK997mwvMgdxr
[2024-12-01T23:25:37.448646550Z DEBUG solana_runtime::message_processor::stable_log] Program 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM consumed 7564 of 200000 compute units
[2024-12-01T23:25:37.448690919Z DEBUG solana_runtime::message_processor::stable_log] Program 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM failed: An account required by the instruction is missing
test processor::test::test_mint_created_nft_token ... FAILED

failures:

---- processor::test::test_mint_created_nft_token stdout ----
Running `test_mint_created_nft_token`...
Program ID: 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM
Creating NFT...
NFT created successfully!
Minting NFT...
thread 'processor::test::test_mint_created_nft_token' panicked at src/processor.rs:236:9:
assertion failed: tx_result.is_ok()
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    processor::test::test_mint_created_nft_token

test result: FAILED. 1 passed; 1 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.42s

error: test failed, to rerun pass `--lib`

More Details

If you want a fully reproducible code, you can clone it from GitHub. The base was originally taken from official Solana program examples.

2 Answers 2

1

You might need to make sure the order of accounts in your accounts matches the ones in the instructions you are constructing in your tests.

in your instruction, the token program comes before the metadata program

  let token_metadata_program = next_account_info(accounts_iter)?;
  let token_program = next_account_info(accounts_iter)?;

But in your tests, this order is inverted

AccountMeta::new_readonly(spl_token::ID, false),
AccountMeta::new_readonly(mpl_token_metadata::ID, false),
1
  • 1
    Yeah, this definitely helped. Appreciate it! It always good to ask for a fresh look. I also needed to change the accounts passed to the ATA instruction invocation to match the order expected by the SPL ATA program.
    – Michael
    Commented Dec 3 at 20:21
0

As Jimii mentioned above that the order of accounts is wrong. There is one more issue in test where associated token account id is being created using payer_id. It should be done using wallet_id

So instead of this

let associated_token_account_id =
        get_associated_token_address_with_program_id(
            &payer_id,
            &mint_account_id,
            &spl_token::ID,
        );

It should be

    let associated_token_account_id =
        get_associated_token_address_with_program_id(
            &wallet_id,
            &mint_account_id,
            &spl_token::ID,
        );
2
  • Thaks! But can both the payer_id and wallet_id match?
    – Michael
    Commented Dec 3 at 20:23
  • Yes, both payer_id and wallet_id can be kept same as well Commented Dec 4 at 4:14

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.