Skip to content

Add a ERC1967Clones library#6405

Open
Amxx wants to merge 6 commits intoOpenZeppelin:masterfrom
Amxx:ERC1967Clones
Open

Add a ERC1967Clones library#6405
Amxx wants to merge 6 commits intoOpenZeppelin:masterfrom
Amxx:ERC1967Clones

Conversation

@Amxx
Copy link
Copy Markdown
Collaborator

@Amxx Amxx commented Mar 13, 2026

These minimal proxy differ from the "normal" ERC1967Proxy:

  • no initdata is provided at construction. Factory that uses the ERC1967Clones library must initialize manually.
  • construction doesn't revert if implementation has no code.
  • Upgrade(address) event is not emitted at construction

PR Checklist

  • Tests
  • Documentation
  • Changeset entry (run npx changeset add)

@Amxx Amxx added this to the 5.7 milestone Mar 13, 2026
@Amxx Amxx requested a review from a team as a code owner March 13, 2026 11:26
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 13, 2026

⚠️ No Changeset found

Latest commit: b72a49e

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 13, 2026

Walkthrough

This pull request introduces a new ERC1967Clones library in contracts/proxy/ERC1967/ERC1967Clones.sol that provides deployment and clone-address utilities using inline assembly. The library includes functions for non-deterministic deployment via CREATE and deterministic deployment via CREATE2, along with address computation helpers. Supporting test suite and test infrastructure updates are included, including modifications to the shared proxy behavior specification to accept configuration objects instead of boolean parameters.

Suggested labels

ignore-changeset

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add a ERC1967Clones library' directly and clearly describes the main change introduced in the pull request: the addition of a new ERC1967Clones library with deployment and clone-address utilities.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The pull request description directly relates to the changeset by explaining the purpose and key differences of the new ERC1967Clones library being introduced.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can suggest fixes for GitHub Check annotations.

Configure the reviews.tools.github-checks setting to adjust the time to wait for GitHub Checks to complete.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
test/proxy/ERC1967/ERC1967Clones.test.js (2)

36-36: Add a positive non-contract implementation case.

allowNonContractAddress: true disables the shared rejection test, but this file never replaces it with an assertion that deploying against this.nonContractAddress actually succeeds and stores that address in the implementation slot. That is one of the main behavioral deltas of ERC1967Clones, so it should be covered directly.

