Skip to content

fix: let a selected EIP-712 definition win name clashes with non-selected sources - #8581

Merged
popescuoctavian merged 2 commits into
mainfrom
popescuoctavian/fix-eip712-types-clash
Sep 1, 2026
Merged

fix: let a selected EIP-712 definition win name clashes with non-selected sources#8581
popescuoctavian merged 2 commits into
mainfrom
popescuoctavian/fix-eip712-types-clash

Conversation

@popescuoctavian

Copy link
Copy Markdown
Contributor
  • Because this PR includes a bug fix, relevant tests have been included.
  • Because this PR includes a new feature, the change was previously discussed on an Issue or with someone from the team.
  • I didn't do anything of this.

Running test solidity on the aave-v4 migration branch (pinned at 27c95ea) aborts with:

Error HHE818: Two different EIP-712 struct definitions named "PositionManagerUpdate" were found:
- tests/helpers/mocks/EIP712Types.sol
- src/config-engine/interfaces/IAaveV4ConfigEngine.sol

The two definitions. The project's hardhat.config.ts selects a single file, eip712Types.include: ['tests/helpers/mocks/EIP712Types.sol'], which defines both the signed root SetUserPositionManagers and, right below it, the dependency it references, PositionManagerUpdate {address positionManager; bool approve;} — the type whose name is baked into the on-chain typehash, so it cannot be renamed. The clash comes from a non-selected production interface that reuses the name for an unrelated configuration payload: IAaveV4ConfigEngine.PositionManagerUpdate {ISpokeConfigurator spokeConfigurator; address spoke; address positionManager; bool active;}. Neither side is reasonably renameable (one is typehash-compatible with the deployed contracts, the other is audited production source shared with the Foundry build), and the config already does exactly what our docs prescribe.

Why the collector throws. canonicalizeStructs indexes struct definitions by name, selected sources first, so a selected definition deterministically wins any name clash. Conflicting non-selected definitions are deferred, and the deferred path threw whenever the clashing name was reachable from a selected struct — here PositionManagerUpdate is reachable because selected SetUserPositionManagers has a PositionManagerUpdate[] member. The throw's rationale ("which copy got inlined would depend on iteration order") is only true when the name has no selected definition; when one exists, it always wins, so the abort was spurious. It also made behavior inconsistent: the same clash was documented as harmless when the selected struct was only a root, but became fatal the moment a sibling selected struct referenced it. #8344 fixed the unreferenced-clash case; this is the remaining mixed (selected-vs-non-selected, reachable) case its repro didn't exercise.

