1

I have the following accounts in an instruction:

#[derive(Accounts)]
pub struct CreatePixel<'info> {
    #[account(init, payer=user, space=Pixel::LEN)]
    pub pixel: Account<'info, Pixel>,
    #[account(mut)]
    pub user: Signer<'info>,
    pub system_program: Program<'info, System>,
}

When I build, the generated IDL has these accounts, note pixel is a signer:

"accounts": [
  {
    "name": "pixel",
    "isMut": true,
    "isSigner": true
  },
  {
    "name": "user",
    "isMut": true,
    "isSigner": true
  },
  {
    "name": "systemProgram",
    "isMut": false,
    "isSigner": false
  }
],

If I send a transaction in a unit test, I get "Error: Signature verification failed":

await program.methods
  .createPixel(posX, posY, 0, 0, 255)
  .accounts({
    pixel: pixelAddress,
    user: anchorProvider.wallet.publicKey,
    systemProgram: web3.SystemProgram.programId,
  })
  .rpc()

Is it correct that pixel needs to be a signer?

I'm aware that the correct thing to do here is to use a PDA, and that's the end-point of this code. But I thought I'd used code like this before without needing pixel to be a signer so just want to double-check that the behaviour I'm seeing is expected.

2 Answers 2

2

There's two steps to creating a program account

  • send an instruction to the system program with the space required for the account and intended program owner of the account
  • the owning program initializes some data on the account, in anchor this means adding the 8 byte discriminator and the borsch serialized struct of the account

When you use init on a non PDA account you're letting anchor call the system program instruction for you instead of calling it before passing in the account. But in order for that to work, anchor needs the account's signature since the system program instruction to create an account reuqires the signature of the account being created.

As a side note if you were to call the system program instruction yourself beforehand, you would use the zero constraint instead of init. Usually you would do this if you had an account to initialize over 10kb in size, although with the new realloc functionality in Solana, that can increase accounts by 10kb each transaction, this practice is changing.

2

Yes, pixel needs to be included in the signer's array. Wherever we init any account except PDAs, the account needs to sign for their account creation instruction. So that we can prove that we have the corresponding private key of the account.

const pixelAddress = anchor.web3.Keypair.generate();

await program.methods
  .createPixel(posX, posY, 0, 0, 255)
  .accounts({
    pixel: pixelAddress.publicKey,
    user: anchorProvider.wallet.publicKey,
    systemProgram: web3.SystemProgram.programId,
  })
  . signers([pixelAddress])
  .rpc()

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.