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

送金やコントラクト操作が失敗したとき、最初から難しいエラー名だけを探すと、かえって原因を見失いやすくなります。
まずは、取引がブロックに入ったか、何を実行しようとしたか、同じ条件で試すと何が起きるかを順番に確認します。
イメージは荷物の追跡です。伝票番号から「受付済みか」「届け先と内容は正しいか」「途中で止まった理由は何か」「同じ受付番号の別便がないか」を確認するのと似ています。
この記事では、実行結果 → 入力データ → シミュレーション → 送信順番号の順で調べます。秘密鍵をコードへ貼り付ける必要はありません。読み取り専用のPublic Clientとトランザクションハッシュがあれば進められます。
先に用語を普通の言葉で確認する
| 用語 | 普通の言葉でいうと | 主に分かること |
|---|---|---|
取引情報(transaction) |
何を実行しようとしたかを書いた注文票 | 送信元、送信先、入力内容、送信順番号、手数料条件 |
| 実行結果(transaction receipt) | ブロックチェーンが返す受領票 | 確定したブロック、成功・失敗、使用したガス、発生したログ |
| 入力データ(calldata) | コントラクトへ渡した指示書 | 呼び出した関数と引数 |
| シミュレーション | 実際には送信せず、同じ条件で行う試運転 | 権限不足、残高不足、期限切れなどのエラー |
| 送信順番号(nonce) | 同じアカウントから送る取引の整理番号 | 保留中、置換済み、別ハッシュの可能性 |
英語の名前は、コードやExplorerの表示と照合するために残しています。最初は「注文票・受領票・指示書・試運転・整理番号」と考えれば十分です。
まず確認する4項目
| 項目 | 確認できること | 次に判断すること |
|---|---|---|
| 取引情報 | from、to、input、nonce、手数料条件 |
呼び出した関数と送信条件が正しいか |
| 実行結果 | ブロック、status、gasUsed、ログ |
確定済みか、実行途中で取り消されたか |
| シミュレーション | 現在または指定ブロックでの試運転結果 | 権限、残高、利用許可額、期限などに問題がないか |
| 送信順番号 | latestとpendingの取引数 |
未確定、置換、別ハッシュの可能性があるか |
実行結果が取得できない場合、すぐに「失敗した」とは断定できません。まだ未確定、参照しているチェーンが違う、ノードがそのハッシュを知らない、同じ送信順番号の別取引へ置き換わった、という可能性も残ります。
具体例:ガスの問題に見えても、原因が利用許可額の場合がある
たとえば、ウォレットでトークン交換をしようとして「ガスを見積もれません」と表示されたとします。
この場合、ガス代を上げれば直るとは限りません。同じ送信元・送信先・数量でシミュレーションすると、「このコントラクトが使えるトークン量の許可が足りない」と分かることがあります。
そのとき確認すべきなのはガス代ではなく、トークンの利用許可額(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.statusがrevertedなら、取引はブロックに取り込まれましたが、コントラクトの処理は途中で取り消されています。
ただし、実行結果だけでは具体的なエラー名まで分からない場合があります。次に、取引情報の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 lowはnonce too low・replacement underpricedの原因と直し方で整理しています。送金先サービスへ反映されない問題は、チェーン上の状態と受取側の処理を分けてMetaMaskの送金が反映されないときの確認も参照してください。
調査結果を残すテンプレート
チェーンID(chainId):
トランザクションハッシュ:
送信元 / 送信先:
送信順番号(nonce):
ブロック番号:
実行結果のstatus:
関数名 / 引数:
シミュレーションしたブロック:
エラー名 / 引数:
同じnonceを使った別ハッシュ:
この形で記録すると、「チェーン上で失敗した」「まだ未確定」「別取引へ置き換わった」「受取側だけ未反映」を混同しにくくなります。
最初からすべての16進数を読めなくても問題ありません。まずは実行結果の有無、status、呼び出した関数、シミュレーションのエラー、送信順番号の5点を揃えるだけでも、原因をかなり狭められます。
確認した一次情報
- viem getTransactionReceipt確認日: 2026/8/28
- viem decodeFunctionData確認日: 2026/8/28
- viem simulateContract確認日: 2026/8/28
- ethereum.org トランザクション確認日: 2026/8/28



