AnchorでPDAを作成すると「custom program error: 0x0」エラー

PDA(Program Derived Addresses) を試していた時に発生した現象。

(補足)ポイント

以下2点が本現象のポイント

  • 「% anchor test」を実施したときは正常に動作するが、「% anchor test --skip-local-validator」でlocalnetにデプロイすると本現象が発生する。
  • キーペアの生成(anchor.web3.Keypair.generate())をして利用した場合は正常に動作するが、自分のKey(anchor.AnchorProvider.local().wallet.publicKey)を使うと本現象が発生する。

現象

solana-test-validatorを起動して、以下を実行すると「custom program error: 0x0」エラーが発生。

% anchor test --skip-local-validator

  0 passing (325ms)
  1 failing

  1) hello
       Creates a User Stats:
     Error: failed to send transaction: Transaction simulation failed: Error processing Instruction 0: custom program error: 0x0
      at Connection.sendEncodedTransaction (node_modules/@solana/web3.js/src/connection.ts:4506:13)
      at processTicksAndRejections (node:internal/process/task_queues:96:5)
      at Connection.sendRawTransaction (node_modules/@solana/web3.js/src/connection.ts:4465:20)
      at sendAndConfirmRawTransaction (node_modules/@project-serum/anchor/src/provider.ts:288:21)
      at AnchorProvider.sendAndConfirm (node_modules/@project-serum/anchor/src/provider.ts:148:14)
      at MethodsBuilder.rpc [as _rpcFn] (node_modules/@project-serum/anchor/src/program/namespace/rpc.ts:29:16)



error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
 *  Terminal will be reused by tasks, press any key to close it. 

原因

「custom program error: 0x0」は、すでにアカウントが作成されている、という意味らしい。つまり、すでにPDAアカウントがすでに作成されているため、本エラーが発生している。

「% anchor test --skip-local-validator」を利用していたため、最初にPDAアカウント作成して、それがずっと残っていた。

補足

以下の原因は、anchor testをすると、毎回リセットされた状態になるためエラーが発生しなかった。

- 「% anchor test」を実施したときは正常に動作するが、「% anchor test --skip-local-validator」でlocalnetにデプロイすると本現象が発生する。

以下の原因は、キーペアを新規作成していたため、PDAアカウントが作成されていなかったため、エラーが発生しなかった。

- キーペアの生成(anchor.web3.Keypair.generate())をして利用した場合は正常に動作するが、自分のKey(anchor.AnchorProvider.local().wallet.publicKey)を使うと本現象が発生する。

対応

PDAアカウントが作成されているか確認する

PDAアカウントをfetchして、本当に作成されているのか確認する。

    const [userStatsPDA, _] = await PublicKey.findProgramAddress(
      [
        anchor.utils.bytes.utf8.encode('user-stats'),
        provider.wallet.publicKey.toBuffer(),
        // user.publicKey.toBuffer(),
      ],
      program.programId
    );

   let fetchUserStats = await program.account.userStats.fetch(userStatsPDA);
    console.log(fetchUserStats);

test-ledgerを削除する

test-ledgerフォルダにデータが入っているため、一旦削除してから、「% anchor test --skip-local-validator」を実行する。
1回目の実行は成功して(PDAアカウントが正常に作成されている)、2回目を実行すると0x0エラーになるはず(すでにPDAアカウントが作成されていて、そのデータがtest-ledgerフォルダに入っているため)。