2

So if I anchor build and then anchor deploy on the devnet and then use anchor test --skip-build --skip-deploy since I already built and deployed everything before, I find the first time around the all three tests pass with zero simulation errors. However second time around, I get an error during the simulation phase but the three tests in question still pass:

candid@DESKTOP-5D7VSM9:~/platform$ anchor test --skip-build --skip-deploy

Found a 'test' script in the Anchor.toml. Running it as a test suite!

Running test suite: "/home/candid/platform/Anchor.toml"

yarn run v1.22.22
warning package.json: No license field
$ /home/candid/platform/node_modules/.bin/ts-mocha -p ./tsconfig.json -t 1000000 'tests/**/*.ts'


  counter
SendTransactionError: failed to send transaction: Transaction simulation failed: Error processing Instruction 0: custom program error: 0x0
    at Connection.sendEncodedTransaction (/home/candid/platform/node_modules/@solana/web3.js/src/connection.ts:5923:13)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at Connection.sendRawTransaction (/home/candid/platform/node_modules/@solana/web3.js/src/connection.ts:5880:20)
    at sendAndConfirmRawTransaction (/home/candid/platform/node_modules/@coral-xyz/anchor/src/provider.ts:370:21)
    at AnchorProvider.sendAndConfirm (/home/candid/platform/node_modules/@coral-xyz/anchor/src/provider.ts:160:14)
    at MethodsBuilder.rpc [as _rpcFn] (/home/candid/platform/node_modules/@coral-xyz/anchor/src/program/namespace/rpc.ts:29:16) {
  logs: [
    'Program AzkCnsvxXkMFSBK2H91BfeLVvRf4QQDcEjEvenD3kkNK invoke [1]',
    'Program log: Instruction: Initialize',
    'Program 11111111111111111111111111111111 invoke [2]',
    'Allocate: account Address { address: B1PePsqbRFDUwaYkkFcpKTW1LytbJ4yq75ZwtwHvhSom, base: None } already in use',
    'Program 11111111111111111111111111111111 failed: custom program error: 0x0',
    'Program AzkCnsvxXkMFSBK2H91BfeLVvRf4QQDcEjEvenD3kkNK consumed 6561 of 200000 compute units',
    'Program AzkCnsvxXkMFSBK2H91BfeLVvRf4QQDcEjEvenD3kkNK failed: custom program error: 0x0'
  ],
  programErrorStack: ProgramErrorStack {
    stack: [
      [PublicKey [PublicKey(AzkCnsvxXkMFSBK2H91BfeLVvRf4QQDcEjEvenD3kkNK)]],
      [PublicKey [PublicKey(11111111111111111111111111111111)]]
    ]
  }
}
    ✔ Is initialized! (839ms)
Transaction Signature: rBK4seF4vbXEJjLz7uVGHVx27VGeejgpxgfdsLsDaac7jYjc4WJh5DeBfgesN9uqCgipiLKDL9p47rjJVtZZSYU
Count: 1
    ✔ Increment (720ms)
Transaction Signature: 2dntBqvUgHfdN7bh6kZEdRLrh7Bs7UADjWZcwMouBbH2hAhHGKNMdMVgGnYaV35SRkjdp2wS4tjsEEZJgi76afMr
Count after decrement: 0
    ✔ Decrement (540ms)


  3 passing (2s)

Done in 3.87s.

Should I worry about this? It looks like it's happening because I am attempting to initialize an already initialized account. Since a constant seed is being used, the same address is being used on each test. How do I reset this without having to redeploy the contract on devnet? Or is that not possible?

Would it have any negative effect to the enduser that is interacting with the smart contract through the frontend once I implement it? They will only be able to increment or decrement the value on the frontend.

Here is my Rust on chain code:

use anchor_lang::prelude::*;