Fix. The deferred conflict now throws only when the name is reachable AND has no selected definition (reachable.has(name) && !selectedNames.has(name)). This bounds the registry the way include promises (and matches Foundry's include-scoped bind-json registry, where non-included sources never contribute definitions), while HHE818 still fires on the genuinely ambiguous cases: two selected definitions of the same name, and clashes among non-selected definitions that a selected struct depends on. The HHE818 remediation text now also mentions resolving a dependency clash by including the source with the canonical definition.

Tests. The case asserting the old throw now asserts the selected definition wins (order-independently), plus a new regression test mirroring the aave-v4 shape exactly (one included file defines both the root and the dependency; a non-selected file redefines the dependency's name). All EIP-712 suites pass (89 tests); package lint/build pass; changeset included.

@popescuoctavian popescuoctavian added no changeset needed This PR doesn't require a changeset no docs needed This PR doesn't require links to documentation no peer bump needed labels Aug 27, 2026
@changeset-bot

changeset-bot Bot commented Aug 27, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 39979b6

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copilot AI lite review requested due to automatic review settings August 27, 2026 09:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes Hardhat’s EIP-712 type canonicalization conflict detection so that when a struct name is defined both in a selected source and in a non-selected source, the selected definition deterministically wins even if the name is reachable as a dependency—avoiding a spurious HHE818 abort.

Changes:

  • Adjust deferred name-clash throwing to only error for reachable conflicts when no selected definition exists.
  • Improve HHE818 dependency-conflict remediation guidance to mention resolving by including the canonical dependency source.
  • Update/add regression tests to assert selected-vs-non-selected reachable clashes resolve deterministically (including an aave-v4-shaped case).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
packages/hardhat/src/internal/builtin-plugins/solidity-test/eip712/canonicalize.ts Updates conflict detection rule and remediation text so selected definitions win reachable clashes vs non-selected sources.
packages/hardhat/test/internal/builtin-plugins/solidity-test/eip712/canonicalize.ts Updates existing test expectation and adds regression coverage for selected winning reachable clashes and the aave-v4-shaped scenario.
Suppressed comments (1)

packages/hardhat/test/internal/builtin-plugins/solidity-test/eip712/canonicalize.ts:587

  • Same as above: prefer sorting a copy ([...expected].sort()) to avoid mutating shared expectation arrays in-place.
      assert.deepEqual(
        canonicalizeStructs([...collected].reverse(), selected).sort(),
        [...expected.sort()],
      );

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +578 to +580
assert.deepEqual(canonicalizeStructs(collected, selected).sort(), [
...expected.sort(),
]);
@popescuoctavian

Copy link
Copy Markdown
Contributor Author

/bench scenarios=aave-v4

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Starting regression benchmark for c0ab12bccb9a (projects matching aave-v4).

@github-actions

Copy link
Copy Markdown
Contributor

❌ Regression benchmark failed for c0ab12bccb9a. This is either a detected performance regression or an infrastructure failure — see the run for details.

View workflow run

Copilot AI review requested due to automatic review settings August 27, 2026 12:08
@github-actions

Copy link
Copy Markdown
Contributor

hardhat

Total size of the bundle: 38M
Total number of dependencies (including transitive): 37

List of dependencies (sorted by size)
29M	total
7.3M	@sentry/core
5.2M	zod
2.7M	micro-eth-signer
1.9M	@noble/curves
1.7M	undici
1.3M	@nomicfoundation/hardhat-utils
1.2M	@noble/hashes
884K	@nomicfoundation/hardhat-vendored
864K	@streamparser/json
692K	tsx
656K	@nomicfoundation/hardhat-errors
624K	micro-packed
492K	@scure/bip39
408K	json-stream-stringify
368K	ethereum-cryptography
332K	@streamparser/json-node
320K	enquirer
320K	@nomicfoundation/hardhat-zod-utils
292K	semver
204K	ws
204K	adm-zip
168K	esbuild
168K	@scure/base
152K	@nomicfoundation/edr
136K	fast-equals
100K	chokidar
96K	@scure/bip32
72K	@nomicfoundation/solidity-analyzer
56K	rfdc
48K	ansi-colors
44K	resolve.exports
44K	readdirp
36K	p-map
24K	strip-ansi
24K	is-safe-filename
24K	env-paths
24K	ansi-regex

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 26 changed files in this pull request and generated 2 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment thread packages/hardhat/package.json
Comment thread packages/hardhat/CHANGELOG.md
@popescuoctavian
popescuoctavian force-pushed the popescuoctavian/fix-eip712-types-clash branch from 642df4b to 39979b6 Compare August 27, 2026 12:15
@popescuoctavian

Copy link
Copy Markdown
Contributor Author

/bench scenarios=aave-v4

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Starting regression benchmark for 39979b663a0a (projects matching aave-v4).

@github-actions

Copy link
Copy Markdown
Contributor

❌ Regression benchmark failed for 39979b663a0a. This is either a detected performance regression or an infrastructure failure — see the run for details.

View workflow run

@popescuoctavian
popescuoctavian changed the base branch from popescuoctavian/bench-update-aave-v4 to main August 31, 2026 12:14
@popescuoctavian
popescuoctavian changed the base branch from main to popescuoctavian/bench-update-aave-v4 August 31, 2026 12:14
Base automatically changed from popescuoctavian/bench-update-aave-v4 to main September 1, 2026 16:11
@popescuoctavian
popescuoctavian added this pull request to the merge queue Sep 1, 2026
Merged via the queue into main with commit fe1cc76 Sep 1, 2026
179 checks passed
@popescuoctavian
popescuoctavian deleted the popescuoctavian/fix-eip712-types-clash branch September 1, 2026 16:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no changeset needed This PR doesn't require a changeset no docs needed This PR doesn't require links to documentation no peer bump needed status:triaging

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants