3

I have two methods in my program, one to initialize a PDA and set some state variables the other to transfer SOL from one to another account

pub fn initialize(ctx: Context<Initialize>, max_participation_amount: u64) -> Result<()> {
        let state_account: &mut Account<State> = &mut ctx.accounts.auction_instance;
        state_account.vault_public_key = ctx.accounts.vault.key().clone();
        state_account.max_participation_amount = max_participation_amount;

        Ok(())
}

pub fn fund_vault(ctx: Context<FundVault>) -> Result<()> {
        let payer = &mut ctx.accounts.payer;
        let receiver = &mut ctx.accounts.receiver;
        let amount: u64 = 1_000_000_000;

        if payer.lamports() < amount {
            return Err(error!(ErrorCode::NotEnoughSol));
        }

        let instruction = transfer(&payer.key(), &receiver.key(), amount);

        let account_infos = [
            payer.to_account_info().clone(),
            receiver.to_account_info().clone(),
            ctx.accounts.system_program.to_account_info().clone(),
        ];

        invoke(&instruction, &account_infos)?;

        Ok(())
}

#[derive(Accounts)]
pub struct FundVault<'info> {
     /// CHECK:
    #[account(mut)]
    pub receiver: AccountInfo<'info>,
    #[account(mut)]
    pub payer: Signer<'info>,
    pub system_program: Program<'info, System>,
}


#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(init, payer = user, seeds = [b"vault"], bump, space = Vault::LEN)]
    pub vault: Account<'info, Vault>,
    #[account(init, payer = user, space = State::LEN)]
    pub auction_instance: Account<'info, State>,
    #[account(mut)]
    pub user: Signer<'info>,
    pub system_program: Program<'info, System>,
}

my typescript test code looks like this

  it("Is initialized!", async () => {
    const a = await provider.connection.getAccountInfo(
      provider.wallet.publicKey,
    );
    console.log(pdaVaultPublicKey);
    console.log(a.owner);

    const tx = await program.methods
      .initialize(new anchor.BN(maxParticipationAmount))
      .accounts({ vault: pdaVaultPublicKey, user: provider.publicKey })
      .rpc();

    const b = await provider.connection.getAccountInfo(
      provider.wallet.publicKey,
    );
    console.log(b.owner);
  });

  it("it is funded", async () => {
    const newUser = anchor.web3.Keypair.generate();

    let airdropSig = await provider.connection.requestAirdrop(
      newUser.publicKey,
      5 * anchor.web3.LAMPORTS_PER_SOL,
    );

    const latestBlockHash = await provider.connection.getLatestBlockhash();

    await provider.connection.confirmTransaction({
      blockhash: latestBlockHash.blockhash,
      lastValidBlockHeight: latestBlockHash.lastValidBlockHeight,
      signature: airdropSig,
    });

    const tx = await program.methods
      .fundVault()
      .accounts({
        payer: newUser.publicKey,
        receiver: provider.wallet.publicKey,
      })
      .signers([newUser])
      .rpc();
  });

so while running each of the test one by one no errors occur. running the test in the order posted I am getting this error while the second test is being run.

     Error: failed to send transaction: Transaction simulation failed: This account may not be used to pay transaction fees
      at Connection.sendEncodedTransaction (node_modules/@solana/web3.js/src/connection.ts:4546:13)
      at processTicksAndRejections (internal/process/task_queues.js:95:5)
      at Connection.sendRawTransaction (node_modules/@solana/web3.js/src/connection.ts:4505:20)
      at sendAndConfirmRawTransaction (node_modules/@project-serum/anchor/src/provider.ts:288:21)
      at AnchorProvider.sendAndConfirm (node_modules/@project-serum/anchor/src/provider.ts:148:14)
      at MethodsBuilder.rpc [as _rpcFn] (node_modules/@project-serum/anchor/src/program/namespace/rpc.ts:29:16)

my assumption is that the owner of the wallet provider while calling first instruction (initialize) is changed and for some reason calling the second instruction (fundVault) fails.

fundVault is in this example just a transfer of SOL from some newly generated keypair to the provider wallet

I have console logged the owner in my tests and indeed the owner does change in initialize instruction. But I am not sure why it changes and why it might have any influence on the second instruction call.

anybody having any ideas?

2
  • 1
    How come you're only passing 2 accounts into the call to initialize when it requires 4? I suspect Anchor is taking your 2nd account (user) as auction_instance and initializing it as a State account, but I don't know enough about how Anchor behaves when you pass the wrong number of accounts to know for sure. I asked about the same error message here, so I know you definitely can init a user wallet and break it like this: solana.stackexchange.com/questions/378/…
    – Callum M
    Commented Aug 2, 2022 at 17:21
  • wow, that was the problem all along...auction_instance should be an PDA, and should be passed to instruction...I suppose passing wrong number of accounts did something "weird" to the ownership of the provider, whereafter I couldnt sign anything...
    – mosmislit
    Commented Aug 2, 2022 at 17:46

2 Answers 2

1

my assumption is that the owner of the wallet provider while calling first instruction (initialize) is changed

Correct. initialize changes the owner away from the system program. Only system program owned accounts may pay transaction fees. so it has nothing to do with the second instruction but rather that the fee-payer specified is now not actually able to pay fees. The most coherent solution would be to initialize an account other than the "wallet" and use that for the other tests. A "wallet" should always be a system program owned account used as a fee-payer and signing authority. Calling anything else a "wallet" comes with an endless list of owning program defined caveats

0

I created a wallet json with this command:

solana-keygen grind --starts-with Tok:1

it created this JSON

TokTuEZfJ41yxHmfQmtgmDdXZCwaZn3MtAjRHjjQGSo.json

Then I load the keypair from this file.

// implementation of loadKeypair is not important here
const me = loadKeypair("./TokTuEZfJ41yxHmfQmtgmDdXZCwaZn3MtAjRHjjQGSo.json");

I passed the me as signers array

const signers = [me];

I got the same error. Then I run this command

solana config set -k ./TokTuEZfJ41yxHmfQmtgmDdXZCwaZn3MtAjRHjjQGSo.json

transaction went through

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.