Also applies to: 54-54

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/proxy/ERC1967/ERC1967Clones.test.js` at line 36, The test disables the
shared rejection for non-contract addresses but never adds the positive path;
add a new assertion in the same suite (where shouldBehaveLikeProxy is called
with allowNonContractAddress: true) that deploys the proxy/clone pointing at
this.nonContractAddress and then reads the implementation slot (via the helper
used elsewhere in the tests, e.g., the ERC1967 implementation getter or direct
slot read) to assert it equals this.nonContractAddress; ensure the test actually
performs the deployment call that was previously skipped and then verifies the
stored implementation value.

39-46: Assert the predicted CREATE2 address.

The deterministic path currently recovers the clone address from return$deployDeterministic only, so the new computeAddress helpers can drift without failing this suite. A fixed-salt case that compares the predicted address to the emitted/deployed address would cover the new API directly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/proxy/ERC1967/ERC1967Clones.test.js` around lines 39 - 46, Update the
deterministic deployment test to assert the predicted CREATE2 address matches
the emitted/deployed address: in the createProxy helper (the async function
using this.factory.$deployDeterministic and reading the address from the
return$deployDeterministic event) pass a fixed salt (instead of a random one)
and call the new computeAddress helper (or factory.computeAddress /
computeCreate2Address) with the same implementation and salt to derive the
expected address, then assert that this predicted address equals the address
recovered from the event before returning the contract instance.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@contracts/proxy/ERC1967/ERC1967Clones.sol`:
- Around line 64-68: The init/template bytecode in ERC1967Clones.sol stores
implementationSlot and returns without emitting the Upgraded(address) event;
update the init code sequences around the mstore calls that write
implementationSlot and implementation (the blocks using implementationSlot and
implementation at the ptr locations shown) to also emit Upgraded(implementation)
before returning. Concretely, add the LOG1 opcode sequence (topic =
keccak256("Upgraded(address)")) that reads the stored implementation value (the
same value put via the implementation variable) and logs it as the event
topic/argument in both places matching the two code blocks (the current
sequences at the ptr offsets and the repeated sequence later), ensuring the
event uses the ERC1967 Upgraded signature and the implementation value already
stored at ptr so the runtime/creation behavior matches ERC1967Proxy.

---

Nitpick comments:
In `@test/proxy/ERC1967/ERC1967Clones.test.js`:
- Line 36: The test disables the shared rejection for non-contract addresses but
never adds the positive path; add a new assertion in the same suite (where
shouldBehaveLikeProxy is called with allowNonContractAddress: true) that deploys
the proxy/clone pointing at this.nonContractAddress and then reads the
implementation slot (via the helper used elsewhere in the tests, e.g., the
ERC1967 implementation getter or direct slot read) to assert it equals
this.nonContractAddress; ensure the test actually performs the deployment call
that was previously skipped and then verifies the stored implementation value.
- Around line 39-46: Update the deterministic deployment test to assert the
predicted CREATE2 address matches the emitted/deployed address: in the
createProxy helper (the async function using this.factory.$deployDeterministic
and reading the address from the return$deployDeterministic event) pass a fixed
salt (instead of a random one) and call the new computeAddress helper (or
factory.computeAddress / computeCreate2Address) with the same implementation and
salt to derive the expected address, then assert that this predicted address
equals the address recovered from the event before returning the contract
instance.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 20ab8844-08bf-4c3d-9c26-584d3d11a3b3

📥 Commits

Reviewing files that changed from the base of the PR and between 45f032d and c657d51.

📒 Files selected for processing (4)
  • contracts/proxy/ERC1967/ERC1967Clones.sol
  • test/proxy/ERC1967/ERC1967Clones.test.js
  • test/proxy/ERC1967/ERC1967Proxy.test.js
  • test/proxy/Proxy.behaviour.js

Comment on lines +64 to +68
mstore(add(ptr, 0x52), 0x545af43d5f5f3e5f3d91603757fd5bf3)
mstore(add(ptr, 0x42), implementationSlot)
mstore(add(ptr, 0x22), 0x60095155f3365f5f375f5f365f7f)
mstore(add(ptr, 0x14), implementation)
mstore(ptr, 0x60395f8160225f3973)
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.

🛠️ Refactor suggestion | 🟠 Major

Emit Upgraded from the init code.

This template stores the implementation slot and returns without ever logging Upgraded(implementation). That is a meaningful ERC-1967/tooling compatibility gap, and it introduces an extra behavioral difference vs ERC1967Proxy beyond the two called out for this PR. If the bytecode-size tradeoff is intentional, it should be documented explicitly.

Also applies to: 83-87

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/proxy/ERC1967/ERC1967Clones.sol` around lines 64 - 68, The
init/template bytecode in ERC1967Clones.sol stores implementationSlot and
returns without emitting the Upgraded(address) event; update the init code
sequences around the mstore calls that write implementationSlot and
implementation (the blocks using implementationSlot and implementation at the
ptr locations shown) to also emit Upgraded(implementation) before returning.
Concretely, add the LOG1 opcode sequence (topic =
keccak256("Upgraded(address)")) that reads the stored implementation value (the
same value put via the implementation variable) and logs it as the event
topic/argument in both places matching the two code blocks (the current
sequences at the ptr offsets and the repeated sequence later), ensuring the
event uses the ERC1967 Upgraded signature and the implementation value already
stored at ptr so the runtime/creation behavior matches ERC1967Proxy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant