3

need some assistance in creating an associate token account from the program. So we need users to be sending a SPL token to our program. To keep the account unique for each user we want to create PDA for each user. Each user's PDA will have an ATA, where users will send their tokens to. So program will then withdraw the tokens and do the necessary actions.

I am fairly new to Rust and stuck while creating ATA from the program. Below is the code I am trying to execute.

invoke(
        &create_associated_token_account(
            initializer.key,
            pda_ata.key,
            token.key,
            associated_system_program.key
        ),
        &[initializer.clone(), pda_ata.clone(), token.clone(), associated_system_program.clone()],
    )?;

Error I see in Logs:

'Instruction references an unknown account xxxxxxxx',

failed: An account required by the instruction is missing

So I want a user to

  • send 5 CUSTOM TOKENS
  • Program to Receive above 5 tokens and add to the user's tally.
  • Once there are 10 TOKENS received then a user can perform an action.

What should be the best approach to this via one INSTRUCTION?

  • create PDA for each user
  • create ATA for the PDA program
  • transfer from User's ATA to PDA ATA

Here is the complete function

pub fn create_user_token_pda(
    program_id: &Pubkey,
    accounts: &[AccountInfo]
) -> ProgramResult {

    let account_info_iter = &mut accounts.iter();

    let initializer = next_account_info(account_info_iter)?;
    let pda_user_token = next_account_info(account_info_iter)?;
    let pda_user_token_ata = next_account_info(account_info_iter)?;
    let token = next_account_info(account_info_iter)?;
    let system_program = next_account_info(account_info_iter)?;
    let token_program = next_account_info(account_info_iter)?;
    let associated_token_program = next_account_info(account_info_iter)?;

    if !initializer.is_signer {
        msg!("Missing required signature");
        return Err(ProgramError::MissingRequiredSignature);
    }

    msg!("initializer: {}", initializer.key);
    msg!("pda_user_token: {}", pda_user_token.key);
    msg!("pda_user_token_ata: {}", pda_user_token_ata.key);
    msg!("token: {}", token.key);
    msg!("system_program: {}", system_program.key);
    msg!("token_program: {}", token_program.key);
    msg!("associated_token_program: {}", associated_token_program.key);
    
    let rent = Rent::get()?;
    let rent_lamports = rent.minimum_balance(1);

    let (user_token, user_token_bump) =
        Pubkey::find_program_address(&[initializer.key.as_ref(), "token".as_ref()], program_id);
    if user_token != *pda_user_token.key {
        msg!("Invalid seeds for PDA");
        return Err(ProgramError::InvalidArgument);
    }

    msg!("create user token pda - 3");

    if **pda_user_token.try_borrow_lamports()? == 0 {

        invoke_signed(
            &system_instruction::create_account(
                initializer.key,
                pda_user_token.key,
                rent_lamports,
                1,
                program_id,
            ),
            &[
                initializer.clone(),
                pda_user_token.clone(),
                system_program.clone(),
            ],
            &[&[initializer.key.as_ref(), "token".as_ref(), &[user_token_bump]]],
        )?;
    }

    // let associated_token_address = get_associated_token_address(
    //     pda_user_token.key,
    //     token.key
    // );
    // msg!("associated_token_address: {}", associated_token_address.to_string());

    invoke(
        &create_associated_token_account(
            initializer.key,
            pda_user_token.key,
            token.key,
            token_program.key
        ),
        &[initializer.clone(), pda_user_token.clone(), pda_user_token_ata.clone(), token.clone(), token_program.clone(), system_program.clone(), associated_token_program.clone()]
    )?;

    msg!("created user token pda");

    Ok(())
}

Here is the frontend code

