1

I'm creating a basic staking contract and just trying to initialize the pool where the reward tokens will be held. I'm receiving the following error:

TryingToInitPayerAsProgramAccount. Error Number: 4101. Error Message: You cannot/should not initialize the payer account as a program account.

I really don't think i'm initializing the payer account as a program account? Here is the Initialize context

#[derive(Accounts)]
#[instruction(bump: u8)]
pub struct Initialize<'info> {
    #[account(mut)]
    pub owner: Signer<'info>,
    #[account(init, payer = owner, space = StakingPool::LEN + 8)]
    pub staking_pool: Account<'info, StakingPool>,

    pub stake_token_mint: Box<Account<'info, Mint>>,

    #[account(
        seeds = [
            b"vault".as_ref(),
        ],
        bump,
    )]
    pub token_vault: SystemAccount<'info>,

    #[account(
        init_if_needed,
        payer = owner,
        associated_token::authority = token_vault,
        associated_token::mint = stake_token_mint,
    )]
    pub vault_ata: Box<Account<'info, TokenAccount>>,

    pub system_program: Program<'info, System>,
    pub token_program: Program<'info, Token>,
    pub associated_token_program: Program<'info, AssociatedToken>,
}

And here is the frontend call

  const [stakingPoolPDA, bump] = PublicKey.findProgramAddressSync(
    [Buffer.from(Anchor.utils.bytes.utf8.encode('vault'))], // Adjust with actual seeds
    programId
  );

//   console.log('stakingPoolPDA', stakingPoolPDA.toString());

  const vaultAta = getAssociatedTokenAddressSync(
    goldMintAddress, 
    stakingPoolPDA, 
    true 
  );
      const ix = await program.methods
        .initialize(bump)
        .accounts({
          stakeTokenMint: goldMintAddress,
          tokenVault: stakingPoolPDA,
          vaultAta: vaultAta,
        })
        .instruction();
      const transaction = new Anchor.web3.Transaction();
      transaction.add(ix);
      let signature = await sendTransaction(transaction, connection, {
        skipPreflight: true,

the actual instruction implementation is just setting some initial values for the stake pool:

    pub fn initialize(ctx: Context<Initialize>, bump: u8) -> Result<()> {
        msg!("Instruction: Initialize");
        msg!("before staking pool");
        let staking_pool: &mut Account<'_, StakingPool> = &mut ctx.accounts.staking_pool;
        staking_pool.owner = ctx.accounts.owner.key();
        staking_pool.staking_token_mint = ctx.accounts.stake_token_mint.key();
        staking_pool.total_staked_amount = 0;
        staking_pool.total_reward_amount = 0;
        staking_pool.total_user_count = 0;
        staking_pool.reward_per_second = 0;
        staking_pool.bump = bump;

        Ok(())
    }

I'm thinking this has to be an issue with something not directly related to the error message, as i'm not initializing the owner/payer anywhere in my code. On the frontend, i'm only passing in the accounts that anchor can not infer (signer, system program, etc). But even when i explicitly pass in all the accounts i'm getting the same error

2 Answers 2

4

It looks like you didn't provide the staking_pool account:

    #[account(init, payer = owner, space = StakingPool::LEN + 8)]
    pub staking_pool: Account<'info, StakingPool>, 

The account is missing in your instruction.

      const ix = await program.methods
        .initialize(bump)
        .accounts({
          stakeTokenMint: goldMintAddress,
          tokenVault: stakingPoolPDA,
          vaultAta: vaultAta,
        })
        .instruction();
4
  • I thought I wouldn’t have to explicitly provide the staking_pool account since it is being initialized in the smart contract when that instruction is called (hence the init annotation). Is my logic incorrect here? If so, how would I know what address to pass in?
    – blockchief
    Commented Jan 3 at 8:45
  • You do have to explicitly provide it. You probably want to include a seed and bump constraint in your staking_pool #[account(init...)] line to ensure that you only pass a valid PDA from the client.
    – AMilz
    Commented Jan 3 at 12:48
  • Hm okay thank you. I was under the impression that I didn’t want staking_pool to be a PDA though? That’s why I initialized it with a set amount of space in the context. Not sure why I would want that particular account to be a PDA when I already have token_vault as a PDA to handle things like reward distribution and management
    – blockchief
    Commented Jan 3 at 20:49
  • 1
    Following up, turning staking_pool into a PDA and then explicitly passing the account from the client ended up working! Thanks for the help. There were a couple other small things, but that was the core issue
    – blockchief
    Commented Jan 5 at 3:29
3

if it's not fixed yet, and you could share the repo i can maybe help you better.

But from just your initialize context:

You are missing your owner ATA. Should be something like this :

Ex:

  #[account(
    mut,
    associated_token::mint = stake_token_mint,
    associated_token::authority = owner
)]
pub owner_ata: Account<'info, TokenAccount>,

Also if you are dealing with SPL tokens you vault account cant be a system account.

Ex: A system account has no data therefore can only transact with native Sol.

Also seems to be an error with ur seeds

b"vault".as_ref(),

perhaps u meant

ex:

seeds=[b"vault", owner.key().as_ref()].

In your ts code looks like you are missing some accounts also.

Have a great day.

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.