Skip to content
Advertisement

How to pass public key in javascript struct to anchor rpc method

I am trying to pass a public key to a anchor rpc method but think I am not generating it correctly in Javascript, I tried padding it with and without quotes but to no avail. anchor test gives me a TypeError: key.toBuffer is not a function IF I pad it. IF I do not pad it then I get the error Error: AnchorError caused by account: my_account. Error Code: AccountDidNotSerialize. Error Number: 3004. Error Message: Failed to serialize the account.

I suspect the issue is with my javascript code:

  const pubkey1 = anchor.web3.Keypair.generate();
  const signatory1 =
    {
      name: "matt",
      publicKey: pubkey1.publicKey, // Error Code: AccountDidNotSerialize. Error Number: 3004
      // publicKey: '"' + pubkey1.publicKey + '"', // TypeError: key.toBuffer is not a function
    };
    // Invoke the update rpc.
    await program.rpc.addSignatory(signatory1, {
      accounts: {
        myAccount: myAccount.publicKey,
      },
    });

Full code below.

Lib.rs

use anchor_lang::prelude::*;

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");

#[program]
mod basic_1 {
    use super::*;

    pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {
        let my_account = &mut ctx.accounts.my_account;
        my_account.data = data;
        Ok(())
    }

    pub fn update(ctx: Context<Update>, data: u64) -> Result<()> {
        let my_account = &mut ctx.accounts.my_account;
        my_account.data = data;
        Ok(())
    }

    pub fn add_signatory(ctx: Context<Update>, signatory: Signatory) -> Result<()> {
        let my_account = &mut ctx.accounts.my_account;
        //my_account.data = data;
        my_account.signatories.push(signatory);
        Ok(())
    }

    pub fn add_signatories(ctx: Context<Update>, signatories: Vec<Signatory>) -> Result<()> {
        let my_account = &mut ctx.accounts.my_account;
        //my_account.data = data;
        my_account.signatories = signatories;
        Ok(())
    }


}

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

#[derive(Accounts)]
pub struct Update<'info> {
    #[account(mut)]
    pub my_account: Account<'info, MyAccount>,
}

#[account]
pub struct MyAccount {
    pub data: u64,
    pub project_id: u64,
    pub project_name: String,
    pub signatories: Vec<Signatory>,
    //pub signatories: [Signatory; 3]
}

#[derive(Default, AnchorSerialize, AnchorDeserialize, Clone)]
pub struct Signatory {
    pub name: String,
    pub public_key: Pubkey,
}

basic-1.js

const assert = require("assert");
const anchor = require("@project-serum/anchor");
const { SystemProgram } = anchor.web3;

describe("basic-1", () => {
  // Use a local provider.
  const provider = anchor.AnchorProvider.local();

  // Configure the client to use the local cluster.
  anchor.setProvider(provider);

  it("Creates and initializes an account in a single atomic transaction (simplified)", async () => {
    // #region code-simplified
    // The program to execute.
    const program = anchor.workspace.Basic1;

    // The Account to create.
    const myAccount = anchor.web3.Keypair.generate();

    // Create the new account and initialize it with the program.
    // #region code-simplified
    await program.rpc.initialize(new anchor.BN(1234), {
      accounts: {
        myAccount: myAccount.publicKey,
        user: provider.wallet.publicKey,
        systemProgram: SystemProgram.programId,
      },
      signers: [myAccount],
    });
    // #endregion code-simplified

    // Fetch the newly created account from the cluster.
    const account = await program.account.myAccount.fetch(myAccount.publicKey);

    // Check it's state was initialized.
    assert.ok(account.data.eq(new anchor.BN(1234)));

    // Store the account for the next test.
    _myAccount = myAccount;
  });

  it("Updates a previously created account", async () => {
    const myAccount = _myAccount;

    // #region update-test

    // The program to execute.
    const program = anchor.workspace.Basic1;

    // Invoke the update rpc.
    await program.rpc.update(new anchor.BN(4321), {
      accounts: {
        myAccount: myAccount.publicKey,
      },
    });

    // Fetch the newly updated account.
    const account = await program.account.myAccount.fetch(myAccount.publicKey);

    // Check it's state was mutated.
    assert.ok(account.data.eq(new anchor.BN(4321)));

    // #endregion update-test
  });

  it("add a single signatory", async () => {
    const myAccount = _myAccount;

    // #region update-test

    // The program to execute.
    const program = anchor.workspace.Basic1;
    const pubkey1 = anchor.web3.Keypair.generate();
    const pubkey2 = anchor.web3.Keypair.generate();
    // const pubkey1 = "abc";
    // const pubkey2 = "def";
    // console.log("deepak " + pubkey1.publicKey);
    console.log("deepak without prop" + pubkey1);
    const signatory1 =
    {
      name: "matt",
      publicKey: pubkey1.publicKey,
      // publicKey: '"' + pubkey1.publicKey + '"',
      // public_key: pubkey1.publicKey,
    };

    // Invoke the update rpc.
    await program.rpc.addSignatory(signatory1, {
      accounts: {
        myAccount: myAccount.publicKey,
      },
    });

    // Fetch the newly updated account.
    const account = await program.account.myAccount.fetch(myAccount.publicKey);
    //assert.ok(account.signatories.len().eq(new anchor.BN(1)));
    assert.equal(account.signatories.length, 1);


    const signatory2 =
    {
      name: "smith",
      publicKey: pubkey2.publicKey,
      // publicKey: '"' + pubkey2.publicKey + '"',
      // public_key: pubkey2.publicKey,
    };

    // Invoke the update rpc.
    await program.rpc.addSignatory(signatory2, {
      accounts: {
        myAccount: myAccount.publicKey,
      },
    });

    // Fetch the newly updated account.
    const account2 = await program.account.myAccount.fetch(myAccount.publicKey);
    //assert.ok(account.signatories.len().eq(new anchor.BN(1)));
    assert.equal(account2.signatories.length, 2);



    // Check it's state was mutated.
    assert.ok(account.data.eq(new anchor.BN(4321)));

    // #endregion update-test
  });

  /*
  it("add multiple signatories", async () => {
    const myAccount = _myAccount;

    // #region update-test

    // The program to execute.
    const program = anchor.workspace.Basic1;
    const pubkey1 = anchor.web3.Keypair.generate();
    const pubkey2 = anchor.web3.Keypair.generate();

    const signatories1 = [
      {
        name: "matt",
        public_key: pubkey1,
      },
      {
        name: "smith",
        public_key: pubkey2,
      },
    ];


    // Invoke the update rpc.
    await program.rpc.addSignatories(signatories1, {
      accounts: {
        myAccount: myAccount.publicKey,
      },
    });

    // Fetch the newly updated account.
    const account = await program.account.myAccount.fetch(myAccount.publicKey);

    // Check it's state was mutated.
    assert.ok(account.data.eq(new anchor.BN(4321)));

    // #endregion update-test
  });
*/

});

