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