6

Including the newAccount Keypair as signer in the initialize instruction using the anchor format below results in a WalletSendTransactionError: Signature verification failed after approving from connected wallet.

const newAccount = Keypair.generate()

const transaction = await program.methods
  .initialize()
  .accounts({
    counter: newAccount.publicKey,
    user: wallet.publicKey,
    systemAccount: SystemProgram.programId,
  })
  .signers([newAccount]) <---- including signature here not working
  .transaction()

sendTransaction(transaction, connection)

It works fine if I include newAccount as signer in SendTransaction:

const transaction = await program.methods
  .initialize()
  .accounts({
    counter: newAccount.publicKey,
    user: wallet.publicKey,
    systemAccount: SystemProgram.programId,
  })
  .transaction()

sendTransaction(transaction, connection, { signers: [newAccount] }) <--- works like this

Could someone explain why the first approach does not work? I thought .signers[( )] included the signature in the transaction.

Using .rpc also works with no errors:

   const transaction = await program.methods
      .initialize()
      .accounts({
        counter: newAccount.publicKey,
        user: wallet.publicKey,
        systemAccount: anchor.web3.SystemProgram.programId,
      })
      .signers([newAccount])    <---- works with .rpc()
      .rpc()

Below is the initialize instruction:

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

    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
        let counter = &mut ctx.accounts.counter;
        counter.count = 0;
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(init, payer = user, space = 8 + 8)]
    pub counter: Account<'info, Counter>,
    #[account(mut)]
    pub user: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[account]
pub struct Counter {
    pub count: u64,
}
3
  • Could you maybe share the Accounts struct as well for the initialize instruction?
    – sayantank
    Commented Aug 10, 2022 at 7:04
  • 1
    Also could you try logging transaction.signatures.map(s => s.publicKey.toBase58()) in the first case? Also just to sanity check, does the first example work if you do .rpc() at the end instead of .transaction()?
    – Callum M
    Commented Aug 10, 2022 at 9:09
  • Added the initialize instruction. Using .signers([ ]) works correctly with .rpc. Although didn't get any signatures when I tried to console.log transaction.signatures.map(s => s.publicKey.toBase58()) using .transaction with .signers([newAccount]). Logged []
    – john
    Commented Aug 10, 2022 at 13:29

1 Answer 1

3

The sendTransaction method attempts to do -- too much. It should be used for simple/basic examples.

sendTransaction will sign and send the transaction, irrespective if it's being signed.

On line 4495:

async sendTransaction(
    transaction: Transaction,
    signers: Array<Signer>,
    options?: SendOptions,
  ): Promise<TransactionSignature> {
    
    // some code was removed for brevity

        transaction.lastValidBlockHeight = latestBlockhash.lastValidBlockHeight;
        transaction.recentBlockhash = latestBlockhash.blockhash;
        transaction.sign(...signers); 

It calls .sign with the signers that were passed in, hence why you get the Signature verification failed error.

connection.sendRawTransaction or connection.sendEncodedTransaction should be used instead.

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.