2

I have basic instruction, all it does is create an ATA for a user (mint is already initialized), this is what my context looks like

// lib.rs
use {
    crate::constants::*,
    crate::states::*,
    anchor_lang::prelude::*,
    anchor_spl::{
        associated_token::{AssociatedToken, ID as ASSOCIATED_TOKEN_ID},
        token_2022::{Token2022, ID as TOKEN_2022_ID},
        token_interface::{Mint, Token, TokenAccount},
    },
};

#[derive(Accounts)]
pub struct CreateAta<'info> {
    #[account(
        mut,
        seeds = [MINT_SEED],
        bump,
        mint::authority = mint,
        mint::freeze_authority = mint,
        mint::token_program = TOKEN_2022_ID,
    )]
    pub mint: InterfaceAccount<'info, Mint>,

    #[account(mut)]
    pub user: Signer<'info>,

    #[account(
        init,
        payer = user,
        associated_token::mint = mint,
        associated_token::authority = user,
        associated_token::token_program = token_program
    )]
    pub user_ata: InterfaceAccount<'info, TokenAccount>,

    pub system_program: Program<'info, System>,
    #[account(address = TOKEN_2022_ID)]
    pub token_program: Program<'info, Token2022>,
    #[account(address = ASSOCIATED_TOKEN_ID)]
    pub associated_token_program: Program<'info, AssociatedToken>,
}

declare_id!("MyProgramId");

#[program]
pub mod app {
    use super::*;

    pub fn create_ata(ctx: Context<CreateAta>) -> Result<()> {
        Ok(())
    }
}

I'm getting the ATA by computing the PDA using any of the below methods on the client side

const userAta = getAssociatedTokenAddressSync(
  mint, // valid mint address
  user, // wallet to get ATA of
  false,
  TOKEN_2022_PROGRAM_ID, // token2022_PROGRAM_ID
  ASSOCIATED_TOKEN_PROGRAM_ID // associated token pubkey
);
const userAta2 = PublicKey.findProgramAddressSync(
  [user.toBuffer(), TOKEN_2022_PROGRAM_ID.toBuffer(), mint.toBuffer()], // passing wallet pubkey, token 2022 and mint
  ASSOCIATED_TOKEN_PROGRAM_ID // associated token pubkey
)[0];

Both return the same ATA Pubkey (let's assume it's FbYX2uN573G5WsgiPdHU6fS5PNUyjdXfGfpZNkYUuT4k), but for some reason, the instruction is never successful, and checking the logs, I get this

[
  "Program MyProgramId invoke [1]",
  "Program log: Instruction: CreateAta",
  "Instruction references an unknown account FbYX2uN573G5WsgiPdHU6fS5PNUyjdXfGfpZNkYUuT4k",
  "Program MyProgramId consumed 16823 of 200000 compute units",
  "Program MyProgramId failed: An account required by the instruction is missing",
];

I do not understand why it sees the ATA account as an Unknown account, please what am I missing?

EDIT:

Here is how I call the instruction client side, created an sdk from the generated IDL,