async function sendCreateUserTokenPda(signer: web3.Keypair, programId: web3.PublicKey, connection: web3.Connection) {
    let buffer = Buffer.alloc(1000)
    createUserTokenPdaInstructionLayout.encode(
        {
            variant: 1,
            title: ""
        },
        buffer
    )

    buffer = buffer.slice(0, createUserTokenPdaInstructionLayout.getSpan(buffer))
    
    const [userTokenPda] = await web3.PublicKey.findProgramAddressSync(
        [signer.publicKey.toBuffer(), Buffer.from("token")],
        programId
    )

    const userTokenPdaATA = await getAssociatedTokenAddress(
        customTokenProgramId,
        userTokenPda,
        true
    );

    console.log("User Token PDA:", userTokenPda.toBase58())
    console.log("User Token PDA ATA:", userTokenPdaATA.toBase58())
    

    const transaction = new web3.Transaction()
    
    const instruction = new web3.TransactionInstruction({
        programId: programId,
        data: buffer,
        keys: [
            {
                pubkey: signer.publicKey,
                isSigner: true,
                isWritable: false
            },
            {
                pubkey: userTokenPda,
                isSigner: false,
                isWritable: true
            },
            {
                pubkey: userTokenPdaATA,
                isSigner: false,
                isWritable: false
            },
            {
                pubkey: customTokenProgramId,
                isSigner: false,
                isWritable: false
            },
            {
                pubkey: web3.SystemProgram.programId,
                isSigner: false,
                isWritable: false
            },
            {
                pubkey: TOKEN_PROGRAM_ID,
                isSigner: false,
                isWritable: false
            },
            {
                pubkey: ASSOCIATED_TOKEN_PROGRAM_ID,
                isSigner: false,
                isWritable: false
            }            
        ]
    });

    transaction.add(instruction)
    const tx = await web3.sendAndConfirmTransaction(connection, transaction, [signer])
    console.log(`https://explorer.solana.com/tx/${tx}?cluster=custom&customUrl=http://localhost:88999`)
}

Error / Logs:


logs: [
    'Program FHS8mWsaFNbTVGNV7PkhnZ3rNmtXnDmmkDE82QWGeLT6 invoke [1]',
    'Program log: process_instruction: FHS8mWsaFNbTVGNV7PkhnZ3rNmtXnDmmkDE82QWGeLT6: 7 accounts, data=[1, 0, 0, 0, 0]',
    'Program log: initializer: Ggnjfap7FRubFPBWTpoPFHyPvVm9q1t9cB4GHx8MJT7d',
    'Program log: pda_user_token: DmmStsPFssdKnawJziTxfq7CbRgoHt9FTZjgMBD3jVKg',
    'Program log: pda_user_token_ata: HLELxBKDFPVYPz8ht5fDhzP3VbsCExcZWx4Qe8ASGVe1',
    'Program log: token: 3myLM5cXvikPWiqeTq2kHacFEdBq6wsRnxvhpd7HUHEC',
    'Program log: system_program: 11111111111111111111111111111111',
    'Program log: token_program: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
    'Program log: associated_token_program: ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL',
    'Program log: create user token pda - 3',
    "HLELxBKDFPVYPz8ht5fDhzP3VbsCExcZWx4Qe8ASGVe1's writable privilege escalated",
    'Program FHS8mWsaFNbTVGNV7PkhnZ3rNmtXnDmmkDE82QWGeLT6 consumed 97439 of 200000 compute units',
    'Program FHS8mWsaFNbTVGNV7PkhnZ3rNmtXnDmmkDE82QWGeLT6 failed: Cross-program invocation with unauthorized signer or writable account'
  ]

Thanks

1 Answer 1

1

Looks like either your user_token or pda_user_token are derived differently in client and program so the instruction thinks its an unknown account. Double check that all accounts you log in the program also also the same as you pass in from the client.

If that doesnt help pls post your frontend code as well.

With associated_system_program i guess you mean associated_token_program right?

Here is a working spl vault example in playground: https://beta.solpg.io/tutorials/spl-token-vault Its written in anchor though.

5
  • Thanks Jonas. I have pasted the frontend code. Could you please let me know if the accounts I am passing to the invoke function call are correct? With associated_system_program i guess you mean associated_token_program right?- Yes. I will update the code locally. ` invoke( &create_associated_token_account( initializer.key, pda_user_token_ata.key, token.key, associated_system_program.key ), &[initializer.clone(), pda_user_token_ata.clone(), token.clone(), associated_system_program.clone()], )?; ` Commented Mar 17 at 22:56
  • Hey Jonas, The PDA, PDA's ATA created in the program is same as what's getting passed from the client side. Commented Mar 17 at 23:26
  • You are missing 9834S4ZzyNUebJ5bV1eJDUYaCRmsNgfhpc9WgBry42Jb in the instruction. Can you check which account that is? And then pass it in eventually?
    – Jonas H.
    Commented Mar 18 at 1:49
  • Hey Jonas, I updated the frontend to pass TOKEN_PROGRAM_ID instead of ASSOCIATED_TOKEN_PROGRAM_ID. Are you available for a quick google meet so I can show you and maybe it will be much easier for you to pin point the exact error? I have updated the code in the original post. Are the accounts passed to invoke function looks ok to you? Commented Mar 18 at 6:49
  • If you upload it to a public repository or a sharable playground link i can have a look.
    – Jonas H.
    Commented Mar 26 at 16:48

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.