declare_id!("AzkCnsvxXkMFSBK2H91BfeLVvRf4QQDcEjEvenD3kkNK");

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

    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
        let counter = &mut ctx.accounts.counter;
        counter.bump = ctx.bumps.counter; // store bump seed in `Counter` account
        msg!("Counter account created! Current count: {}", counter.count);
        msg!("Counter bump: {}", counter.bump);
        Ok(())
    }

    pub fn increment(ctx: Context<Increment>) -> Result<()> {
        let counter = &mut ctx.accounts.counter;
        msg!("Previous counter: {}", counter.count);
        counter.count = counter.count.checked_add(1).unwrap();
        msg!("Counter incremented! Current count: {}", counter.count);
        Ok(())
    }


    pub fn decrement(ctx: Context<Decrement>) -> Result<()> {
        let counter = &mut ctx.accounts.counter;
        msg!("Previous counter: {}", counter.count);
        counter.count = counter.count.checked_sub(1).ok_or(ErrorCode::CounterUnderflow)?;
        msg!("Counter decremented! Current count: {}", counter.count);
     Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(mut)]
    pub user: Signer<'info>,

    // Create and initialize `Counter` account using a PDA as the address
    #[account(
        init,
        seeds = [b"counter"], // optional seeds for pda
        bump,                 // bump seed for pda
        payer = user,
        space = 8 + Counter::INIT_SPACE
    )]
    pub counter: Account<'info, Counter>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct Increment<'info> {
    // The address of the `Counter` account must be a PDA derived with the specified `seeds`
    #[account(
        mut,
        seeds = [b"counter"], // optional seeds for pda
        bump = counter.bump,  // bump seed for pda stored in `Counter` account
    )]
    pub counter: Account<'info, Counter>,
}

#[derive(Accounts)]
pub struct Decrement<'info> {
    #[account(
        mut,
        seeds = [b"counter"], // optional seeds for pda
        bump = counter.bump,  // bump seed for pda stored in `Counter` account
    )]
    pub counter: Account<'info, Counter>,
}

// Add this error code to handle underflow when decrementing
#[error_code]
pub enum ErrorCode {
    #[msg("The counter cannot be decremented below zero.")]
    CounterUnderflow,
}

#[account]
#[derive(InitSpace)]
pub struct Counter {
    pub count: u64, // 8 bytes
    pub bump: u8,   // 1 byte
}

Here is my typescript for Anchor Test:

import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { Platform } from "../target/types/platform";
import { PublicKey } from "@solana/web3.js";

describe("counter", () => {
  // Configure the client to use the local cluster.
  const provider = anchor.AnchorProvider.env();
  anchor.setProvider(provider);

  const program = anchor.workspace.Platform as Program<Platform>;
  const wallet = provider.wallet as anchor.Wallet;
  const connection = provider.connection;

  const [counterPDA] = PublicKey.findProgramAddressSync(
    [Buffer.from("counter")],
    program.programId
  );

  it("Is initialized!", async () => {
    try {
      const txSig = await program.methods.initialize().rpc();

      const accountData = await program.account.counter.fetch(counterPDA);
      console.log(`Transaction Signature: ${txSig}`);
      console.log(`Count: ${accountData.count}`);
    } catch (error) {
      // If PDA Account already created, then we expect an error
      console.log(error);
    }
  });

  it("Increment", async () => {
    const transactionSignature = await program.methods.increment().rpc();

    const accountData = await program.account.counter.fetch(counterPDA);

    console.log(`Transaction Signature: ${transactionSignature}`);
    console.log(`Count: ${accountData.count}`);
  });

  it("Decrement", async () => {

    // Now call the decrement method
    const transactionSignature = await program.methods.decrement().rpc();

    // Fetch the updated account data
    const accountData = await program.account.counter.fetch(counterPDA);

    console.log(`Transaction Signature: ${transactionSignature}`);
    console.log(`Count after decrement: ${accountData.count}`);
  });

});

I am using Anchor 0.29.0 because I'm still unable to get 0.30.0 to interface correctly with my frontend, but 0.29.0 works fine.

2 Answers 2

2

This is an expected behaviour. At least based on the code you provided. The test says If PDA Account already created, then we expect an error.

The error you receive is 0x0

'Allocate: account Address { address: B1PePsqbRFDUwaYkkFcpKTW1LytbJ4yq75ZwtwHvhSom, base: None } already in use',
'Program 11111111111111111111111111111111 failed: custom program error: 0x0',

For the SystemProgram it means that you try to initialize an account (data at a particular public key address) that has been just initialized. As you run the test against the devnet we can assume the first time the test was run the test account was created and now it exists on-chain. When you try to create it again it fails as not possible.

However, the test code expects it may happen and does not consider it as an error. It prints that as an error message but considers it as normal.

    } catch (error) {
      // If PDA Account already created, then we expect an error
      console.log(error);
    }

I assume the logging is not appropriate for the expected processing but from the code perspective, it seems all fine.

1

I would recommend using SolanaProgramTest to test Solana programs. If you're using devnet, you'll have to redeploy under a different address or change the seeds between each initialization of an account with constant seeds, because it'll already have been initialized on devnet. With SolanaProgramTest, you won't have to worry about this. It's also much, much faster than deploying on devnet every time you want to run tests on new functionality.

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.