const createATAForUser = async (programId: PublicKey) => {
    const USER = Keypair.generate();
    // Funds user account via 

    // program is basically a type of Program that wraps the ProgramState type + IDL, Program is gottewn from anchor library
    const program = new Program<App>(APP_IDL as any, programId, provider);

    //Imports some const like the associated token Id, systemProgram, token2022 from the @solana/web3js library, and computed my mint address

    const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash("confirmed");
    const tx = new Transaction({
        blockhash: blockhash,
        feePayer: user.publicKey,
        lastValidBlockHeight: lastValidBlockHeight,
    }).add (
        await program.methods
        .createAta()
        .accounts({
        associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
        mint:mint,
        systemProgram: SystemProgram.programId,
        tokenProgram: TOKEN_2022_PROGRAM_ID,
        user: USER.publickey,
        userAta: getAssociatedTokenAddressSync(mint, user:USER.publickey, undefined, TOKEN_2022_PROGRAM_ID),
        })
        .instruction()
    );

    await sendAndConfirmTransaction(
        connection,
        tx,
        [USER],
        { commitment: "confirmed" }
    );
}
6
  • 1
    Did you double check already that you are passing in all the programs? SystemProgram for the new account and token and atoken program? Can a mint be its own authority? O.o What is user in the program? You are not passing in a signer. Does this even compile? Please post more code on how you call the program
    – Jonas H.
    Jan 15 at 2:18
  • 1
    Yes, I'm passing all the required programs Yes, a mint can be its own authority User is the signer, passed it in my real program, while formatting on SO, I somehow didn't include it, updated the question to reflect that And yea, it does compile, reason I can call the instruction. I've update the question to include the javascript caller code. Jan 15 at 2:46
  • Jonas isnt is types incorrect? Jan 15 at 5:08
  • 1
    maybe init doesnt work with InterfaceAccount yet. Ill investigate a bit tomorrow. Usually that works. Can check here for example: beta.solpg.io/tutorials/battle-coins that uses init if needed for token account
    – Jonas H.
    Jan 15 at 21:22
  • Yeah, the same conclusion I came to, init might not be supported for InterfaceAccount (TokenAccount) yet. Jan 16 at 0:51

2 Answers 2

3

Your provided JS client code shows:

await program.methods
  .createAta()
  .accounts({
    /* .. */
    userAta: getAssociatedTokenAddressSync(mint, user:USER.publickey),
});

It should be:

await program.methods
  .createAta()
  .accounts({
    /* .. */
    userAta: getAssociatedTokenAddressSync(
        mint,
        USER.publickey,
        /* allowOwnerOffCurve */,
        TOKEN_2022_PROGRAM_ID,
    ),
});
1
  • My bad for not indicating that's a helper function wrapped around the main function of the same name. if token ata was wrong, the token_program constraint in my anchor context wouldn't let it be successful, which it was when created before passing. Jan 16 at 9:15
0

Hey man i've one question? Are you issuing this ft on your contract?(If yes please, provide the code).

Otherwise this should fix your code:

#[derive(Accounts)]
pub struct CreateAta<'info> {
#[account(mut)]
pub user: Signer<'info>,
pub mint: InterfaceAccount<'info, Mint>,
#[account(
    init_if_needed,
    payer = user,
    associated_token::mint = mint,
    associated_token::authority = user,
)]
pub user_ata: InterfaceAccount<'info, TokenAccount>,
pub system_program: Program<'info, System>,
pub token_program: Interface<'info, TokenInterface>,
pub associated_token_program: Program<'info, AssociatedToken>
}

Edit: since you had the types incorrect i remember that you might not be importing everything correctly.

 use anchor_spl::token_interface::{TokenInterface, Mint, 
 TokenAccount,};

Edit 2:

In your ts code you are only deriving the pda you are not actually initing.

Correct code:

 //Derive PDA
 userAta = getAssociatedTokenAddressSync(mint, user.publicKey);

 //Derive and Init if necessary
 userAta = getOrCreateAssociatedTokenAccount(
 provider.connection, providerWallet.payer, mint, 
 user.publicKey)).address; 

Note : we use getOrCreateAssociatedTokenAccount to derive and init the ata.

5
  • By incorrect type, do you mean the Token2022 instead of TokenInterface? that's not a problem actually, my token is deployed already, so, I can replace Interface<', TokenInterface> with Program<', Token2022>... Concerning the getOrCreateAssociatedToken, I can not rely on everyone using my Dapp, it could be a CPI call or a custom community Dapp build, that's the reason I'm using init_if_needed on-chain, literally doing the getOrCreate thingy, so that too, isn't an issue, as that should create the ATA if it doesn't exist already.... Added the program to the question for context. Jan 15 at 8:54
  • Tried it out the way you recommended, the error remains the same. Jan 15 at 9:34
  • For reference on the ATA, you could check this out quicknode.com/guides/solana-development/anchor/… Jan 15 at 10:30
  • Share the repo ill give a look. Jan 15 at 20:17
  • It's a private repo, I wish I could add you to it. So far it seems init doesn't work for the interface TokenAccount, I had to do what as avoiding(creating ata before my main instruction) Jan 16 at 1:02

Your Answer

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