3MIKAN
仮想通貨直コン

viemで失敗トランザクションを調べる|取引結果・入力データ・エラーの確認順

失敗したトランザクションを、取引結果、入力データ、シミュレーション、送信順番号の順に調べる方法を、viemのコードと具体例で解説します。

取引ハッシュから実行結果、入力データ、シミュレーション結果、送信順番号を確認する流れ

送金やコントラクト操作が失敗したとき、最初から難しいエラー名だけを探すと、かえって原因を見失いやすくなります。

まずは、取引がブロックに入ったか、何を実行しようとしたか、同じ条件で試すと何が起きるかを順番に確認します。

イメージは荷物の追跡です。伝票番号から「受付済みか」「届け先と内容は正しいか」「途中で止まった理由は何か」「同じ受付番号の別便がないか」を確認するのと似ています。

この記事では、実行結果 → 入力データ → シミュレーション → 送信順番号の順で調べます。秘密鍵をコードへ貼り付ける必要はありません。読み取り専用のPublic Clientとトランザクションハッシュがあれば進められます。

先に用語を普通の言葉で確認する

用語 普通の言葉でいうと 主に分かること
取引情報(transaction 何を実行しようとしたかを書いた注文票 送信元、送信先、入力内容、送信順番号、手数料条件
実行結果(transaction receipt) ブロックチェーンが返す受領票 確定したブロック、成功・失敗、使用したガス、発生したログ
入力データ(calldata) コントラクトへ渡した指示書 呼び出した関数と引数
シミュレーション 実際には送信せず、同じ条件で行う試運転 権限不足、残高不足、期限切れなどのエラー
送信順番号(nonce) 同じアカウントから送る取引の整理番号 保留中、置換済み、別ハッシュの可能性

英語の名前は、コードやExplorerの表示と照合するために残しています。最初は「注文票・受領票・指示書・試運転・整理番号」と考えれば十分です。

取引の解析を実行結果、入力データ、シミュレーション、送信順番号の順に進める流れ

まず確認する4項目

項目 確認できること 次に判断すること
取引情報 fromtoinputnonce、手数料条件 呼び出した関数と送信条件が正しいか
実行結果 ブロック、statusgasUsed、ログ 確定済みか、実行途中で取り消されたか
シミュレーション 現在または指定ブロックでの試運転結果 権限、残高、利用許可額、期限などに問題がないか
送信順番号 latestpendingの取引数 未確定、置換、別ハッシュの可能性があるか

実行結果が取得できない場合、すぐに「失敗した」とは断定できません。まだ未確定、参照しているチェーンが違う、ノードがそのハッシュを知らない、同じ送信順番号の別取引へ置き換わった、という可能性も残ります。

具体例:ガスの問題に見えても、原因が利用許可額の場合がある

たとえば、ウォレットでトークン交換をしようとして「ガスを見積もれません」と表示されたとします。

この場合、ガス代を上げれば直るとは限りません。同じ送信元・送信先・数量でシミュレーションすると、「このコントラクトが使えるトークン量の許可が足りない」と分かることがあります。

そのとき確認すべきなのはガス代ではなく、トークンの利用許可額(allowance)です。このように、画面上の短いエラーと本当の原因が違うことがあるため、順番に証拠を確認します。

1. 取引情報と実行結果を分けて取得する

取引情報には「何を送ろうとしたか」が入り、実行結果には「実際にどうなったか」が入ります。まず、この2つを分けて取得します。

const transaction = await publicClient.getTransaction({ hash })

const receipt = await publicClient.getTransactionReceipt({ hash })
console.log({
  from: transaction.from,
  to: transaction.to,
  nonce: transaction.nonce,
  input: transaction.input,
  blockNumber: receipt.blockNumber,
  status: receipt.status,
  gasUsed: receipt.gasUsed,
})

receipt.statusrevertedなら、取引はブロックに取り込まれましたが、コントラクトの処理は途中で取り消されています。

ただし、実行結果だけでは具体的なエラー名まで分からない場合があります。次に、取引情報のinputへ入っている入力データを確認します。

2. 入力データから呼び出した関数を特定する

入力データ(calldata)は、コントラクトへ渡した指示書です。先頭4バイトには呼び出す関数の目印が入り、その後ろに引数が続きます。

正しいABIがある場合は、decodeFunctionDataで関数名と引数を復元できます。

import { decodeFunctionData } from 'viem'

const decoded = decodeFunctionData({
  abi,
  data: transaction.input,
})

console.log(decoded.functionName, decoded.args)

復元できない場合は、次の可能性があります。

  • ABIが呼び出し先と一致していない
  • proxyの実装側ABIを使っていない
  • 通常の関数ではなくfallbackへ送られた
  • 先頭4バイトだけでは候補を一つに決められない

関数名の候補だけで断定せず、検証済みソースコードやproxyの実装アドレスと照合してください。

ABIと入力データの構造はABI・function selector・event logsを手で読む方法で詳しく扱います。

3. 同じ条件でシミュレーションする

シミュレーションは、実際に取引を送信せずに行う試運転です。送信元、送信先、送るETH量、関数、引数を元の取引と揃えてsimulateContractを実行します。

const result = await publicClient.simulateContract({
  address: transaction.to,
  abi,
  functionName: decoded.functionName,
  args: decoded.args,
  account: transaction.from,
  value: transaction.value,
})

この方法なら、秘密鍵を使わずに、権限不足、残高不足、利用許可額不足、期限切れなどを調べられます。

ただし、元の取引が実行された時点と現在では、チェーンの状態が違う場合があります。残高、利用許可額、価格、停止状態、期限などが変わっていれば、現在のシミュレーション結果も変わります。

過去の状態を取得できる環境では、元の取引が入ったブロック付近でも確認します。

custom error、Error(string)Panic(uint256)の読み分けはexecution revertedとcustom errorをviemでデコードする方法へ進んでください。ガス見積もりの段階で止まる場合はestimateGasが失敗する理由が対応します。

4. 実行結果がない場合は送信順番号を確認する

送信順番号(nonce)は、同じアカウントから送る取引の順番を示す整理番号です。番号が同じ取引は両方とも確定できないため、手数料を上げた別取引へ置き換わることがあります。

const [latestNonce, pendingNonce] = await Promise.all([
  publicClient.getTransactionCount({
    address: transaction.from,
    blockTag: 'latest',
  }),
  publicClient.getTransactionCount({
    address: transaction.from,
    blockTag: 'pending',
  }),
])
  • latestは、確定済みの取引だけを基準にした次の番号です。
  • pendingは、そのノードが把握している保留中の取引も含めた番号です。
  • 送信した番号がlatestより小さいのに元のハッシュへ実行結果がない場合は、同じ番号を使った別ハッシュも確認します。

ウォレットとExplorerの表示が違うときは、チェーンID、送信元、送信順番号、トランザクションハッシュを同じ条件で照合します。

送信順番号の置換とnonce too lownonce too low・replacement underpricedの原因と直し方で整理しています。送金先サービスへ反映されない問題は、チェーン上の状態と受取側の処理を分けてMetaMaskの送金が反映されないときの確認も参照してください。

調査結果を残すテンプレート

チェーンID(chainId):
トランザクションハッシュ:
送信元 / 送信先:
送信順番号(nonce):
ブロック番号:
実行結果のstatus:
関数名 / 引数:
シミュレーションしたブロック:
エラー名 / 引数:
同じnonceを使った別ハッシュ:

この形で記録すると、「チェーン上で失敗した」「まだ未確定」「別取引へ置き換わった」「受取側だけ未反映」を混同しにくくなります。

最初からすべての16進数を読めなくても問題ありません。まずは実行結果の有無、status、呼び出した関数、シミュレーションのエラー、送信順番号の5点を揃えるだけでも、原因をかなり狭められます。

確認した一次情報