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");
});
rpc()
withtransaction().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!).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 logsconst 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 whatsolana config get
outputs).