basic_1.json

{
  "version": "0.1.0",
  "name": "basic_1",
  "instructions": [
    {
      "name": "initialize",
      "accounts": [
        {
          "name": "myAccount",
          "isMut": true,
          "isSigner": true
        },
        {
          "name": "user",
          "isMut": true,
          "isSigner": true
        },
        {
          "name": "systemProgram",
          "isMut": false,
          "isSigner": false
        }
      ],
      "args": [
        {
          "name": "data",
          "type": "u64"
        }
      ]
    },
    {
      "name": "update",
      "accounts": [
        {
          "name": "myAccount",
          "isMut": true,
          "isSigner": false
        }
      ],
      "args": [
        {
          "name": "data",
          "type": "u64"
        }
      ]
    },
    {
      "name": "addSignatory",
      "accounts": [
        {
          "name": "myAccount",
          "isMut": true,
          "isSigner": false
        }
      ],
      "args": [
        {
          "name": "signatory",
          "type": {
            "defined": "Signatory"
          }
        }
      ]
    },
    {
      "name": "addSignatories",
      "accounts": [
        {
          "name": "myAccount",
          "isMut": true,
          "isSigner": false
        }
      ],
      "args": [
        {
          "name": "signatories",
          "type": {
            "vec": {
              "defined": "Signatory"
            }
          }
        }
      ]
    }
  ],
  "accounts": [
    {
      "name": "MyAccount",
      "type": {
        "kind": "struct",
        "fields": [
          {
            "name": "data",
            "type": "u64"
          },
          {
            "name": "projectId",
            "type": "u64"
          },
          {
            "name": "projectName",
            "type": "string"
          },
          {
            "name": "signatories",
            "type": {
              "vec": {
                "defined": "Signatory"
              }
            }
          }
        ]
      }
    }
  ],
  "types": [
    {
      "name": "Signatory",
      "type": {
        "kind": "struct",
        "fields": [
          {
            "name": "name",
            "type": "string"
          },
          {
            "name": "publicKey",
            "type": "publicKey"
          }
        ]
      }
    }
  ],
  "metadata": {
    "address": "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
  }
}

Answer

You misunderstood the problem. The issue here is not with Pubkey passing.

You are running out of space while adding signatory.

What you need to do is pre calculate the space required to store max number of signatories allowed to store in vec. It must have the bound.

Please refer https://borsh.io/#pills-specification to calculate how much space is required for you.

To validate this you can change the space from 100 to 1000

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

this worked fine for me

✔ Creates and initializes an account in a single atomic transaction (simplified) (186ms)
✔ Updates a previously created account (407ms)
✔ add a single signatory (409ms)
✔ add multiple signatories (414ms)
Advertisement