1

I've looked around and I see this is a common error that devs run into based on the fact that an account was already initialized. I'm just wondering how to fix it so that I can run tests without running into this error. I'm testing on localnet and I've already tried deleting the test-ledger folder and running solana-test-validator -r but neither works. So here is my code. The first two tests pass, it's the third test where I think the gameAccountPDA is being used again that's causing the failure.

GameTest.ts

const provider = anchor.AnchorProvider.env();
  const ownerWallet = provider.wallet as anchor.Wallet;
  console.log('ownerWallet: ', ownerWallet.publicKey.toString())
  anchor.setProvider(provider)
  const systemProgram = SystemProgram.programId;
  const program = anchor.workspace.Game as Program<Game>;

    let [playerAccount,] = web3.PublicKey.findProgramAddressSync(
        [ownerWallet.publicKey.toBuffer()],
        program.programId
    );
    console.log( 'player account: ', playerAccount.toString())

    let [counterAccount,] = web3.PublicKey.findProgramAddressSync(
        [ownerWallet.publicKey.toBuffer()],
        program.programId
    );
    console.log( 'counter account: ', counterAccount.toString())

  // PDA's
  let [gameAcctPDA,] = web3.PublicKey.findProgramAddressSync(
      [Buffer.from( "game" ), ownerWallet.publicKey.toBuffer()],
      program.programId
  );
  console.log('gameAcct: ', gameAcctPDA.toString())

  let [wordVaultAcctPDA,] = web3.PublicKey.findProgramAddressSync(
      [Buffer.from("word-vault"), ownerWallet.publicKey.toBuffer()],
      program.programId
  );
  console.log('wordVaultAcct: ', wordVaultAcctPDA.toString())

  let [moneyVaultAcctPDA,] = web3.PublicKey.findProgramAddressSync(
      [Buffer.from("money-pot")],
      program.programId
  );
  console.log('moneyVaultAcct: ', moneyVaultAcctPDA.toString())


  beforeEach( async () => {
    // await airdrop(owner.publicKey, provider);
  } );

  it( 'Game Is Created along with Word Vault and Game Pot.', async (): Promise<void> => {
    const gameName = 'SayWord';
    const minDeposit = 1;

    let transaction = await program.methods.initializeCreateGame(gameName, minDeposit)
        .accounts( {
          game: gameAcctPDA,
          wordVault: wordVaultAcctPDA,
          moneyPotVault: moneyVaultAcctPDA,
          owner: ownerWallet.publicKey,
          systemProgram: systemProgram
        } )
        .signers([ownerWallet.payer])
        .rpc();

    console.log("Your transaction signature: ", transaction);

    const gameData = await program.account.gameData.fetch( gameAcctPDA );
    const wordVault = await program.account.wordVault.fetch( wordVaultAcctPDA );
    const moneyPot = await program.account.gamePot.fetch( moneyVaultAcctPDA );

    expect( gameData.name ).to.equal( gameName );
    expect( gameData.minDeposit ).to.equal( minDeposit );
    expect( gameData.owner.toString() ).to.deep.equal(ownerWallet.publicKey.toString());
    expect( gameData.totalGamesWon ).to.equal( 0 );
    expect( wordVault.secretWord ).to.be.empty;
    expect( moneyPot.totalWinnings.toNumber() ).to.equal( 0 );
  } );

  it( 'Secret word is added to the word vault.', async (): Promise<void> => {
    const secretWord = 'dog';

    let transaction = await program.methods.addSecretWord( secretWord )
        .accounts({
          wordVault: wordVaultAcctPDA,
          owner: ownerWallet.publicKey,
          systemProgram: systemProgram
        })
        .signers([ ownerWallet.payer ])
        .rpc()

    console.log( "Your transaction signature: ", transaction );

    const wordVault = await program.account.wordVault.fetch( wordVaultAcctPDA );

    expect( wordVault.secretWord ).to.equal( secretWord );
    expect( wordVault.secretWord ).to.not.be.empty;
  });

  it( 'Player account created to join the game', async (): Promise<void> => {
      let transaction = await program.methods.initializeJoinGame()
          .accounts({
              player: playerAccount,
              tokenCounter: counterAccount,
              gameAccount: gameAcctPDA,
              owner: ownerWallet.publicKey,
              systemProgram: systemProgram
          })
          .signers([ ownerWallet.payer ])
          .rpc()

      console.log( "Your transaction signature: ", transaction );

      const onChainPlayer = await program.account.player.fetch( playerAccount );
      const counter = await program.account.counter.fetch( counterAccount );

      expect( onChainPlayer.owner ).to.equal( ownerWallet.publicKey );
      expect( onChainPlayer.gameAccount ).to.equal( gameAcctPDA );
      expect( onChainPlayer.tokenCount ).to.equal(0 );
      expect( onChainPlayer.gamesWon ).to.equal(0 );
      expect( onChainPlayer.depositedAmount ).to.equal(0 );

      expect( counter.count ).to.equal( 50 )
  });

create_game.rs - Passes

