UNPKG

hardhat

Version:

Hardhat is an extensible developer tool that helps smart contract developers increase productivity by reliably bringing together the tools they want.

72 lines 3.86 kB
import { bytesIncludesUtf8String, bytesToUtf8String, } from "@nomicfoundation/hardhat-utils/bytes"; import { isPathSelected } from "../../../utils/glob.js"; import { toUserSourceName } from "../../solidity/source-names.js"; import { buildUserDefinedValueTypeIndex, extractStructsFromAst, } from "./ast-walker.js"; import { canonicalizeStructs } from "./canonicalize.js"; /** * Walks every compiled source's AST, extracts every struct definition, and * returns the flat list of canonical EIP-712 type strings expected by EDR's * `eip712CanonicalTypes` config field. Only structs from sources matching * `include`/`exclude` are emitted; non-selected sources still feed the dep * graph so cross-file deps inline correctly. * * `inputToUserSource` maps solc input source names to user source names; it's * built by the caller from the artifact set so we don't pay to parse every * build info just to recover that mapping. * * When `include` is empty/unset the feature is off: collection short-circuits * and returns an empty list without parsing any build info. */ export function collectEip712CanonicalTypes(buildInfosAndOutputs, inputToUserSource, config) { const { include, exclude } = config; if (include.length === 0) { return []; } const collected = []; const selectedNames = new Set(); for (const { buildInfo, output } of buildInfosAndOutputs) { // Byte-level fast path: a build info whose source bytes don't contain // `struct ` can't define any EIP-712 type, so skip JSON-parsing its output. if (!bytesIncludesUtf8String(buildInfo, "struct ")) { continue; } const parsedOutput = JSON.parse(bytesToUtf8String(output)); const sources = parsedOutput.output.sources; if (sources === undefined) { continue; } // Two constraints determine the index's scope: // // 1. Per build info, not pooled across them. solc assigns node ids // fresh in each compilation, so the same numeric id can mean // different user-defined value types in different builds. Pooling // would let one compilation's definition silently overwrite // another's at the same key, mis-resolving `referencedDeclaration`. // See the test "scopes user-defined value type resolution per build // info when node ids collide" for a repro. // // 2. Whole build info, not narrowed to a subset of sources. A struct // member's `referencedDeclaration` can point at a user-defined // value type defined in any source within the same compilation, so // the index must cover every source in the build. const userDefinedValueTypeI = buildUserDefinedValueTypeIndex(Object.values(sources).map((s) => s.ast)); for (const [inputSourceName, source] of Object.entries(sources)) { // A transitive project file that doesn't produce an artifact is missing // from `inputToUserSource`; recovering the user source name from the // input source name covers that case. const userSourceName = inputToUserSource.get(inputSourceName) ?? toUserSourceName(inputSourceName); // Collect every source so non-selected files can serve as dep targets; // selection is enforced at emit time via `selectedNames`. const structs = extractStructsFromAst(source.ast, userSourceName, userDefinedValueTypeI); collected.push(...structs); if (isPathSelected(userSourceName, include, exclude)) { for (const s of structs) { selectedNames.add(s.name); } } } } return canonicalizeStructs(collected, selectedNames); } //# sourceMappingURL=index.js.map