Skip to content

Commit 96b785c

Browse files
Verbosity (-vvv) improve style (#8031)
1 parent 7a9a64c commit 96b785c

File tree

9 files changed

+918
-58
lines changed

9 files changed

+918
-58
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
import "hardhat/console.sol";
5+
6+
/// @notice Demonstrates different EVM call types for trace output testing.
7+
/// Used by scripts/demo-call-types.ts to exercise [CALL], [CREATE],
8+
/// [STATICCALL], [DELEGATECALL], and [EVENT] trace tags.
9+
10+
/// @notice Helper contract with view and write functions
11+
contract Logic {
12+
event ValueSet(address indexed setter, uint256 value);
13+
14+
uint256 public value;
15+
16+
function setValue(uint256 _value) external {
17+
console.log("Logic.setValue:", _value);
18+
value = _value;
19+
emit ValueSet(msg.sender, _value);
20+
}
21+
22+
function getValue() external view returns (uint256) {
23+
return value;
24+
}
25+
26+
function pureAdd(uint256 a, uint256 b) external pure returns (uint256) {
27+
return a + b;
28+
}
29+
30+
function mustBePositive(uint256 _value) external {
31+
require(_value > 0, "Value must be positive");
32+
console.log("Logic.mustBePositive:", _value);
33+
value = _value;
34+
emit ValueSet(msg.sender, _value);
35+
}
36+
}
37+
38+
/// @notice Orchestrator that calls Logic, exercising multiple call types
39+
contract Orchestrator {
40+
event Orchestrated(uint256 result);
41+
42+
Logic public logic;
43+
44+
constructor(Logic _logic) {
45+
logic = _logic;
46+
}
47+
48+
/// @notice Regular external CALL to Logic.setValue
49+
function doCall(uint256 _value) external {
50+
logic.setValue(_value);
51+
}
52+
53+
/// @notice Calls Logic.mustBePositive — reverts when _value == 0
54+
function doCallThatReverts(uint256 _value) external {
55+
logic.mustBePositive(_value);
56+
}
57+
58+
/// @notice External view call to Logic → produces STATICCALL
59+
function doStaticCall() external view returns (uint256) {
60+
return logic.getValue();
61+
}
62+
63+
/// @notice Explicit delegatecall to Logic.setValue → produces DELEGATECALL trace
64+
function doDelegateCall(uint256 _value) external returns (bool success) {
65+
bytes memory data = abi.encodeWithSelector(Logic.setValue.selector, _value);
66+
(success, ) = address(logic).delegatecall(data);
67+
}
68+
69+
/// @notice Mixed: DELEGATECALL + CALL (Logic.setValue) + STATICCALL (Logic.getValue)
70+
function doAllCallTypes(uint256 a, uint256 b) external returns (uint256) {
71+
// DELEGATECALL to Logic.pureAdd (uses pureAdd to avoid storage collision)
72+
bytes memory data = abi.encodeWithSelector(Logic.pureAdd.selector, a, b);
73+
address(logic).delegatecall(data);
74+
// CALL to Logic.setValue
75+
logic.setValue(a + b);
76+
// STATICCALL to Logic.getValue
77+
uint256 readBack = logic.getValue();
78+
emit Orchestrated(readBack);
79+
return readBack;
80+
}
81+
}
82+
83+
/// @notice Factory that creates contracts in a single transaction
84+
contract CallTypesFactory {
85+
event ContractsDeployed(address indexed logic, address indexed orchestrator);
86+
87+
function deploy() external returns (address, address) {
88+
Logic logicContract = new Logic();
89+
Orchestrator orch = new Orchestrator(logicContract);
90+
emit ContractsDeployed(address(logicContract), address(orch));
91+
return (address(logicContract), address(orch));
92+
}
93+
}
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/**
2+
* Comprehensive demo of all trace output features.
3+
*
4+
* Run with dedup (normal):
5+
* pnpm hardhat run scripts/demo-trace-output.ts -vvvv
6+
*
7+
* Run without dedup (all traces):
8+
* pnpm hardhat run scripts/demo-trace-output.ts -vvvvv
9+
*
10+
* For ANSI color output (red headers on failure, dim on success):
11+
* FORCE_COLOR=3 pnpm hardhat run scripts/demo-trace-output.ts -vvvv
12+
*
13+
* What to look for:
14+
* - All call kinds: CALL, CREATE, STATICCALL, DELEGATECALL
15+
* - Connection labels: "Trace from connection #0 (default)", "#1 (node)"
16+
* - -vvvv: dedup active — single counter.write.inc() → 1 trace (not 3)
17+
* - -vvvvv: no dedup — estimateGas + sendTx both shown per write
18+
* - Red header on failed RPC, dim on success
19+
* - Batch-mined txs grouped under 1 "Traces from" header
20+
*/
21+
import hre from "hardhat";
22+
23+
// ═══════════════════════════════════════════════════════════════════════
24+
// Setup: two connections to two independent EDR-simulated networks
25+
// ═══════════════════════════════════════════════════════════════════════
26+
const connA = await hre.network.connect("default");
27+
const connB = await hre.network.connect("node");
28+
29+
const [counterA, counterB, revertContract] = await Promise.all([
30+
connA.viem.deployContract("Counter", []),
31+
connB.viem.deployContract("Counter", []),
32+
connA.viem.deployContract("Revert", []),
33+
]);
34+
35+
const logic = await connA.viem.deployContract("Logic", []);
36+
const orchestrator = await connA.viem.deployContract("Orchestrator", [
37+
logic.address,
38+
]);
39+
const factory = await connA.viem.deployContract("CallTypesFactory", []);
40+
41+
// ═══════════════════════════════════════════════════════════════════════
42+
// 1. All call kinds — CALL, CREATE, STATICCALL, DELEGATECALL, events
43+
// ═══════════════════════════════════════════════════════════════════════
44+
console.log("\n╔══════════════════════════════════════════════════════╗");
45+
console.log("║ 1 — All call kinds ║");
46+
console.log("╚══════════════════════════════════════════════════════╝\n");
47+
48+
// CALL + event: Orchestrator → Logic.setValue (external CALL, emits ValueSet)
49+
console.log(" 1a. CALL + event: Orchestrator.doCall(42)\n");
50+
await orchestrator.write.doCall([42n]);
51+
52+
// STATICCALL: Orchestrator → Logic.getValue (view → STATICCALL)
53+
console.log("\n 1b. STATICCALL: Orchestrator.doStaticCall()\n");
54+
await orchestrator.read.doStaticCall();
55+
56+
// CREATE: deploy new contracts inline (via CallTypesFactory)
57+
console.log("\n 1c. CREATE: CallTypesFactory.deploy()\n");
58+
await factory.write.deploy();
59+
60+
// Mixed: DELEGATECALL + CALL + STATICCALL + event in one transaction
61+
console.log(
62+
"\n 1d. Mixed (all types in 1 tx): Orchestrator.doAllCallTypes(7, 3)\n",
63+
);
64+
await orchestrator.write.doAllCallTypes([7n, 3n]);
65+
66+
// DELEGATECALL: Orchestrator → Logic.setValue via delegatecall
67+
// NOTE: This must come last — delegatecall overwrites Orchestrator's storage
68+
// (slot 0 = logic address), making subsequent calls to logic.* fail.
69+
console.log("\n 1e. DELEGATECALL: Orchestrator.doDelegateCall(42)\n");
70+
await orchestrator.write.doDelegateCall([42n]);
71+
72+
// ═══════════════════════════════════════════════════════════════════════
73+
// 2. Multi-connection — sequential txs on two networks
74+
// Headers should show different connection labels / colors
75+
// ═══════════════════════════════════════════════════════════════════════
76+
console.log("\n╔══════════════════════════════════════════════════════╗");
77+
console.log("║ 2 — Multi-connection, sequential ║");
78+
console.log("╚══════════════════════════════════════════════════════╝\n");
79+
80+
await counterA.write.inc();
81+
await counterB.write.inc();
82+
83+
// ═══════════════════════════════════════════════════════════════════════
84+
// 3. Multi-connection — concurrent txs on both networks
85+
// Traces from each connection are atomic (no interleaving)
86+
// ═══════════════════════════════════════════════════════════════════════
87+
console.log("\n╔══════════════════════════════════════════════════════╗");
88+
console.log("║ 3 — Multi-connection, concurrent (3 txs each) ║");
89+
console.log("╚══════════════════════════════════════════════════════╝\n");
90+
91+
await Promise.all([
92+
counterA.write.inc(),
93+
counterA.write.inc(),
94+
counterA.write.inc(),
95+
counterB.write.inc(),
96+
counterB.write.inc(),
97+
counterB.write.inc(),
98+
]);
99+
100+
// ═══════════════════════════════════════════════════════════════════════
101+
// 4. Batch mining with evm_mine — grouped traces under a single header
102+
// "Traces from connection #N (network): evm_mine" (plural)
103+
// ═══════════════════════════════════════════════════════════════════════
104+
console.log("\n╔══════════════════════════════════════════════════════╗");
105+
console.log("║ 4 — evm_mine: 5 txs in one block (grouping demo) ║");
106+
console.log("╚══════════════════════════════════════════════════════╝\n");
107+
console.log(' → expect 1 "Traces from" header + 5 traces below it\n');
108+
109+
await connA.provider.request({ method: "evm_setAutomine", params: [false] });
110+
await Promise.all(Array.from({ length: 5 }, () => counterA.write.inc()));
111+
await connA.provider.request({ method: "evm_mine", params: [] });
112+
await connA.provider.request({ method: "evm_setAutomine", params: [true] });
113+
114+
// ═══════════════════════════════════════════════════════════════════════
115+
// 5. Failed RPC calls — header should be red, method name highlighted
116+
// ═══════════════════════════════════════════════════════════════════════
117+
console.log("\n╔══════════════════════════════════════════════════════╗");
118+
console.log("║ 5 — Failing calls (red header / method name) ║");
119+
console.log("╚══════════════════════════════════════════════════════╝\n");
120+
121+
// 5a. Revert contract with known error message
122+
console.log(" 5a. Revert.boom() — reverts with 'Boom':\n");
123+
try {
124+
await revertContract.read.boom();
125+
} catch (e: any) {
126+
console.log(` → Reverted: ${e.details ?? e.message}\n`);
127+
}
128+
129+
// 5b. Invalid selector on Counter (no fallback)
130+
console.log(" 5b. Invalid selector 0xdeadbeef on Counter:\n");
131+
try {
132+
await connA.provider.request({
133+
method: "eth_call",
134+
params: [{ to: counterA.address, data: "0xdeadbeef" }, "latest"],
135+
});
136+
} catch {
137+
console.log(" → Reverted as expected\n");
138+
}
139+
140+
// 5c. Orchestrator.doCallThatReverts(0) — nested revert
141+
// Deploy fresh orchestrator since the earlier doDelegateCall corrupted storage
142+
console.log(" 5c. Orchestrator.doCallThatReverts(0) — nested revert:\n");
143+
const logic2 = await connA.viem.deployContract("Logic", []);
144+
const orch2 = await connA.viem.deployContract("Orchestrator", [logic2.address]);
145+
try {
146+
await orch2.write.doCallThatReverts([0n]);
147+
} catch (e: any) {
148+
console.log(` → Reverted: ${e.details ?? e.message}\n`);
149+
}
150+
151+
// ═══════════════════════════════════════════════════════════════════════
152+
// 6. Deduplication — single write triggers estimateGas + sendTx + receipt
153+
// but only ONE trace should appear
154+
// ═══════════════════════════════════════════════════════════════════════
155+
console.log("\n╔══════════════════════════════════════════════════════╗");
156+
console.log("║ 6 — Deduplication ║");
157+
console.log("╚══════════════════════════════════════════════════════╝\n");
158+
console.log(" A single counter.write.inc() triggers 3 RPC calls:");
159+
console.log(" 1. eth_estimateGas → suppressed at -vvvv, shown at -vvvvv");
160+
console.log(" 2. eth_sendTransaction → always shown (this is the real tx)");
161+
console.log(" 3. eth_getTransactionReceipt → no traces (read-only)");
162+
console.log(
163+
" At -vvvv: 1 trace. At -vvvvv: 2 traces (estimateGas + sendTx).\n",
164+
);
165+
166+
await counterA.write.inc();
167+
168+
// ═══════════════════════════════════════════════════════════════════════
169+
// Summary
170+
// ═══════════════════════════════════════════════════════════════════════
171+
console.log("\n╔══════════════════════════════════════════════════════╗");
172+
console.log("║ Summary ║");
173+
console.log("╚══════════════════════════════════════════════════════╝");
174+
console.log("");
175+
console.log(" -vvvv: dedup + suppression active (1 trace per write)");
176+
console.log(" -vvvvv: no dedup (estimateGas + sendTx both shown)");
177+
console.log("");
178+
console.log("Done.");

0 commit comments

Comments
 (0)