#[derive(Accounts)]
pub struct CreateGame<'info> {
    #[account(
    init,
    payer = owner,
    space = 8 + GameData::MAX_SIZE,
    seeds = [b"game", owner.key().as_ref()],
    bump
    )]
    pub game: Account<'info, GameData>,
    #[account(
    init_if_needed,
    seeds = [b"word-vault", owner.key().as_ref()],
    bump,
    payer = owner,
    space = 8 + WordVault::MAX_SIZE
    )]
    pub word_vault: Account<'info, WordVault>,
    #[account(
    init_if_needed,
    payer = owner,
    seeds = [b"money-pot"],
    space = 8 + GamePot::MAX_SIZE,
    bump,
    )]
    pub money_pot_vault: Account<'info, GamePot>,
    #[account(mut)]
    pub owner: Signer<'info>,
    pub system_program: Program<'info, System>,
}

pub fn handle_create_game(ctx: Context<CreateGame>, name: String, min_deposit: u8) -> Result<()> {
    let game = &mut ctx.accounts.game;
    let money_pot = &mut ctx.accounts.money_pot_vault;

    game.name = name;
    msg!("Game name: {}", game.name);

    game.min_deposit = min_deposit;
    msg!("Minimum deposit: {}", game.min_deposit);

    game.owner = *ctx.accounts.owner.key;
    msg!("Game owner: {}", game.owner);

    game.total_games_won = 0;
    msg!("Total Games Won: {}", game.total_games_won);

    money_pot.total_winnings = 0;
    msg!("Total Winnings: {}", money_pot.total_winnings);

    Ok(())

join_game.rs - fails

#[derive(Accounts)]
pub struct JoinGame<'info> {
    #[account(
    init_if_needed,
    payer = owner,
    seeds = [owner.key().as_ref()],
    space = 8 + Player::MAX_SIZE,
    bump
    )]
    pub player: Account<'info, Player>,
    #[account(
    init,
    seeds = [owner.key().as_ref()],
    payer = owner,
    space = 8 + Counter::MAX_SIZE,
    bump
    )]
    pub token_counter: Account<'info, Counter>,
    #[account(mut)]
    pub game_account: Account<'info, GameData>,
    #[account(mut)]
    pub owner: Signer<'info>,
    pub system_program: Program<'info, System>,
}

pub fn handle_join_game(ctx: Context<JoinGame>) -> Result<()> {
    let player = &mut ctx.accounts.player;
    let game = &mut ctx.accounts.game_account;

    // Counter to track number of tokens purchased to use the GPT model
    let counter = &mut ctx.accounts.token_counter;

    game.total_players = game.total_players.checked_add(1).unwrap();
    counter.owner = *ctx.accounts.owner.key;
    counter.count = counter.count.checked_add(50).unwrap(); // Initially provides 50 free tokens.
    msg!("Your Initial Token Counter Is Set To: {}", counter.count);

    // Game account that the player is joining
    player.game_account = *ctx.accounts.game_account.to_account_info().key;
    msg!("Joined Game: {}", player.game_account);

    player.games_won = 0;
    msg!("Your Player Account: {}", player.games_won);

    player.deposited_amount = 0;
    msg!("Initial Deposit Amount: {}", player.deposited_amount);

    player.token_count = 0;
    msg!("Initial Chat Token Amount: {}", player.token_count);

    Ok(())

1 Answer 1

3

In join_game, why are you initializing the PDA again? Presumably this already exists after game is created?

Pass it in as an account without the initialize? You should already know the PDA address at the point you are joining a game.

Sorry if I missed some important detail here and if this is a non sequiter answer for you.

EDIT: I misread the code originally and I am retracting this response. It appears you are using the same seed twice for two different accounts in the join_game.rs. Didn't hear back but I assume this was the problem.

5
  • I thought by using #[account(mut)] pub game_account: Account<'info, GameData> I'd be doing that without initializing it because I need to set state data during this account, so wouldn't GameData need to be passed into the instruction? I just figured out the issue I think, I'm using seeds = [owner.key().as_ref()] for both the Player and Counter accounts which generates identical PublicKeys which is why it says it's already been initialized. Hence 0x0 error. I just added unique 'b'player' seeds. Although doesn't that make it a PDA?
    – Brian M.
    Commented Dec 19, 2023 at 19:29
  • Did adding the b,’player’ to the second seed make it work? Yes it’s a PDA. You are storing the token counter value. I was going to suggest adding the b’string’ seed only because I’ve never used a single account key as a seed with no other values, but you seem to have gotten that. I will be in front of a computer in 15 minutes and I can probably Susa it out if you don’t or someone else doesn’t jump in and solve before then. But did the additional seed make it work? Commented Dec 19, 2023 at 20:12
  • 1
    Ok I am in front of computer and can acutally see the code now. These two acounts are using the same seed? So I assume adding the b'player' seed worked? #[account( init_if_needed, payer = owner, seeds = [owner.key().as_ref()], space = 8 + Player::MAX_SIZE, bump )] pub player: Account<'info, Player>, #[account( init, seeds = [owner.key().as_ref()], payer = owner, space = 8 + Counter::MAX_SIZE, bump )] pub token_counter: Account<'info, Counter>, Commented Dec 19, 2023 at 20:31
  • David's previous comment sounds like the answer! Your playerAccount and counterAccount PDAs are actually deriving the same address.
    – john
    Commented Dec 19, 2023 at 21:14
  • Yep that was it!
    – Brian M.
    Commented Dec 19, 2023 at 21:42

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.