UNPKG

hardhat-tracer

Version:

Hardhat Tracer plugin

258 lines 10.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TraceRecorder = void 0; const debug_1 = __importDefault(require("debug")); const opcodes_1 = require("./opcodes"); const transaction_trace_1 = require("./transaction-trace"); const check_opcodes_1 = require("./utils/check-opcodes"); const hex_1 = require("./utils/hex"); const item_1 = require("./utils/item"); const utils_1 = require("./utils"); const debug = (0, debug_1.default)("hardhat-tracer:trace-recorder"); class TraceRecorder { constructor(vm, tracerEnv) { this.previousTraces = []; this.printIndex = -1; this.previousTracesMap = new Map(); this.vm = vm; this.tracerEnv = tracerEnv; (0, check_opcodes_1.checkIfOpcodesAreValid)(tracerEnv.opcodes, vm); this.awaitedItems = []; this.addressStack = []; vm.events.on("beforeTx", this.handleBeforeTx.bind(this)); vm.evm.events?.on("beforeMessage", this.handleBeforeMessage.bind(this)); // vm.evm.events?.on("newContract", this.handleNewContract.bind(this)); vm.evm.events?.on("step", this.handleStep.bind(this)); vm.evm.events?.on("afterMessage", this.handleAfterMessage.bind(this)); vm.events.on("afterTx", this.handleAfterTx.bind(this)); } storeLastTrace(txHash) { const lastTrace = this.previousTraces[this.previousTraces.length - 1]; if (lastTrace) { this.previousTracesMap.set(txHash.toLowerCase(), lastTrace); } } getTrace(txHash) { return this.previousTracesMap.get(txHash.toLowerCase()); } handleBeforeTx( // tx: any, // TypedTransaction, resolve) { if (!this.tracerEnv.switch.verboseEnabled) return resolve?.(); debug("handleBeforeTx"); this.trace = new transaction_trace_1.TransactionTrace(); // this.trace.hash = hexPrefix(Buffer.from(tx.hash()).toString("hex")); resolve?.(); } handleBeforeMessage(message, resolve) { if (!this.tracerEnv.switch.verboseEnabled) return resolve?.(); debug("handleBeforeMessage"); if (!this.trace) { throw new Error("[hardhat-tracer]: trace is undefined in handleBeforeMessage"); } const isDelegateCall = !!message.to && !!message.codeAddress && !message.to.equals(message.codeAddress); const isStaticCall = message.isStaticCall; const salt = undefined; // TODO let item; if (isDelegateCall) { if (message.to === undefined) { throw new Error("[hardhat-tracer]: message.to is undefined in handleBeforeMessage"); } item = { opcode: "DELEGATECALL", params: { from: (0, hex_1.hexPrefix)(message.caller.toString()), to: (0, hex_1.hexPrefix)((message.codeAddress ?? message.to).toString()), inputData: (0, hex_1.hexPrefix)(Buffer.from(message.data).toString("hex")), gasLimit: Number(message.gasLimit.toString()), value: this.trace.parent?.params.value, }, children: [], }; this.addressStack.push(item.params.to); } else if (message.to) { item = { opcode: isStaticCall ? "STATICCALL" : "CALL", params: { from: (0, hex_1.hexPrefix)(message.caller.toString()), to: (0, hex_1.hexPrefix)(message.to.toString()), inputData: (0, hex_1.hexPrefix)(Buffer.from(message.data).toString("hex")), gasLimit: Number(message.gasLimit.toString()), value: (0, hex_1.hexPrefix)(message.value.toString(16)), }, children: [], }; this.addressStack.push(item.params.to); } else if (message.to === undefined && salt === undefined) { item = { opcode: "CREATE", params: { from: (0, hex_1.hexPrefix)(message.caller.toString()), initCode: (0, hex_1.hexPrefix)(Buffer.from(message.data).toString("hex")), gasLimit: Number(message.gasLimit.toString()), value: (0, hex_1.hexPrefix)(message.value.toString(16)), }, children: [], }; this.addressStack.push({ value: "lazy" }); } else if (message.to === undefined && salt !== undefined) { item = { opcode: "CREATE2", params: { from: (0, hex_1.hexPrefix)(message.caller.toString()), initCode: (0, hex_1.hexPrefix)(Buffer.from(message.data).toString("hex")), gasLimit: Number(message.gasLimit.toString()), value: (0, hex_1.hexPrefix)(message.value.toString(16)), salt: (0, hex_1.hexPrefix)(Buffer.from(salt).toString("hex")), }, children: [], }; this.addressStack.push({ value: "lazy" }); } else { item = { opcode: "UNKNOWN_MESSAGE", params: {}, children: [], }; console.error("handleBeforeMessage: message type not handled", message); } this.trace.insertItem(item, { increaseDepth: true }); resolve?.(); } // This is now called in handleAfterMessage // prev: public handleNewContract( newContract(contractAddress, resolve) { if (!this.tracerEnv.switch.verboseEnabled) return resolve?.(); debug("handleNewContract %s", contractAddress); if (!this.trace || !this.trace.parent) { console.error("handleNewContract: trace.parent not present"); } else { switch (this.trace.parent.opcode) { case "CREATE": const createItem = this.trace.parent; createItem.params.deployedAddress = (0, hex_1.hexPrefix)(contractAddress); break; case "CREATE2": const create2Item = this.trace.parent; create2Item.params.deployedAddress = (0, hex_1.hexPrefix)(contractAddress); break; default: console.log(this.trace.parent); console.error("handleNewContract: opcode not handled"); break; } } let ptr = this.addressStack[this.addressStack.length - 1]; ptr.value = contractAddress; resolve?.(); } handleStep(step, resolve) { if (!this.tracerEnv.switch.verboseEnabled) return resolve?.(); // debug("handleStep %s", step.opcode.name); if (!this.trace) { throw new Error("[hardhat-tracer]: trace is undefined in handleStep"); } if (this.awaitedItems.length) { this.awaitedItems = this.awaitedItems.filter((awaitedItems) => awaitedItems.next > 0); for (const awaitedItem of this.awaitedItems) { awaitedItem.next--; if (awaitedItem.next === 0) { const item = awaitedItem.parse(step, this.addressStack[this.addressStack.length - 1]); this.trace.insertItem(item); } } } if (this.tracerEnv.enableAllOpcodes || this.tracerEnv.opcodes.get(step.opcode.name)) { const result = (0, opcodes_1.parse)(step, this.addressStack[this.addressStack.length - 1]); if (result) { if ((0, item_1.isItem)(result)) { debug("parsed step %s", step.opcode.name); this.trace.insertItem(result); } else { debug("parsed step awaited %s", step.opcode.name); this.awaitedItems.push(result); } } } this.previousOpcode = step.opcode.name; resolve?.(); } handleAfterMessage(evmResult, resolve) { if (!this.tracerEnv.switch.verboseEnabled) return resolve?.(); debug("handleAfterMessage"); if (!this.trace) { throw new Error("[hardhat-tracer]: trace is undefined in handleAfterMessage"); } let { errorStr, isException } = (0, utils_1.parseExec)(evmResult.execResult); // TODO add support - not doing for now, will add if needed // if (isSelfDestruct) { // const selfdestructs = Object.entries(evmResult.execResult.selfdestruct); // for (const [address, beneficiary] of selfdestructs) { // debug("self destruct %s", address); // this.trace.insertItem({ // opcode: "SELFDESTRUCT", // params: { // beneficiary: hexPrefix(beneficiary.toString("hex")), // }, // }); // } // } if (isException) { debug("exception %s", errorStr); this.trace.insertItem({ opcode: "EXCEPTION", params: { error: errorStr, type: "EvmError", }, }); } if (evmResult.execResult.contractAddress) { this.newContract(evmResult.execResult.contractAddress.toString()); } this.trace.returnCurrentCall((0, hex_1.hexPrefix)(evmResult.execResult.output?.toString("hex") ?? "0x"), Number(evmResult?.execResult.executionGasUsed), errorStr); this.addressStack.pop(); resolve?.(); } handleAfterTx( // _tx: any, // AfterTxEvent, resolve) { if (!this.tracerEnv.switch.verboseEnabled) return resolve?.(); debug("handleAfterTx"); if (this.tracerEnv.enabled) { if (!this.trace) { throw new Error("[hardhat-tracer]: trace is undefined in handleAfterTx"); } // store the trace for later use (printing or outputting) debug("record the trace"); this.previousTraces.push(this.trace); } // clear the trace debug("clear running trace data"); this.trace = undefined; this.previousOpcode = undefined; this.awaitedItems = []; this.addressStack = []; resolve?.(); } } exports.TraceRecorder = TraceRecorder; //# sourceMappingURL=trace-recorder.js.map