2

I've been able to write a simple program to create some accounts using a generated keypair. But, when I've tried to modify things in an attempt to make the user the authority of the account and use a PDA with seeds to create the account, I am coming across this error Error: failed to send transaction: Transaction simulation failed: Error processing Instruction 0: Cross-program invocation with unauthorized signer or writable account. I figure something is wrong with my seeds or how I'm signing, but I haven't been able to find what it is... I also tried without adding a signer (as I thought it was implicit), but the error is the same. I'll attach my validator and my TS test below.

I'm also quite new to writing Anchor/Rust, so if anyone sees anything that could be better, or any best practices that should be adhered to, I would love to know!

Anchor validator:

#[derive(Accounts)]
#[instruction(username: String, profile_pic_url: String, background_pic_url: String)]
pub struct CreateInstructor<'info> {

    // Create account of type Instructor and assign instructor's pubkey as the payer
    // Seeded with instructorWalletPubKey + "instructor"
    #[account(
        init, 
        seeds = [authority.key().as_ref(), "instructor".as_bytes().as_ref()], 
        constraint = instructor.to_account_info().owner == program_id,
        bump,
        payer = authority, 
        space = Instructor::LEN
    )]
    pub instructor: Account<'info, Instructor>,

    // Define user as mutable - money in their account, description
    #[account(mut)]
    pub authority: Signer<'info>,


    // Ensure System Program is the official one from Solana and handle errors
    pub system_program: Program<'info, System>,
}

Test:

    it("creates an instructor account", async () => {
      const provider = anchor.AnchorProvider.env();
      anchor.setProvider(provider);

      const instructorSeeds = [
        provider.wallet.publicKey.toBuffer(),
        Buffer.from(anchor.utils.bytes.utf8.encode("instructor")),
      ];

      const [instructorPubKey, _] = await anchor.web3.PublicKey.findProgramAddress(
        instructorSeeds,
        program.programId
      );

      await program.methods
        .createInstructor("username", "profile pic url", "background pic url")
        .accounts({
          instructor: instructorPubKey,
          authority: provider.wallet.publicKey,
          systemProgram: anchor.web3.SystemProgram.programId,
        })
        .signers([(provider.wallet as anchor.Wallet).payer])
        .rpc();

      // Assertions
      const instructorAccount = await program.account.instructor.fetch(
        instructorPubKey
      );
      assert.equal(instructorAccount.username, "username");
      assert.equal(instructorAccount.profilePicUrl, "profile pic url");
      assert.equal(instructorAccount.backgroundPicUrl, "background pic url");
    });
12
  • This looks very similar to solana.stackexchange.com/questions/1583/… Oct 19, 2022 at 17:51
  • Thanks for taking a look, Steve! Could be; thanks for the link. But the error is a bit different, which led me to think otherwise. Instead of "signer privilege escalated," I'm getting "Error processing instruction 0." I'm still learning, so I assumed the errors weren't all that related. My fault for not posting its entirety; I just edited my original post to include this and changes to the code based on Ademola's suggestions. Oct 20, 2022 at 19:23
  • Your error message is from the client, and the other reporter's error is from the transaction log. How about this; get the serialized transaction from Anchor by replacing rpc() with transaction().serialize({requireAllSignatures: false, verifySignatures: false}) and then paste that transaction into explorer.solana.com/tx/inspector. That will let you simulate the transaction and read the logs (use the right cluster!). Oct 20, 2022 at 20:37
  • I'm an idiot... Thanks for pointing that out haha. I'm not quite familiar with debugging in this manner. I tried rewriting the rpc call as a transaction with an instruction like so: const latestBlockhash = await connection.getLatestBlockhash("finalized"); const txn = new anchor.web3.Transaction({ feePayer: provider.wallet.publicKey, ...latestBlockhash, }); // txn.add(ixn); txn.serialize({ requireAllSignatures: false, verifySignatures: false }); And logging the txn. I'm looking up (on devnet) the recent blockhash, but don't see any logs Oct 20, 2022 at 21:47
  • 1
    @steveluscher I'm thinking that my provider might be messed up here in my tests. In the past when I had success, I noticed that my provider wallet was on the filesystem, but I realize now that it's using my Phantom wallet. Could that play a role? I'm using const provider = anchor.AnchorProvider.env(); anchor.setProvider(provider); to get my provider, but it's still using my Phantom wallet in the test as opposed to the FS wallet (and what solana config get outputs). Nov 1, 2022 at 22:28

2 Answers 2

1

As far as I know, this is how PDAs are specified in the Rust program and derived in the client.

Rust:

..
seeds = [authority.key().as_ref(), "instructor".as_bytes().as_ref()]
..

Js/Ts:

const instructorSeeds = [
   provider.wallet.publicKey.toBuffer(),
   Buffer.from(anchor.utils.bytes.utf8.encode("instructor")),
];

b"instructor" and "instructor".as_bytes() mean the same thing and are interchangeable.

then:

const [instructorPubKey, _] = await anchor.web3.PublicKey.findProgramAddress(
  instructorSeeds,
  program.programId
);

The anchor.web3.PublicKey.findProgramAddress function returns a tuple, not a constant. const [instructorPubkey, _] is the proper way to get your derived pubkey value.

6
  • I actually blindly tried that previously too, but I get the same error. So, I'm thinking the issue may be something else... For educational purposes though, why exactly use anchor.utils.bytes.utf8.encode("instructor") as opposed to Buffer.from("instructor")? I feel like I've seen both being used. Does it have to do with "instructor".as_bytes() being used in the seed as opposed to b"instructor"? Oct 13, 2022 at 19:06
  • I'll update my answer to give what I think is the best solution. It involves changing your anchor program though
    – Ademola
    Oct 13, 2022 at 20:55
  • Just tried it, but the error stays the same. Thanks for clarifying about b"instructor"! May I ask why the change to the seeds is necessary? Just curious! As in, why the seeds is changed to ....as_bytes().as_ref() and why we use Buffer,from(...encode())? I'll see if I can find anything in the docs too. But yeah, I'm quite puzzled about this... It was working before adding the seeds, and I've modified it from that point twice and gotten to this same error both times haha Oct 13, 2022 at 21:42
  • It isn't necessary. As far as I've seen most people(myself included) just prefer passing in each seed as a reference. As for the error persisting, make sure to rebuild and redeploy your contract before testing again.
    – Ademola
    Oct 15, 2022 at 19:28
  • Understood. I've been running anchor test, which I thought built, deployed, and tested, but I also tried explicitly with anchor build and anchor deploy, but still no luck. Oct 16, 2022 at 19:17
1

I solved the issue, but I actually don't know exactly how. I think trying to solve it led me to more issues and I'm unsure what actually wound up fixing it. What I did notice was that in trying to fix it so many times (and being a noob), I think I redeployed my program. After noticing this, copying the new program ID in my app/ directory, Anchor.toml, and lib.rs, and retrying the RPC call, it worked. I assume at the start my seeds were also messed up. But, unfortunately, I couldn't with confidence find the root cause.

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.