レッスン4 — cast — EVM の curl + jq
問い
cast は Foundry の chain CLI、EVM の curl + jq。call / send / storage / abi-decode / 4byte の 5 動詞で chain と対話する。 mainnet 状態を読む / tx 送る / storage slot 直接読む / 計算済み calldata を decode / 4-byte signature を逆引き — どれも Rust から shell に降りる瞬間。
原理(最小モデル)
cast call <addr> "fn(args)" <args>= read. State-changing でない関数を呼ぶ、結果を返す、gas 不要、tx 送信なし、view/pure関数の確認に。cast send <addr> "fn(args)" <args> --private-key <key>= write. State 変更 + gas + nonce + 署名、--rpc-urlで network 指定、--fromでなりすまし(cheatcode 等価)。cast storage <addr> <slot>= 直接 storage 読み. 公開 getter なしで storage slot を読む、layout 知識必須、mappingの slot 計算はcast indexで。cast abi-decode "fn(args)" <calldata>= calldata 逆変換. Tx を receipt から拾い、calldata を function signature で decode、parameter を type で表示。cast 4byte <selector>= 4-byte signature 逆引き.0xa9059cbb→transfer(address,uint256)、unknown selector の意味解析。ethereum.reth.rs/rpcを mainnet RPC 例として使う. rethlab 公式 RPC、無料、rate limit 緩い、--rpc-urlで指定。- Tab 補完 (
cast completions). zsh / bash / fish 用、daily use の friction を消す。
具体例 + ステップで組み立てる
レッスン 4 — cast — EVM の curl + jq
ゴール
このレッスンで掴む概念:
castはalloy::Providerを terminal コマンドとして露出させたものだ。 すべてのcastsubcommand がalloy_provider::Providerの method に map する — rethlab のalloy-providerレッスンで Rust コードから呼んだのと同じ trait だ。cast call↔provider.call(...)、cast block↔provider.get_block(...)、cast send↔provider.send_transaction(...)。CLI は同じ Rust code path への thin な shell wrapper にすぎない。Rust でprovider.call().await?をすでに書いたなら、新しいメンタルモデル(Mental model)は要らない。タイピングの筋肉記憶(Muscle memory)を更新するだけだ。castは alloy bindings + shell prompt。背後の RPC リクエストは同一だ。castは 何を尋ねるか と どのチェーンに尋ねるか を分離する。 どのコマンドも--rpc-url <URL>フラグを option として取り、ノードを指す。フラグなしならcastは環境変数$ETH_RPC_URLを使う。同じcast callを mainnet、sepolia、ローカル anvil instance のいずれに対しても、フラグ 1 つ変えるだけで実行できる。コマンド自体は同一だ。ターゲットチェーンは束縛(Binding)ではなく引数(Parameter)。 これが L1 エンジニアにとっての payoff だ。同じクエリが prod、staging、forked simulation を読む。置換 1 つで。cast call(読み取り専用)とcast send(state-changing)の 2 つが 90% の時間使う動詞だ。cast callは view / pure 関数を走らせるか、broadcast せずに transaction を simulate する。関数の return 値を raw bytes として(あるいは function signature を渡せば decoded で)返す。cast sendは実際に transaction を broadcast する。--private-keyを要求し、transaction hash を表示する。残りのコマンド (cast block、cast tx、cast logs、cast abi-encode、cast 4byte) は内省(Introspection)とデータ操作のツールだ。便利だが、load-bearing なペアはcallとsend。Production debug の大半は forked anvil に対するcast call。Production deploy の大半は testnet に続いて mainnet へのcast send。cast abi-encode/cast abi-decodeがデータレイヤーのループを閉じる。 Calldata を手で構築する必要があるとき (cast send --create用、multisig 提出用、Solidity script への埋め込み用)、cast abi-encode "transfer(address,uint256)" 0x... 1000がオンチェーンで送られる exact bytes を生成する。cast abi-decodeは逆。calldata と function signature を与えると、type 付き引数を取り出す。これはforgeの test runner が内部で使うのと 同じ ABI 機構(ABI machinery)が CLI で露出されたものだ。Calldata を手で debug したことがあるなら、cast abi-decodeは何年も前に shell alias に入れておくべきだったツールだ。
確認:
cast --version
cast call --rpc-url https://ethereum.reth.rs/rpc \
0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 "totalSupply()(uint256)"
…で Reth プロジェクトの public RPC 経由で real mainnet に対して走り、USDC の現在の total supply を返す(~12 桁の数字、6 桁 decimal 精度)。本レッスン完走後は次の 3 つを手にする。どの cast subcommand がどの alloy method に map するか、cast call と cast send のどちらに手を伸ばすべきか、calldata を必要に応じて手で組み立てる方法。
具体的な変更:
- ソースファイル編集なし。 レッスン 4 はすべて CLI invocation。Mainnet に対して ~8 種類の
castコマンドを走らせ、(option として)ローカル anvil に対しても走らせる。 .env(option)—ETH_RPC_URL=https://ethereum.reth.rs/rpcを設定して、毎コマンドに--rpc-urlを渡すのを避けたいかもしれない。レッスン5で anvil を扱う際、terminal session ごとにETH_RPC_URLを mainnet と forked anvil の間で切り替えるデモをする。
合計で Solidity ゼロ行。レッスン 4 は shell time。Pedagogical move は alloy-method ↔ cast-subcommand マッピングを内面化すること。次に Rust の Provider に手を伸ばすとき、まず cast に手を伸ばすようになる。
おさらい
レッスン3の後はこうなっている。
forge invariantが Handler に対してランダム call 系列を走らせ、毎 call 後にinvariant_*を check する。- Handler が target を wrap し、入力を bound し、ghost variable (shadow specification) を track する。
- Sequence shrinking が 30+ call の失敗を 2-call minimal counterexample まで reduce する。
レッスン 3 は test/ ファイル内に住んだ。レッスン 4 は test ディレクトリを完全に離れる。cast は、deploy 済み contract、transaction hash、decode したい calldata があり、それを見るためだけに Solidity script を書きたくないときに手を伸ばすツールだ。L1 エンジニアの debug ループは forge test だけではなく、forge test の後の cast call だ。
計画
invocation のカテゴリが 5 つ。
cast call— terminal から mainnet state を読む。USDC のtotalSupply()と既知 address のbalanceOf(address)をクエリする。cast block/cast tx— チェーン内省。最近の mainnet block を lookup する。Hash で特定 transaction を検査する。cast abi-encode/cast abi-decode— calldata 操作。ERC-20 transfer call の bytes を構築する。Bytes を type 付き引数へ decode して戻す。cast 4byte/cast 4byte-decode— function-selector lookup。Calldata の先頭 4 bytes を与えると、public な 4byte directory 経由で人間可読 function 名を見つける。- ローカル anvil に対する
cast send(preview) — state-changing transaction。重要なものは deploy しない。この演習はcast sendがチェーンとどう interact するかをデモする (レッスン 5 が anvil 自体を深掘りする)。
(答え: cast call --rpc-url <URL> <contract-address> "<function-signature>" [args...]。各ピースが直接 map する。--rpc-url フラグは alloy の RootProvider の underlying transport URL。Contract address は transaction の to フィールド。Function signature は cast が内部で 4-byte selector に hash する人間可読 ABI 短縮形 (alloy が同じ Function::parse 機構を使う)。任意の args は positional。Return は raw hex (function signature の後に "(...returntypes)" で return type を指定しない限り)。指定すれば cast が decode する。同じ code path、2 つの surface。プログラム用は Rust、shell 用は cast。)
cast が alloy::Provider にどう map するか
┌─────────────────────────┬────────────────────────────────────────────────┐
│ cast subcommand │ alloy::Provider method │
├─────────────────────────┼────────────────────────────────────────────────┤
│ cast call │ provider.call(tx) │
│ cast send │ provider.send_transaction(tx) │
│ cast block │ provider.get_block(block_id) │
│ cast tx <hash> │ provider.get_transaction_by_hash(hash) │
│ cast receipt <hash> │ provider.get_transaction_receipt(hash) │
│ cast logs │ provider.get_logs(filter) │
│ cast balance <addr> │ provider.get_balance(addr) │
│ cast nonce <addr> │ provider.get_transaction_count(addr) │
│ cast chain-id │ provider.get_chain_id() │
│ cast gas-price │ provider.get_gas_price() │
│ cast block-number │ provider.get_block_number() │
├─────────────────────────┼────────────────────────────────────────────────┤
│ cast abi-encode │ alloy_dyn_abi::DynSolType::abi_encode │
│ cast abi-decode │ alloy_dyn_abi::DynSolType::abi_decode │
│ cast 4byte │ (public 4byte directory lookup, not RPC) │
│ cast keccak <data> │ alloy_primitives::keccak256(data) │
└─────────────────────────┴────────────────────────────────────────────────┘
構造的に押さえるべきこと。cast ≈ alloy::Provider(RPC operation 用)、cast ≈ alloy_dyn_abi(ABI operation 用)。rethlab Fundamentals コースでこれら 2 つの crate を grok 済みなら、すべての cast subcommand が何をするかをすでに知っている。まだ引数構文を知らないだけだ。
手を動かす walk-through
Step 1: 方角合わせ — cast --version と cast help
cast --version
forge version (両者は同じ foundry-rs/foundry バイナリ配布から船出する) と一致する cast Version: 1.7.x のような表示が見えるはず。
cast help
出力は subcommand のフラットなリスト。押さえる点が 3 つ。
- Subcommand は何に触るかでカテゴリ分けされている。
cast call、cast send、cast call --traceはチェーン state と interact する。cast abi-*、cast keccak、cast 4byteはローカルデータ操作ツール (RPC なし)。cast walletは鍵を管理する。頭の中で bucket 分けする。RPC コマンドは--rpc-urlを要求する。ローカルコマンドはしない。 - 多くの subcommand に alias がある。
cast callはcast cでもある。cast sendはcast sでもある。Interactive 利用では長形を打つ必要はない。Full name はスクリプトに現れる。 cast help <subcommand>が任意の subcommand の詳細フラグを与える。cast help callはcast callが受け付けるすべてのフラグを表示する (block tag、value、gas override 等)。迷ったら docs を読むよりcast help <subcommand>のほうが速い。
Step 2: cast call で mainnet state を読む
レッスン全体で使う public RPC endpoint: https://ethereum.reth.rs/rpc(Reth プロジェクトの public ノード — rethlab alloy-provider レッスンと同じもの)。
cast call --rpc-url https://ethereum.reth.rs/rpc \
0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 \
"totalSupply()(uint256)"
これは mainnet 上の USDC contract address に対し、totalSupply() を呼び、cast に return を uint256 として decode してくれと頼んでいる。
期待される出力:
35234876543210000000 # 実際の数字は変わる。~350 億 USDC、6 桁 decimal 精度
押さえる点が 6 つ。
-
Function signature は 4-byte selector ではなく人間可読 Solidity 形式だ。 cast が内部で
"totalSupply()(uint256)"を alloy と同じ parser で parse し、signature を keccak256 で hash し、先頭 4 bytes を取り、それを背後のeth_callにおける function selector として使う。書くのは Solidity-ergonomic な構文、encode は cast がやる。 -
Function 名の後の
(uint256)が return type の annotation だ。 これがないと cast は raw hex bytes (0x0000...) を表示する。あれば cast は return をuint256として decode し、decimal を表示する。複数 return の関数も同じパターンに従う —"slot0()(uint160,int24,uint16,uint16,uint16,uint8,bool)"は Uniswap V3 pool の slot0 signature で、cast は各 tuple 要素を 1 行ずつ表示する。[!TIP]
cast abi-decodeによるデコードの堅牢なフォールバック 複雑な構造体や動的配列、ネストされたタプルを返す関数の場合、cast callのインラインデコードアノテーション(例:"myFunction()(uint256[],(string,address))")が CLI パーサーの文脈制限により解析に失敗することがある。その際のプロフェッショナルなフォールバック手段は、デコードを指定せずに関数を呼び出して生の hex バイトを出力させ、それをパイプ経由でcast abi-decodeに渡す方法だ:cast call <contract-address> "myFunction()" | cast abi-decode "myFunction()(uint256[],(string,address))"このアプローチは、コマンドライン引数のパース時よりも permissive(寛容)なコンテキストで ABI デコーダーが実行されるため、複雑なネスト構造やカスタムデータ型であっても確実にデコードできる。実運用において、通常の型シグネチャでデコードできない場合は、即座にこのパイプライン方式に切り替えるのが定石だ。
-
Private key 不要。
cast callは read-only。Broadcast せずノードの state view に対して実行する。これが production debug のワークホースだ。mainnet に対して任意の view 関数を 1 wei も使わずに simulate できる。 -
--rpc-urlは shell 環境のETH_RPC_URLで代替できる。export ETH_RPC_URL=https://ethereum.reth.rs/rpcを 1 回設定し、以降のコマンドからフラグを落とす。レッスン5で anvil を扱う際、terminal session ごとにETH_RPC_URLを mainnet と forked anvil の間で切り替えるデモをする。 -
出力 decimal は人間フォーマットされていない、raw integer だ。 USDC は decimal 6 桁。
35,234,876,543,210,000,000raw は35,234,876,543,210.000000 USDCを意味する。cast は decimal scaling を適用しない。それは自分の仕事だ。あるいはcast --to-unit <value> etherで変換する (名前にもかかわらず、unit conversion は汎用)。 -
cast callで使われる mainnet block は default ではチェーンの現在の head だ。 特定 block に対して call するには--block <number-or-hash-or-tag>を追加する。過去 state の replay に便利。--block 12345678がその block 時点でtotalSupply()が返したであろう値を simulate する。
もう 1 つ試す — 特定 address の USDC balance をクエリ:
cast call --rpc-url https://ethereum.reth.rs/rpc \
0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 \
"balanceOf(address)(uint256)" \
0x47ac0Fb4F2D84898e4D9E7b4DaB3C24507a6D503 # 任意の mainnet address
これが Rust の provider.call(USDC.balanceOf(addr).await?) の CLI 等価物だ。背後の RPC は同一、違うのはキーボード ergonomics だけ。
Step 3: Block と transaction を検査する
現在の block を見る:
cast block latest --rpc-url https://ethereum.reth.rs/rpc
YAML-style な dump が見える: number、hash、parentHash、timestamp、gasLimit、gasUsed、baseFeePerGas、miner、full transactions list、withdrawals 等。Alloy の Block type が持つのと同じデータ構造、terminal 読み用にフォーマットされている。
cast block 19000000 --rpc-url https://ethereum.reth.rs/rpc
Number で lookup して過去 block を replay する。「contract X が block N でどんな state を持っていたか」を debug するときに有用。
特定 transaction を検査:
cast tx 0xa84a9... --rpc-url https://ethereum.reth.rs/rpc # 任意の real な mainnet tx hash
Transaction の from、to、value、input (calldata)、gas、gasPrice、nonce、signature components を返す。input フィールドこそが次に cast abi-decode を使いたくなる場所だ。
Receipt — tx が mine されたとき実際に何が起きたか:
cast receipt 0xa84a9... --rpc-url https://ethereum.reth.rs/rpc
status (1 = success、0 = reverted)、gasUsed、emit された logs (events)、blockNumber 等を含む。「deploy は成功したか」を debug するとき、cast send の後の最初のコマンドは cast receipt。
Step 4: cast abi-encode / cast abi-decode で calldata を操作する
ERC-20 transfer(address,uint256) call の calldata を構築:
cast abi-encode "transfer(address,uint256)" \
0x47ac0Fb4F2D84898e4D9E7b4DaB3C24507a6D503 \
1000000 # 1 USDC、6-decimal 精度
出力 (call の実際の calldata bytes):
0xa9059cbb00000000000000000000000047ac0fb4f2d84898e4d9e7b4dab3c24507a6d50300000000000000000000000000000000000000000000000000000000000f4240
3 セクションを読む。
0xa9059cbb—transfer(address,uint256)の 4-byte selector (signature の keccak256 の先頭 4 bytes)0000...0047ac...— 第 1 引数 (address)、32 bytes に padded0000...0f4240— 第 2 引数 (uint256 1,000,000 = 0xf4240)、32 bytes に padded
これが生 transaction の data フィールドに埋め込む exact な bytes。Multisig 提案、governance calldata、external call を構築する必要のある Solidity script のために bytes を build する方法はこれだ。
逆操作。calldata を与えて、type 付き引数を recover する。
cast abi-decode "transfer(address,uint256)" \
0xa9059cbb00000000000000000000000047ac0fb4f2d84898e4d9e7b4dab3c24507a6d50300000000000000000000000000000000000000000000000000000000000f4240
出力:
0x47ac0Fb4F2D84898e4D9E7b4DaB3C24507a6D503
1000000
abi-decode は、謎 calldata blob と function signature を持っているときに手を伸ばすツールだ。 Production debug の大半は「tx からの calldata がある、これが実際に何をするのか」。それこそが cast abi-decode が解く問題だ。
Step 5: cast 4byte で function-selector lookup
時に calldata はあるが function signature を 知らない。先頭 4 bytes が selector だ。cast が public directory (4byte.directory) をクエリして人間可読名を recover する。
cast 4byte 0xa9059cbb
出力:
transfer(address,uint256)
複数候補 signature が同じ 4 bytes に hash すれば、cast はすべてを列挙する。selector の衝突は存在する (production 関数では稀、obscure な関数では一般的)。cast 4byte は unknown calldata に最初に走らせるコマンドで、その後に cast abi-decode を出す。
Unknown-calldata の full な debug ループ。
# Step 5a — 謎 calldata を与えて、function 名を見つける:
cast 4byte 0xa9059cbb
# → transfer(address,uint256)
# Step 5b — recover した signature を使って calldata を decode する:
cast abi-decode "transfer(address,uint256)" 0xa9059cbb...
# → 0x47ac... 1000000
Step 6: ローカル anvil に対する cast send の preview
cast send は cast call の state-changing なツインだ。Private key を要求し (あるいは wallet 管理コマンドの 1 つ)、transaction を broadcast し、結果の transaction hash を表示する。重要なものは実際には送らない (レッスン 5 が anvil と full なローカル開発ループを扱う)。だが構文は見ておく価値がある。
# 別 terminal でローカル anvil を起動 (レッスン 5 が深掘りする):
# anvil
# anvil は 10 個の funded test account とその private key を表示する。
# ローカル anvil に対して transaction を送る:
cast send --rpc-url http://localhost:8545 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 \
"transfer(address,uint256)" \
0x47ac0Fb4F2D84898e4D9E7b4DaB3C24507a6D503 \
1000000
押さえる点が 3 つ (走らせなくても見える)。
cast callに対して--private-keyだけが新しいフラグ。 他はすべて同一だ。cast が key で transaction を sign し、RPC で broadcast し、hash を表示する。- anvil の default private key
0xac0974...は事前 fund 済みだ。 anvil が起動時に 10 個の deterministic account を seed する。毎回同じ private key、ローカル開発のみ safe。anvil の default key を任意の real network に対して決して使うな。 - 出力は transaction hash。 それを
cast receipt $txに pipe する (hash を back-tick) と status、gas used、emit された logs が見える。2-step pattern はcast send→cast receipt。Alloy ではprovider.send_transaction(...).await?.get_receipt().await?をやるのと同じだ。
レッスン 5 (次のレッスン) が anvil に mainnet forking で戻る。そこで cast send が真に有用になる。real な mainnet transaction を forked state に対して、real ETH を使わずに simulate できる。
よくある失敗パターン
error sending request for url—--rpc-urlが到達不可能。URL、自分のネットワーク、または別の public RPC (Cloudflare、Ankr 等) にフォールバックを check する。Error: Wrong function selector ...— 渡した function signature が contract と一致していない。Contract の実際の calldata にcast 4byteを使って正しい signature を recover するか、block explorer から contract の ABI を読む。Error: missing field "input"— 指している chain に存在しない transaction hash をクエリしている (例: testnet RPC に対して mainnet hash を使った)。Chain を検証する。cast sendが tx hash を返すが receipt はstatus: 0を表示する — tx は mine されたが revert した。同じ calldata でcast callを走らせて revert reason を見る (cast call は broadcast せずに simulate し、revert message を表示する)。Error: insufficient funds—--private-keyが target chain で ETH を持たない account を制御している。ローカル anvil なら anvil の seed 済み account を使う。Testnet なら faucet からリクエストする。
設計の振り返り
cast の設計に焼き込んだ load-bearing な決定が 3 つ。
-
castは背後で alloy を再利用する。別の JSON-RPC クライアントなし。 Foundry のcastバイナリは Reth が使うのと同じalloycrate に link する。すべてのcastinvocation が、自分の Rust プログラムが歩むのと同じ code path を歩む。含意はこうだ。Reth が新しい RPC method (例えば新しい tracing endpoint) をサポートすれば、alloy version が bump された時点でcastはそれを無料で得る。実装は 1 つ、surface は 2 つ。CLI は library と別に保守されていない。 -
Function signature は 4-byte selector ではなく人間可読だ。 cast は
transfer(address,uint256)に対して0xa9059cbbを渡せと要求することもできた (Geth のeth_callは raw bytes を取る)。cast はどちらも受け付けるが、人間可読形式が documented default だ。規律はこうだ。キーボード ergonomics が自分の書いた Solidity ソースと一致する。cast に打ち込むものが、Solidity に打ち込んだものと一致する。メンタルな翻訳ステップなし。 -
--rpc-urlは session 単位ではなく command 単位だ。 環境にETH_RPC_URLを 1 回設定できるが、個々のcastinvocation が inline でそれを上書きできる。これは deliberate に stateless。npmがnpm config set registryで持つような「現在の chain」モードはない。理由はこうだ。chain mistake は破滅的だ (testnet を意図して mainnet に送る)。cast の設計はすべての state-changing コマンドで chain を可視に保つことを強制する。ステートレス性(Statelessness)は usability の見落としではなく safety feature だ。
答え合わせ
レッスン4の後、shell history はこんな具合に含む。
# Mainnet を読む
cast call --rpc-url https://ethereum.reth.rs/rpc \
0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 \
"totalSupply()(uint256)"
# Block を検査
cast block latest --rpc-url https://ethereum.reth.rs/rpc
# Calldata を build
cast abi-encode "transfer(address,uint256)" 0x... 1000000
# 謎 calldata を decode
cast 4byte 0xa9059cbb
cast abi-decode "transfer(address,uint256)" 0xa9059cbb...
# Option: ローカル anvil に対して送る
cast send --rpc-url http://localhost:8545 \
--private-key 0xac09... 0xA0b8... "transfer(address,uint256)" 0x47ac... 1000000
レッスン4の後はこれができる。
- 任意の chain 上の任意の contract の任意の view 関数を terminal から読む
- Block explorer を開かずに block と transaction を検査する
- Multisig 提案、governance、scripts 用に calldata を build / decode する
- 4byte 経由で unknown function selector を lookup する
- ローカル anvil に対して transaction を送る (レッスン5で anvil を full に扱う)
よくある質問
Q1: Etherscan + ブラウザで同じことができるのに、なぜ cast を使うのか?
理由は 3 つ。
- 合成可能性(Composability) —
castの出力はプレインテキストなので、Unix のパイプライン思想そのままにjq、awk、xargs、grepへ直接流し込める。自動化スクリプトへの組み込みが容易だ。Etherscan の出力はブラウザの中だ。 - 再現性(Reproducibility) — cast コマンドは単一の bash one-liner としてチーム内で共有できる。Etherscan ワークフローはランブックに paste できないクリックの連続だ。
- スピード — ローカル Reth ノードに対する
cast callは milliseconds で返る。Etherscan は rate limit 付きで重い Web ブラウザのロードを待たされる。1 時間に数十の view クエリを投げる L1 エンジニアには、思考の同期を保つために cast の 10×+ の速度差が死活問題だ。
Etherscan は一度きりの探索用、cast はそれ以外のすべてに。
Q2: cast はすべての JSON-RPC method を サポートする? それとも subset か?
Subset だ。cast は ~30 の named subcommand を露出し、common method をカバーする。直接露出していないものには cast rpc <method> [params...] を使う。これが raw escape hatch だ。method 名と parameter を JSON-RPC リクエストとして送り、JSON response を表示する。Typed wrapper なしで method を欲しいとき、alloy で provider.client().request::<...>() を使うのと同じパターンだ。
Q3: cast は cast send のために signed transaction をどう扱うのか?
すべて client side で sign する。--private-key を渡すと、cast が client side で transaction を構築し、key (alloy_signer_local を使う) で sign し、signed な transaction を eth_sendRawTransaction 経由で submit する。Private key は machine を離れない。Hardware wallet ワークフローには --ledger か --trezor を代わりに使う。cast が同じ alloy_signer_* trait を歩む。Signing はローカル、RPC が見るのは broadcast bytes だけ。
Q4: cast の代わりに alloy::Provider で Rust プログラムを書くべきはいつか?
ワークフローが 3 コマンドより長く、bash を超える branching / loop / error handling を必要とするときだ。Rough rule。場当たり的クエリは cast、繰り返しのワークフローや CI で走るものは Rust + alloy。一度きりの deploy には cast send で十分。検証、role 設定、ownership 移譲、parameter 設定を必要とする deployment script なら Rust バイナリ (あるいは Foundry の script/ ファイル、Solidity) を書く。cast は 1 行 bash script までスケールする。alloy は deployment バイナリまでスケールする。
Q5: cast call で異なる msg.sender を持つ transaction を simulate できる?
Yes。--from <address> フラグが transaction の見かけ上の sender を上書きする。Access-controlled 関数の test に有用だ。--from <owner-address> で owner が見るものを simulate できる。ただし注意。これは simulated な call だ。On-chain で address を impersonate するわけではない。Test 用に impersonation が必要なら、それは Solidity の vm.prank か、RPC 経由の anvil_impersonateAccount だ (レッスン 5 が両方を扱う)。Simulation には cast call --from、Forked-chain testing には anvil_impersonateAccount。
Q6: cast は non-Ethereum な EVM チェーンで動くか?
Yes。標準 JSON-RPC interface を喋るものなら何でも動く。Optimism、Arbitrum、Base、Polygon、BNB Chain、自分のカスタム L2 — すべて同一に動く。--rpc-url を正しい endpoint に向けるだけだ。例外は非標準 RPC method を持つ chain (Tron、NEAR、non-EVM Solana 等)。明らかに当てはまらない。任意の EVM 互換 chain には cast がある。Non-EVM chain には chain 自身の tooling が要る。
次のレッスン(レッスン5) — anvil + cheatcodes — real な mainnet state でのローカル開発
レッスン 5 が最後の piece を wire する。anvil --fork-url 経由で real な mainnet state に対するローカル開発だ。学ぶこと。
anvil --fork-url <mainnet-rpc>— 起動時に mainnet の現 state を mirror するローカル chain を立ち上げる- anvil が seed する 10 個の funded test account と、なぜ deterministic か
- anvil 固有の RPC method:
anvil_impersonateAccount、anvil_setBalance、anvil_mine、anvil_setStorageAt - Foundry の
vm.*cheatcode (レッスン 1〜3 test の) が anvil の RPC 等価物にどう map するか。同じ機構、違う surface - Full なローカル開発ループ。
anvil --fork-url→ forked mainnet に対するcast send→ 検証用のcast call→ real ETH ゼロ消費
レッスン 5 完走後、laptop を離れずに real な mainnet state に対して開発できる。レッスン 5 がコースの test-discipline + CLI 部分を閉じる。レッスン 6 が capstone で、そこで openhl-liquidation Stage 10b の InsuranceFund を Solidity に port し、同じ 4 つの保存則を forge invariant で証明する。
合格基準
castの 5 動詞(call / send / storage / abi-decode / 4byte)の役割を即答できる。cast callvscast sendの違い(state 変更 + gas 必要かどうか)を即答できる。cast storageで mapping の slot 計算にcast indexを使う理由を即答できる。cast abi-decodeの用途(receipt の calldata を function signature で decode)を即答できる。cast 4byteで unknown selector を逆引きできる仕組みを即答できる。ethereum.reth.rs/rpcを mainnet RPC として指定する手順を即答できる。
まとめ(3行)
cast= EVM の curl + jq、5 動詞(call / send / storage / abi-decode / 4byte)で chain と対話、cast callは read(gas 不要)/cast sendは write(gas + nonce + 署名)。cast storageで公開 getter なしで storage 直接読み、mappingslot はcast indexで計算、cast abi-decodeで calldata 逆変換、cast 4byteで selector → signature 逆引き。--rpc-url ethereum.reth.rs/rpcが mainnet RPC 例、tab 補完で friction 削減、次レッスンでanvil --fork-url+ cheatcodes による state-aware testing へ。