2

I am having a problem initializing a PDA for a program I am working on. This all all worked before I attempted to add in an authority and require a signer to prevent unauthorized access. In the past I ran this code once to establish a single PDA, and then just hard coded that address into the test, updating the one account but then creating no more accounts. But for the first run I use keypair generate to get an address and then use the pubkey as the PDA (I have read this is less than optimal). I am getting an unknown signer error.

Here is the code.


#[derive(Accounts)]
pub struct AccountStruct<'info> {
    #[account(init, payer=authority, space = 50)]
    pub storage_account: Account<'info, GameState>,
    #[account(mut)]
    pub authority: Signer<'info>,
    pub system_program: Program<'info, System>,
}


#[account]
pub struct GameState {
    pub time_of_last_update: i64,
    pub current_state: i8, 
    pub wheel_speed: i8, 
    pub authority: Pubkey,
}


#[derive(Accounts)]
pub struct UpdateValues<'info> {

    |   #[account(mut, has_one = authority)]
    |   pub storage_account: Account<'info, GameState>,
    |   pub authority : Signer<'info>,



    |   //authority: Pubkey,
}

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

    pub fn initialize (ctx: Context<AccountStruct>) -> Result<()> {

    |   Ok(())


    }   


And the TS:

const provider = anchor.AnchorProvider.env();
  anchor.setProvider(provider);
  const program = anchor.workspace.Looptest as Program<Looptest>;
  const storageAccount = anchor.web3.Keypair.generate(); 
  //const storageAccount = new anchor.web3.PublicKey(''); 
  
  
  it("Is Initialized", async () => {
  
        await program.methods.initialize()
            .accounts({
                storageAccount: storageAccount.publickey,
                authority: provider.wallet.publicKey,
                systemProgram: anchor.web3.SystemProgram.programId,
            }).
            signers([storageAccount])
            .rpc()
            });

    
            //console.log("Transaction Signature -> ", tx);
        console.log("Storage Account Public Key -> ", storageAccount.publickey);        
        

Why am I getting the following test error?

    1. looptest Is Initialized: Error: unknown signer: B8g4amuQepUBrn542kgxH6B6utwgv47oZZ348bYU8T7J at Transaction._addSignature (node_modules/@solana/web3.js/src/transaction/legacy.ts:722:13) at forEach (node_modules/@solana/web3.js/src/transaction/legacy.ts:698:12) at Array.forEach () at Transaction._partialSign (node_modules/@solana/web3.js/src/transaction/legacy.ts:696:13) at Transaction.partialSign (node_modules/@solana/web3.js/src/transaction/legacy.ts:688:10) at /home/infinity/looptest/node_modules/@project-serum/anchor/src/provider.ts:143:10 at Array.forEach () at AnchorProvider.sendAndConfirm (node_modules/@project-serum/anchor/src/provider.ts:142:21) at processTicksAndRejections (node:internal/process/task_queues:95:5) at MethodsBuilder.rpc [as _rpcFn] (node_modules/@project-serum/anchor/src/program/namespace/rpc.ts:29:16)

3 Answers 3

4

Not sure why your test isn't working, but here's a reference using Solana Playground: https://beta.solpg.io/63e5aed067edfe0f001069b4

use anchor_lang::prelude::*;

// This is your program's public key and it will update
// automatically when you build the project.
declare_id!("5jvcsLdHs1a8tWsWh3zRCmrtxRZM8XH3eJvnfiLhznmt");

#[program]
mod hello_anchor {
    use super::*;
    pub fn initialize(ctx: Context<AccountStruct>) -> Result<()> {
        ctx.accounts.storage_account.authority = ctx.accounts.authority.key();
        Ok(())
    }
}

#[derive(Accounts)]
pub struct AccountStruct<'info> {
    #[account(init, payer=authority, space = 50)]
    pub storage_account: Account<'info, GameState>,
    #[account(mut)]
    pub authority: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[account]
pub struct GameState {
    pub time_of_last_update: i64,
    pub current_state: i8,
    pub wheel_speed: i8,
    pub authority: Pubkey,
}

You can build and deploy the program directly on Solpg, and then test the instruction either using the Test UI, or in the anchor.test.ts file included with Solpg.

