hardhat
Version:
Hardhat is an extensible developer tool that helps smart contract developers increase productivity by reliably bringing together the tools they want.
106 lines • 4.69 kB
JavaScript
import { styleText } from "node:util";
import { createDebug } from "@nomicfoundation/hardhat-utils/debug";
import { formatTraces } from "./trace-formatters.js";
const log = createDebug("hardhat:core:network-manager:edr:trace-output");
// Rotating palette for per-connection coloring of trace headers.
const LABEL_COLORS = [
(text) => styleText("cyan", text),
(text) => styleText("magenta", text),
(text) => styleText("blueBright", text),
(text) => styleText("yellowBright", text),
(text) => styleText("cyanBright", text),
(text) => styleText("magentaBright", text),
];
// Keyed by `network name` (not connection label) so the map stays bounded
// by the number of distinct networks, not the number of connections.
const networkColorMap = new Map();
// These methods run a simulation before the actual transaction. We skip
// their traces on success to avoid duplicates, but still show them on
// failure since the real transaction won't be sent.
const TRACE_SUPPRESSED_METHODS = new Set(["eth_estimateGas"]);
// Bounded set: receipt-polling deduplication only needs a small window.
// Once the cap is reached the set is cleared so memory stays bounded
// in long-running nodes.
const TRACED_TX_HASHES_CAP = 1024;
/**
* Manages trace output formatting, deduplication, and coloring for a single
* provider connection.
*/
export class TraceOutputManager {
#printLineFn;
#connectionLabel;
#labelColor;
#verbosity;
#tracedTxHashes = new Set();
constructor(printLineFn, connectionId, networkName, verbosity) {
this.#printLineFn = printLineFn;
this.#connectionLabel = `connection #${connectionId} (${networkName})`;
this.#labelColor = this.#colorForNetwork(networkName);
this.#verbosity = verbosity;
}
/**
* Output call traces from an EDR response, applying deduplication and
* suppression rules based on the verbosity level.
*/
outputCallTraces(edrResponse, method, txHash, failed) {
try {
// At verbosity < 5, suppress simulation-only methods on success and
// deduplicate traces for the same transaction. At verbosity >= 5
// (#showAllTraces), every RPC call with traces is shown.
if (this.#verbosity < 5) {
// Skip successful simulation-only methods, their trace will appear
// again in the subsequent eth_sendTransaction. Failed simulations
// are shown because the sendTransaction may never happen.
if (!failed && TRACE_SUPPRESSED_METHODS.has(method)) {
return;
}
// Dedup: skip if we already traced this transaction.
// Prevents the same tx appearing multiple times from receipt polling.
if (txHash !== undefined && this.#tracedTxHashes.has(txHash)) {
return;
}
}
const rawTraces = edrResponse.callTraces();
// EDR returns duplicate traces for eth_estimateGas, take only the first.
const callTraces = TRACE_SUPPRESSED_METHODS.has(method) && rawTraces.length > 1
? [rawTraces[0]]
: rawTraces;
if (callTraces.length === 0) {
return;
}
if (txHash !== undefined) {
if (this.#tracedTxHashes.size >= TRACED_TX_HASHES_CAP) {
this.#tracedTxHashes.clear();
}
this.#tracedTxHashes.add(txHash);
}
const coloredLabel = this.#labelColor(this.#connectionLabel);
const prefix = callTraces.length > 1 ? "Traces from" : "Trace from";
const coloredPrefix = this.#labelColor(prefix);
const styledMethod = failed
? styleText("red", method)
: styleText("dim", method);
const header = `${coloredPrefix} ${coloredLabel}: ${styledMethod}`;
this.#printLineFn(`${header}\n${formatTraces(callTraces, " ")}`);
}
catch (e) {
log("Failed to get call traces: %O", e);
}
}
/**
* Clear the dedup set (e.g. on snapshot revert so replayed txs are traced again).
*/
clearTracedHashes() {
this.#tracedTxHashes.clear();
}
#colorForNetwork(networkName) {
let color = networkColorMap.get(networkName);
if (color === undefined) {
const index = networkColorMap.size % LABEL_COLORS.length;
color = LABEL_COLORS[index];
networkColorMap.set(networkName, color);
}
return color;
}
}
//# sourceMappingURL=trace-output.js.map