diff --git a/packages/hardhat-core/console.sol b/packages/hardhat-core/console.sol index f070e13f9a..f096790535 100644 --- a/packages/hardhat-core/console.sol +++ b/packages/hardhat-core/console.sol @@ -37,6 +37,7 @@ library console { function log() internal pure { _sendLogPayload(abi.encodeWithSignature("log()")); } + function logInt(int256 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(int256)", p0)); } @@ -1548,5 +1549,4 @@ library console { function log(address p0, address p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3)); } - } diff --git a/packages/hardhat-core/scripts/console-library-generator.ts b/packages/hardhat-core/scripts/console-library-generator.ts index 440b8a837a..0aa935e848 100644 --- a/packages/hardhat-core/scripts/console-library-generator.ts +++ b/packages/hardhat-core/scripts/console-library-generator.ts @@ -1,4 +1,4 @@ -import fs from "fs"; +import fs from "node:fs"; import { keccak256 } from "../src/internal/util/keccak"; @@ -17,7 +17,6 @@ function* genPermutations(elemCount: number, len: number) { // from 0 to max number of permutations (elemCount ** len) and convert // each number to a list of digits as per the base `elemCount`, see above. const numberOfPermutations = elemCount ** len; - // Pre-compute the base `elemCount` dividers ([1, elemCount, elemCount ** 2, ...]) const dividers = Array(elemCount) .fill(0) .map((_, i) => elemCount ** i); @@ -38,9 +37,9 @@ type TypeName = { type: string; modifier?: "memory" }; type FnParam = TypeName & { name: string }; /** Computes the function selector for the given function with simple arguments. */ -function selector({ name = "log", params = [] as TypeName[] }) { - const sig = params.map((p) => p.type).join(","); - return keccak256(Buffer.from(`${name}(${sig})`)).slice(0, 4); +function selector({ name = "", params = [] as TypeName[] }) { + const sigParams = params.map((p) => p.type).join(","); + return keccak256(Buffer.from(`${name}(${sigParams})`)).slice(0, 4); } function toHex(value: Uint8Array) { @@ -67,7 +66,66 @@ const TYPES = [ { type: "address" }, ] as const; -let consoleSolFile = `// SPDX-License-Identifier: MIT +/** A list of `console.log*` functions that we want to generate. */ +const CONSOLE_LOG_FUNCTIONS = + // Basic `log()` function + [{ name: "log", params: [] as FnParam[] }] + // Generate single parameter functions that are type-suffixed for + // backwards-compatibility, e.g. logInt, logUint, logString, etc. + .concat( + SINGLE_TYPES.map((single) => { + const param = { ...single, name: "p0" } as const; + const nameSuffix = capitalize(param.type.replace("int256", "int")); + + return { + name: `log${nameSuffix}`, + params: [param], + } as const; + }) + ) + // Also generate the function definitions for `log` for permutations of + // up to 4 parameters using the `types` (uint256, string, bool, address). + .concat( + [...Array(4)].flatMap((_, paramCount) => { + return Array.from( + genPermutations(TYPES.length, paramCount + 1), + (permutation) => ({ + name: "log", + params: permutation.map((typeIndex, i) => ({ + ...TYPES[typeIndex], + name: `p${i}`, + })), + }) + ); + }) + ); + +/** Maps from a 4-byte function selector to a signature (argument types) */ +const CONSOLE_LOG_SIGNATURES: Map = + CONSOLE_LOG_FUNCTIONS.reduce((acc, { params }) => { + // We always use `log` for the selector, even if it's logUint, for example. + const signature = toHex(selector({ name: "log", params })); + const types = params.map((p) => p.type); + acc.set(signature, types); + + // For backwards compatibility, we additionally support the (invalid) + // selectors that contain the `int`/`uint` aliases in the selector calculation. + if (params.some((p) => ["uint256", "int256"].includes(p.type))) { + const aliased = params.map((p) => ({ + ...p, + type: p.type.replace("int256", "int"), + })); + + const signature = toHex(selector({ name: "log", params: aliased })); + acc.set(signature, types); + } + + return acc; + }, new Map()); + +// Finally, render and save the console.sol and logger.ts files +const consoleSolFile = `\ +// SPDX-License-Identifier: MIT pragma solidity >=0.4.22 <0.9.0; library console { @@ -103,114 +161,46 @@ library console { _castToPure(_sendLogPayloadImplementation)(payload); } - function log() internal pure { - _sendLogPayload(abi.encodeWithSignature("log()")); - } -`; - -function renderLogFunction({ name = "log", params = [] as FnParam[] }): string { +${CONSOLE_LOG_FUNCTIONS.map(({ name, params }) => { let fnParams = params .map((p) => `${p.type}${p.modifier ? ` ${p.modifier}` : ""} ${p.name}`) .join(", "); let sig = params.map((p) => p.type).join(","); let passed = params.map((p) => p.name).join(", "); + let passedArgs = passed.length > 0 ? `, ${passed}` : ""; - return ` function ${name}(${fnParams}) internal pure { - _sendLogPayload(abi.encodeWithSignature("log(${sig})", ${passed})); + return `\ + function ${name}(${fnParams}) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(${sig})"${passedArgs})); } - `; +}).join("\n")}\ } +`; -let logger = - "// ------------------------------------\n" + - "// This code was autogenerated using\n" + - "// scripts/console-library-generator.ts\n" + - "// ------------------------------------\n\n"; - -for (let i = 0; i < SINGLE_TYPES.length; i++) { - const type = capitalize(SINGLE_TYPES[i].type); - - logger += `export const ${type}Ty = "${type}";\n`; -} +const loggerFile = `\ +// ------------------------------------ +// This code was autogenerated using +// scripts/console-library-generator.ts +// ------------------------------------ -logger += - "\n/** Maps from a 4-byte function selector to a signature (argument types) */\n" + - "export const CONSOLE_LOG_SIGNATURES: Record = {\n"; +${Array.from(SINGLE_TYPES.map((t) => capitalize(t.type))) + .map((t) => `export const ${t}Ty = "${t}";`) + .join("\n")} /** Maps from a 4-byte function selector to a signature (argument types) */ -const CONSOLE_LOG_SIGNATURES: Map = new Map(); - -// Add the empty log() first -const sigInt = toHex(selector({ name: "log", params: [] })); -CONSOLE_LOG_SIGNATURES.set(sigInt, []); - -// Generate single parameter functions that are type-suffixed for -// backwards-compatibility, e.g. logInt, logUint, logString, etc. -for (let i = 0; i < SINGLE_TYPES.length; i++) { - const param = { ...SINGLE_TYPES[i], name: "p0" } as const; - - const typeAliased = param.type.replace("int256", "int"); - const nameSuffix = capitalize(typeAliased); - - const signature = toHex(selector({ name: "log", params: [param] })); - CONSOLE_LOG_SIGNATURES.set(signature, [param.type]); - - // For full backwards compatibility, also support the (invalid) selectors of - // `log(int)` and `log(uint)`. The selector encoding specifies that one has to - // use the canonical type name but it seems that we supported it in the past. - if (["uint256", "int256"].includes(param.type)) { - const signature = toHex( - selector({ name: "log", params: [{ type: typeAliased }] }) - ); - CONSOLE_LOG_SIGNATURES.set(signature, [param.type]); - } - - consoleSolFile += renderLogFunction({ - name: `log${nameSuffix}`, - params: [param], - }); -} - -// Now generate the function definitions for `log` for permutations of -// up to 4 parameters using the `types` (uint256, string, bool, address). -const MAX_NUMBER_OF_PARAMETERS = 4; -for (let paramCount = 0; paramCount < MAX_NUMBER_OF_PARAMETERS; paramCount++) { - for (const permutation of genPermutations(TYPES.length, paramCount + 1)) { - const params = permutation.map( - (typeIndex, i) => ({ ...TYPES[typeIndex], name: `p${i}` } as const) - ); - - consoleSolFile += renderLogFunction({ params }); - - const types = params.map((p) => p.type); - const signature = toHex(selector({ name: "log", params })); - CONSOLE_LOG_SIGNATURES.set(signature, types); - - // Again, for full backwards compatibility, also support the (invalid) - // selectors that contain the `int`/`uint` aliases in the selector calculation. - if (params.some((p) => ["uint256", "int256"].includes(p.type))) { - const aliased = params.map((p) => ({ - ...p, - type: p.type.replace("int256", "int"), - })); - - const signature = toHex(selector({ name: "log", params: aliased })); - CONSOLE_LOG_SIGNATURES.set(signature, types); - } - } -} - -for (const [sig, types] of CONSOLE_LOG_SIGNATURES) { - const typeNames = types.map((t) => `${capitalize(t)}Ty`).join(", "); - logger += ` ${sig}: [${typeNames}],\n`; -} - -consoleSolFile += "}\n"; -logger += "};\n"; +export const CONSOLE_LOG_SIGNATURES: Record = { +${Array.from(CONSOLE_LOG_SIGNATURES) + .map(([sig, types]) => { + const typeNames = types.map((t) => `${capitalize(t)}Ty`).join(", "); + return ` ${sig}: [${typeNames}],`; + }) + .join("\n")} +}; +`; +fs.writeFileSync(__dirname + "/../console.sol", consoleSolFile); fs.writeFileSync( __dirname + "/../src/internal/hardhat-network/stack-traces/logger.ts", - logger + loggerFile ); -fs.writeFileSync(__dirname + "/../console.sol", consoleSolFile);