-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Verbosity (-vvv) improve style #8031
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
74df490
e3b2cad
b850538
e84d0a8
94a466c
8e7c31c
f801135
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.0; | ||
|
|
||
| import "hardhat/console.sol"; | ||
|
|
||
| /// @notice Demonstrates different EVM call types for trace output testing. | ||
| /// Used by scripts/demo-call-types.ts to exercise [CALL], [CREATE], | ||
| /// [STATICCALL], [DELEGATECALL], and [EVENT] trace tags. | ||
|
|
||
| /// @notice Helper contract with view and write functions | ||
| contract Logic { | ||
| event ValueSet(address indexed setter, uint256 value); | ||
|
|
||
| uint256 public value; | ||
|
|
||
| function setValue(uint256 _value) external { | ||
| console.log("Logic.setValue:", _value); | ||
| value = _value; | ||
| emit ValueSet(msg.sender, _value); | ||
| } | ||
|
|
||
| function getValue() external view returns (uint256) { | ||
| return value; | ||
| } | ||
|
|
||
| function pureAdd(uint256 a, uint256 b) external pure returns (uint256) { | ||
| return a + b; | ||
| } | ||
|
|
||
| function mustBePositive(uint256 _value) external { | ||
| require(_value > 0, "Value must be positive"); | ||
| console.log("Logic.mustBePositive:", _value); | ||
| value = _value; | ||
| emit ValueSet(msg.sender, _value); | ||
| } | ||
| } | ||
|
|
||
| /// @notice Orchestrator that calls Logic, exercising multiple call types | ||
| contract Orchestrator { | ||
| event Orchestrated(uint256 result); | ||
|
|
||
| Logic public logic; | ||
|
|
||
| constructor(Logic _logic) { | ||
| logic = _logic; | ||
| } | ||
|
|
||
| /// @notice Regular external CALL to Logic.setValue | ||
| function doCall(uint256 _value) external { | ||
| logic.setValue(_value); | ||
| } | ||
|
|
||
| /// @notice Calls Logic.mustBePositive — reverts when _value == 0 | ||
| function doCallThatReverts(uint256 _value) external { | ||
| logic.mustBePositive(_value); | ||
| } | ||
|
|
||
| /// @notice External view call to Logic → produces STATICCALL | ||
| function doStaticCall() external view returns (uint256) { | ||
| return logic.getValue(); | ||
| } | ||
|
|
||
| /// @notice Explicit delegatecall to Logic.setValue → produces DELEGATECALL trace | ||
| function doDelegateCall(uint256 _value) external returns (bool success) { | ||
| bytes memory data = abi.encodeWithSelector(Logic.setValue.selector, _value); | ||
| (success, ) = address(logic).delegatecall(data); | ||
| } | ||
|
|
||
| /// @notice Mixed: DELEGATECALL + CALL (Logic.setValue) + STATICCALL (Logic.getValue) | ||
| function doAllCallTypes(uint256 a, uint256 b) external returns (uint256) { | ||
| // DELEGATECALL to Logic.pureAdd (uses pureAdd to avoid storage collision) | ||
| bytes memory data = abi.encodeWithSelector(Logic.pureAdd.selector, a, b); | ||
| address(logic).delegatecall(data); | ||
| // CALL to Logic.setValue | ||
| logic.setValue(a + b); | ||
| // STATICCALL to Logic.getValue | ||
| uint256 readBack = logic.getValue(); | ||
| emit Orchestrated(readBack); | ||
| return readBack; | ||
| } | ||
| } | ||
|
|
||
| /// @notice Factory that creates contracts in a single transaction | ||
| contract CallTypesFactory { | ||
| event ContractsDeployed(address indexed logic, address indexed orchestrator); | ||
|
|
||
| function deploy() external returns (address, address) { | ||
| Logic logicContract = new Logic(); | ||
| Orchestrator orch = new Orchestrator(logicContract); | ||
| emit ContractsDeployed(address(logicContract), address(orch)); | ||
| return (address(logicContract), address(orch)); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,178 @@ | ||
| /** | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IGNORE: only used for manual testing - will be removed before merging. Run
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, I will keep them, it could be useful for the future |
||
| * Comprehensive demo of all trace output features. | ||
| * | ||
| * Run with dedup (normal): | ||
| * pnpm hardhat run scripts/demo-trace-output.ts -vvvv | ||
| * | ||
| * Run without dedup (all traces): | ||
| * pnpm hardhat run scripts/demo-trace-output.ts -vvvvv | ||
| * | ||
| * For ANSI color output (red headers on failure, dim on success): | ||
| * FORCE_COLOR=3 pnpm hardhat run scripts/demo-trace-output.ts -vvvv | ||
| * | ||
| * What to look for: | ||
| * - All call kinds: CALL, CREATE, STATICCALL, DELEGATECALL | ||
| * - Connection labels: "Trace from connection #0 (default)", "#1 (node)" | ||
| * - -vvvv: dedup active — single counter.write.inc() → 1 trace (not 3) | ||
| * - -vvvvv: no dedup — estimateGas + sendTx both shown per write | ||
| * - Red header on failed RPC, dim on success | ||
| * - Batch-mined txs grouped under 1 "Traces from" header | ||
| */ | ||
| import hre from "hardhat"; | ||
|
|
||
| // ═══════════════════════════════════════════════════════════════════════ | ||
| // Setup: two connections to two independent EDR-simulated networks | ||
| // ═══════════════════════════════════════════════════════════════════════ | ||
| const connA = await hre.network.connect("default"); | ||
| const connB = await hre.network.connect("node"); | ||
|
|
||
| const [counterA, counterB, revertContract] = await Promise.all([ | ||
| connA.viem.deployContract("Counter", []), | ||
| connB.viem.deployContract("Counter", []), | ||
| connA.viem.deployContract("Revert", []), | ||
| ]); | ||
|
|
||
| const logic = await connA.viem.deployContract("Logic", []); | ||
| const orchestrator = await connA.viem.deployContract("Orchestrator", [ | ||
| logic.address, | ||
| ]); | ||
| const factory = await connA.viem.deployContract("CallTypesFactory", []); | ||
|
|
||
| // ═══════════════════════════════════════════════════════════════════════ | ||
| // 1. All call kinds — CALL, CREATE, STATICCALL, DELEGATECALL, events | ||
| // ═══════════════════════════════════════════════════════════════════════ | ||
| console.log("\n╔══════════════════════════════════════════════════════╗"); | ||
| console.log("║ 1 — All call kinds ║"); | ||
| console.log("╚══════════════════════════════════════════════════════╝\n"); | ||
|
|
||
| // CALL + event: Orchestrator → Logic.setValue (external CALL, emits ValueSet) | ||
| console.log(" 1a. CALL + event: Orchestrator.doCall(42)\n"); | ||
| await orchestrator.write.doCall([42n]); | ||
|
|
||
| // STATICCALL: Orchestrator → Logic.getValue (view → STATICCALL) | ||
| console.log("\n 1b. STATICCALL: Orchestrator.doStaticCall()\n"); | ||
| await orchestrator.read.doStaticCall(); | ||
|
|
||
| // CREATE: deploy new contracts inline (via CallTypesFactory) | ||
| console.log("\n 1c. CREATE: CallTypesFactory.deploy()\n"); | ||
| await factory.write.deploy(); | ||
|
|
||
| // Mixed: DELEGATECALL + CALL + STATICCALL + event in one transaction | ||
| console.log( | ||
| "\n 1d. Mixed (all types in 1 tx): Orchestrator.doAllCallTypes(7, 3)\n", | ||
| ); | ||
| await orchestrator.write.doAllCallTypes([7n, 3n]); | ||
|
|
||
| // DELEGATECALL: Orchestrator → Logic.setValue via delegatecall | ||
| // NOTE: This must come last — delegatecall overwrites Orchestrator's storage | ||
| // (slot 0 = logic address), making subsequent calls to logic.* fail. | ||
| console.log("\n 1e. DELEGATECALL: Orchestrator.doDelegateCall(42)\n"); | ||
| await orchestrator.write.doDelegateCall([42n]); | ||
|
|
||
| // ═══════════════════════════════════════════════════════════════════════ | ||
| // 2. Multi-connection — sequential txs on two networks | ||
| // Headers should show different connection labels / colors | ||
| // ═══════════════════════════════════════════════════════════════════════ | ||
| console.log("\n╔══════════════════════════════════════════════════════╗"); | ||
| console.log("║ 2 — Multi-connection, sequential ║"); | ||
| console.log("╚══════════════════════════════════════════════════════╝\n"); | ||
|
|
||
| await counterA.write.inc(); | ||
| await counterB.write.inc(); | ||
|
|
||
| // ═══════════════════════════════════════════════════════════════════════ | ||
| // 3. Multi-connection — concurrent txs on both networks | ||
| // Traces from each connection are atomic (no interleaving) | ||
| // ═══════════════════════════════════════════════════════════════════════ | ||
| console.log("\n╔══════════════════════════════════════════════════════╗"); | ||
| console.log("║ 3 — Multi-connection, concurrent (3 txs each) ║"); | ||
| console.log("╚══════════════════════════════════════════════════════╝\n"); | ||
|
|
||
| await Promise.all([ | ||
| counterA.write.inc(), | ||
| counterA.write.inc(), | ||
| counterA.write.inc(), | ||
| counterB.write.inc(), | ||
| counterB.write.inc(), | ||
| counterB.write.inc(), | ||
| ]); | ||
|
|
||
| // ═══════════════════════════════════════════════════════════════════════ | ||
| // 4. Batch mining with evm_mine — grouped traces under a single header | ||
| // "Traces from connection #N (network): evm_mine" (plural) | ||
| // ═══════════════════════════════════════════════════════════════════════ | ||
| console.log("\n╔══════════════════════════════════════════════════════╗"); | ||
| console.log("║ 4 — evm_mine: 5 txs in one block (grouping demo) ║"); | ||
| console.log("╚══════════════════════════════════════════════════════╝\n"); | ||
| console.log(' → expect 1 "Traces from" header + 5 traces below it\n'); | ||
|
|
||
| await connA.provider.request({ method: "evm_setAutomine", params: [false] }); | ||
| await Promise.all(Array.from({ length: 5 }, () => counterA.write.inc())); | ||
| await connA.provider.request({ method: "evm_mine", params: [] }); | ||
| await connA.provider.request({ method: "evm_setAutomine", params: [true] }); | ||
|
|
||
| // ═══════════════════════════════════════════════════════════════════════ | ||
| // 5. Failed RPC calls — header should be red, method name highlighted | ||
| // ═══════════════════════════════════════════════════════════════════════ | ||
| console.log("\n╔══════════════════════════════════════════════════════╗"); | ||
| console.log("║ 5 — Failing calls (red header / method name) ║"); | ||
| console.log("╚══════════════════════════════════════════════════════╝\n"); | ||
|
|
||
| // 5a. Revert contract with known error message | ||
| console.log(" 5a. Revert.boom() — reverts with 'Boom':\n"); | ||
| try { | ||
| await revertContract.read.boom(); | ||
| } catch (e: any) { | ||
| console.log(` → Reverted: ${e.details ?? e.message}\n`); | ||
| } | ||
|
|
||
| // 5b. Invalid selector on Counter (no fallback) | ||
| console.log(" 5b. Invalid selector 0xdeadbeef on Counter:\n"); | ||
| try { | ||
| await connA.provider.request({ | ||
| method: "eth_call", | ||
| params: [{ to: counterA.address, data: "0xdeadbeef" }, "latest"], | ||
| }); | ||
| } catch { | ||
| console.log(" → Reverted as expected\n"); | ||
| } | ||
|
|
||
| // 5c. Orchestrator.doCallThatReverts(0) — nested revert | ||
| // Deploy fresh orchestrator since the earlier doDelegateCall corrupted storage | ||
| console.log(" 5c. Orchestrator.doCallThatReverts(0) — nested revert:\n"); | ||
| const logic2 = await connA.viem.deployContract("Logic", []); | ||
| const orch2 = await connA.viem.deployContract("Orchestrator", [logic2.address]); | ||
| try { | ||
| await orch2.write.doCallThatReverts([0n]); | ||
| } catch (e: any) { | ||
| console.log(` → Reverted: ${e.details ?? e.message}\n`); | ||
| } | ||
|
|
||
| // ═══════════════════════════════════════════════════════════════════════ | ||
| // 6. Deduplication — single write triggers estimateGas + sendTx + receipt | ||
| // but only ONE trace should appear | ||
| // ═══════════════════════════════════════════════════════════════════════ | ||
| console.log("\n╔══════════════════════════════════════════════════════╗"); | ||
| console.log("║ 6 — Deduplication ║"); | ||
| console.log("╚══════════════════════════════════════════════════════╝\n"); | ||
| console.log(" A single counter.write.inc() triggers 3 RPC calls:"); | ||
| console.log(" 1. eth_estimateGas → suppressed at -vvvv, shown at -vvvvv"); | ||
| console.log(" 2. eth_sendTransaction → always shown (this is the real tx)"); | ||
| console.log(" 3. eth_getTransactionReceipt → no traces (read-only)"); | ||
| console.log( | ||
| " At -vvvv: 1 trace. At -vvvvv: 2 traces (estimateGas + sendTx).\n", | ||
| ); | ||
|
|
||
| await counterA.write.inc(); | ||
|
|
||
| // ═══════════════════════════════════════════════════════════════════════ | ||
| // Summary | ||
| // ═══════════════════════════════════════════════════════════════════════ | ||
| console.log("\n╔══════════════════════════════════════════════════════╗"); | ||
| console.log("║ Summary ║"); | ||
| console.log("╚══════════════════════════════════════════════════════╝"); | ||
| console.log(""); | ||
| console.log(" -vvvv: dedup + suppression active (1 trace per write)"); | ||
| console.log(" -vvvvv: no dedup (estimateGas + sendTx both shown)"); | ||
| console.log(""); | ||
| console.log("Done."); | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IGNORE: only used for manual testing - will be removed before merging.
Run
pnpm hardhat run scripts/demo-trace-output.ts -vvvv(or -vvvvv) inexample-projectto see a demoUh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I will keep them, it could be useful for the future