SolanaでTransaction simulation failed: Blockhash not foundエラー

環境

以下の環境で、トークンアカウント&トークン発行を試しているときに、トランザクションに失敗してしまうときがあった。

  • Solana x Anchor x React x Phantom
  • localnet(solana-test-validator)
  • Mac BigSur 11.6

具体的には、Anchorのtoken-proxyをReactに移行したときに発生した。anchor testは通るが、React x Phantomで実行すると失敗する。
project-serum / anchor / token-proxy.js

現象

PhantomでApproveダイアログが表示され、それを実行すると(トランザクションを走らせると)以下のエラーが表示される。

failed to send transaction: Transaction simulation failed: Blockhash not found

トランザクションは、以下のインストラクションをanchor.Provider.sendで送信したときにエラーとなった。

  async function createTokenAccountInstrs(
    provider,
    newAccountPubkey,
    mint,
    owner,
    lamports
  ) {
    if (lamports === undefined) {
      lamports = await provider.connection.getMinimumBalanceForRentExemption(165);
    }
    return [
      web3.SystemProgram.createAccount({
        fromPubkey: provider.wallet.publicKey,
        newAccountPubkey,
        space: 165,
        lamports,
        programId: TOKEN_PROGRAM_ID,
      }),
      TokenInstructions.initializeAccount({
        account: newAccountPubkey,
        mint,
        owner,
      }),
    ];
  }

原因(仮説多数あり)

SolanaのDiscordでは、「データ量が多い」「処理が早すぎる」といった理由で、最新のブロックハッシュが取得できていない、というコメントがあった。それをヒントにすると、今回はおそらく処理が早すぎることが原因と推測。

関連しそうな箇所としては、Phantomのシミュレート機能。

引用:Phantom Friendly transaction details

We now display more helpful information about a transaction before you approve it. By simulating the transaction, we are able to show you what assets may move in and out of your wallet as a result of submitting the transaction. We are also able to show you whether the transaction will fail, or whether it will have other dangerous consequences such as allowing others to spend your tokens.
承認する前に、トランザクションに関するより役立つ情報が表示されるようになりました。トランザクションをシミュレートすることにより、トランザクションを送信した結果としてウォレットに出入りする可能性のあるアセットを表示できます。また、トランザクションが失敗するかどうか、または他の人があなたのトークンを使用できるようにするなど、他の危険な結果をもたらすかどうかを示すこともできます。

トランザクションのシミュレーションをするためにlocalnetに接続しているが、そのシミュレーションが終わる前にトランザクションが先行して走ってしまっていることが原因と思われる?

 1. Simulate Transaction(Phantom) ← これが終わる前に次にステップにいってしまっている?
 ↓
 2. Send Transaction(React)
 ↓
 3. Confirm Transaction(React)

anchor testは正常にトランザクションが通って、React x Phantom経由だとエラーになるのはこのような背景だったため?

対応

(案1)Phantomのシミュレートを待つ

PhantomのApproveダイアログが表示されたときに、1分ほど表示したままにしてからApproveボタンを押すとトランザクションが通った。
sleepを入れようとしたが、トランザクション送信後(anchor.Provider.send)にエラーになるため、React側で処理ができなかった。

(案2)コミット変更

processedを利用していたが、以下を参考にfinalizedに変更する。これにより、完璧にトランザクションが通るようになった。

Solana Docs Configuring State Commitment#

const opts = {
  // preflightCommitment: "confirmed"
  preflightCommitment: "finalized"
}
const connection = new Connection(network, opts.preflightCommitment);

(案3)リトライ実装

試していないがリトライも有効とのこと。
Customizing Rebroadcast Logic

参考