開発者へ / JSON-RPC

Ethereum との相違点

Pickle は Ethereum の形をしていますが、Ethereum 等価ではありません。標準的なクライアントが予期しない応答を受け取る箇所の完全な一覧を、苦労して発見した場合に奪う時間の順に並べます。

revert の理由はデコードできない

error.data は決して埋められません

A revert arrives as message text - "execution reverted: 0x<returndata>" - and error.data is absent.

Libraries decode custom errors and revert strings out of error.data, so on this chain they cannot. ethers and viem will both surface the raw message instead of a decoded error. If you need the reason, parse the hex out of the message yourself and decode it against your own ABI.

理由が必要なら、戻りデータはメッセージの本文の中にあり、自分でデコードできます。

javascript
try {
  await client.readContract({ /* … */ });
} catch (err) {
  // "execution reverted: 0x08c379a0…"
  const hex = String(err.message).match(/0x[0-9a-fA-F]+/)?.[0];
  // Decode that hex against your own ABI's errors, or against Error(string).
}

過去の状態は存在しない

eth_getBalance、eth_getTransactionCount、eth_getCode、eth_getStorageAt はアドレスを解析したうえで、最後に 公開されたスナップショットを読みます。ブロックパラメータは受け付けられ、黙って無視されます。ブロック 100 時点の残高を求めても エラーにはならず、現在の残高が返ります。

eth_call と eth_estimateGas も同じで、さらに送られた gas、gasPrice、nonce を無視します。シミュレーションは 常にブロックのガス上限 30,000,000 で、最新の状態に対して走ります。

これは静かに失敗します。だから二番目にあります

アーカイブ的なやり方はすべて、つまり過去の残高を読む、過去のブロックで状態を再生する、 スナップショットを計算するといったことは、ここではエラーではなく自信に満ちた、もっともらしい、 誤った答えを返します。過去に関わるものはエクスプローラーから読んでください。

ブロックのタグは一点に潰れる

latest、pending、safe、finalized はすべて同じもの、すなわち保持されている先端に解決されます。そして earliest は ブロック 0 ではなく最も古い保持されているブロックに解決されるので、ウィンドウが 滑るにつれて前へ進みます。

EIP-1898 のオブジェクトは受け付けられます。{ blockNumber } または { blockHash } で、ハッシュはインデックスを通じて解決され、もはや保持されて いなければ null を返します。それ以外はすべて -32602 "block tag" です。

結果を信じてよい時点を決めるために safe と finalized を区別するクライアントは、この チェーンから何の信号も得られません。ここでのファイナリティは L1 での決済であり、これらの タグからはまったく見えません。

ルートとブルームはゼロ

ブロックでもレシートでも、stateRoot、transactionsRoot、receiptsRoot はゼロで、logsBloom はゼロの文字が 512 個です。

帰結は二つ。ルートを検証するクライアントは、ここでは何も検証できません。そしてログを 取りに行く前にブルームで前もって絞り込むクライアントは、何にも一致しません。本物の インデックスを読む eth_getLogs で絞り込んでください。

ヘッダーはそれ以外では正しく Cancun 形式で、mixHash、withdrawalsRoot、blobGasUsed、excessBlobGas、parentBeaconBlockRoot を含みます。mixHash は飾りではなく荷重を 担っています。これがないと、forge script を含むすべての revm 系クライアントが、 シミュレートする前に prevrandao not set でヘッダーを拒否します。

レシートとトランザクションのフィールド

  • type は実際に送ったエンベロープが何であれ常に "0x0" であり、 トランザクションは平たい gasPrice 一つだけを報告します。maxFeePerGas、maxPriorityFeePerGas、accessList のいずれも返りません。型付きトランザクションは入口では 受け付けられます。応答の形が型を反映しないだけです。
  • cumulativeGasUsed はそのトランザクション自身の gasUsed であり、 ブロックの累計ではありません。ブロック全体で合計しても二重計上は起きませんが、何も 分かりません。
  • blockHash と blockNumber は、実行から EVM の封印までの間 null です。その窓の中でもレシートは存在するので、「レシートが null でない」は 「ブロックに含まれた」と同じ検査ではありません。
  • トランザクションは標準外の追加フィールド raw、miniBlockNumber、miniBlockHash を持ち、レシートは後ろの二つを 持ちます。便利ですが、移植性はありません。
  • fullTransactions: true を付けた eth_getBlockByNumber は混ざった配列を返すことがあります。レシートがまだインデックスされている トランザクションはオブジェクト、すでに追い出されたものは裸のハッシュ文字列です。
  • eth_getBlockReceipts は追い出されたレシートを黙って省くので、配列はその ブロックのトランザクション一覧より短くなりえます。

logIndex はトランザクションごと

logIndex はブロックではなく、自分のレシートのログの中で採番されます。同じ ブロックの二つのログがどちらも logIndex 0 を持ちえます。

イベントストアを (blockNumber, logIndex) でキーにすると、つまり インデクサーのよくある複合主キーにすると、衝突します。transactionHash を 含めてください。

pending は mempool ではない

pending でいられる公開の mempool はありません。ハッシュが eth_newPendingTransactionFilter と newPendingTransactions のサブスクリプションに届くのは、トランザクションが実行されインデックスされた後だけです。

つまり誤解を招く名前のついた、実行済みトランザクションのフィードです。着地する前に 入ってくるトランザクションを見張ることはできず、このインターフェースを通じて先回りできる ものは何もありません。

定数で答えるメソッド

二つのメソッドは動いているように見えて、測定ではない値を返します。どちらもリファレンスで 印が付いており、どちらもエラーを返すメソッドより危険です。

  • eth_feeHistory は blockCount しか読まず、newestBlock と rewardPercentiles を完全に無視し、baseFeePerGas を 0x1 の繰り返しとして、gasUsedRatio を 0.0 の繰り返しとして返します。
  • eth_createAccessList はパラメータも状態も見ずに { accessList: [], gasUsed: "0x0" } を返します。

三つ目のグループは、概念が当てはまらないため、正直に、しかし空で答えます。eth_accounts は常に空、eth_mining は false、eth_hashrate はゼロ、そして四つの uncle 系メソッドはパラメータを検証せずに ゼロか null を返します。

存在しない名前空間

rpc_modules は eth、net、web3、pickle を告知します。存在せず未実装なのは debug、trace、txpool、admin です。したがって debug_traceTransaction はありません。呼び出しトレースには、それを持っている エクスプローラーを使ってください。

署名系のメソッドは登録されていて、意図的に拒否します。eth_sendTransaction、eth_sign、eth_signTransaction、personal_sign、eth_coinbase はいずれも -32601 を返します。ノードは利用者の鍵を 一つも持ちません。ローカルで署名し、eth_sendRawTransaction を使ってください。