// No imports needed: web3, anchor, pg and more are globally available

describe("Test", () => {
  it("initialize", async () => {
    // Generate keypair for the new account
    const storageAccount = new web3.Keypair();

    // Send transaction
    const txHash = await pg.program.methods
      .initialize()
      .accounts({
        storageAccount: storageAccount.publicKey,
        authority: pg.wallet.publicKey,
        systemProgram: web3.SystemProgram.programId,
      })
      .signers([storageAccount])
      .rpc();
    console.log(`Use 'solana confirm -v ${txHash}' to see the logs`);

    // Confirm transaction
    await pg.connection.confirmTransaction(txHash);

    // Fetch the created account
    const newAccount = await pg.program.account.gameState.fetch(
      storageAccount.publicKey
    );

    assert.equal(
      newAccount.authority.toBase58(),
      pg.wallet.publicKey.toBase58()
    );

     console.log(newAccount.authority.toBase58());
     console.log(storageAccount.publicKey.toBase58());
  });
});

Also, currently your initialize instruction creates an account using a keypair instead of a Program Derived Address (PDA). If you wanted to create an account using a PDA in Anchor, you can use the seeds and bump constraints.

Here is an example where the PDA for the storage_account is derived using the authority public key as a seed. Note that in this example, a user would only be able to call the initialize instruction once. If they tried to call the initialize instruction again, the instruction would fail since an account using that specific PDA would already exists: https://beta.solpg.io/63e5b05c67edfe0f001069b5

use anchor_lang::prelude::*;

// This is your program's public key and it will update
// automatically when you build the project.
declare_id!("5jvcsLdHs1a8tWsWh3zRCmrtxRZM8XH3eJvnfiLhznmt");

#[program]
mod hello_anchor {
    use super::*;
    pub fn initialize(ctx: Context<AccountStruct>) -> Result<()> {
        ctx.accounts.storage_account.authority = ctx.accounts.authority.key();
        Ok(())
    }
}

#[derive(Accounts)]
pub struct AccountStruct<'info> {
    #[account(init, seeds=[authority.key().as_ref()], bump, payer=authority, space = 50)]
    pub storage_account: Account<'info, GameState>,
    #[account(mut)]
    pub authority: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[account]
pub struct GameState {
    pub time_of_last_update: i64,
    pub current_state: i8,
    pub wheel_speed: i8,
    pub authority: Pubkey,
}

Here's the test, note that the address for the storageAccount is found using findProgramAddress:

// No imports needed: web3, anchor, pg and more are globally available

describe("Test", () => {
  it("initialize", async () => {
    // Generate keypair for the new account
    const storageAccount = new web3.Keypair();

    const [pda] = await web3.PublicKey.findProgramAddress(
      [pg.wallet.publicKey.toBuffer()],
      pg.PROGRAM_ID
    );

    // Send transaction
    const txHash = await pg.program.methods
      .initialize()
      .accounts({
        storageAccount: pda,
        authority: pg.wallet.publicKey,
        systemProgram: web3.SystemProgram.programId,
      })
      .rpc();
    console.log(`Use 'solana confirm -v ${txHash}' to see the logs`);

    // Confirm transaction
    await pg.connection.confirmTransaction(txHash);

    // Fetch the created account
    const newAccount = await pg.program.account.gameState.fetch(pda);

    assert.equal(
      newAccount.authority.toBase58(),
      pg.wallet.publicKey.toBase58()
    );

    console.log(newAccount.authority.toBase58());
  });
});

Here are some PDA related resources:

0
0
const txId = await program.rpc.initialize({
  accounts: {
    authority: provider.wallet.publicKey,
    storageAccount: storageAccount.publicKey,
    systemProgram: anchor.web3.SystemProgram.programId,
  },
  signers: [storageAccount],
});
0

Make the user sign the transaction:

pub user: Signer<'info>,

In your test or frontend:

            await program.methods
                .initUserPda()
                .accounts({
                    user: user1.publicKey,
                    program_authority: program_authority,
                    //systemProgram: SystemProgram.programId,
                })
                .signers([user1])
                .rpc();

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.