Skip to content

🔨 contracts: avoid redundant deployments#865

Draft
itofarina wants to merge 4 commits intomainfrom
redeployer
Draft

🔨 contracts: avoid redundant deployments#865
itofarina wants to merge 4 commits intomainfrom
redeployer

Conversation

@itofarina
Copy link
Member

@itofarina itofarina commented Mar 4, 2026

Summary by CodeRabbit

  • Refactor

    • Deployment now requires an explicit preparation step and enforces readiness before plugin deployment; market and auditor references standardized.
  • Tests

    • Added coverage for the "not prepared" failure path, expanded deploy/recovery/refund scenarios, and updated existing tests to invoke preparation where required.
  • Chores

    • Updated gas snapshot estimates and added a changeset placeholder file.

@changeset-bot
Copy link

changeset-bot bot commented Mar 4, 2026

🦋 Changeset detected

Latest commit: 8b1ab79

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 0 packages

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

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai
Copy link

coderabbitai bot commented Mar 4, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds preparatory state and initialization to the Redeployer script (public auditor, marketUSDC, marketWETH), refactors plugin deployment into _deployPlugins() with _protocolOrStub() fallback, introduces NotPrepared() error and prepare() behavior, updates tests to call redeployer.prepare(), refreshes gas snapshots, and adds a changeset.

Changes

Cohort / File(s) Summary
Changeset
​.changeset/bumpy-foxes-refuse.md
Added new changeset file containing placeholder front-matter delimiters.
Gas Snapshots
contracts/.gas-snapshot
Updated gas snapshot entries: replaced/renamed RedeployerTest cases, added RefunderTest cases, and adjusted gas numbers.
Redeployer Script Refactor
contracts/script/Redeployer.s.sol
Added public IAuditor auditor, IMarket marketUSDC, IMarket marketWETH; added prepare() logic; introduced _protocolOrStub() and _deployPlugins(); switched ExaPlugin mapping to use new markets; added error NotPrepared(); adjusted deploy flows to require preparation.
Redeployer Tests
contracts/test/Redeployer.t.sol
Imported NotPrepared, added tests asserting revert when not prepared, and inserted redeployer.prepare() calls and minor nonce adjustments in multiple tests.

Sequence Diagram

sequenceDiagram
    participant Test
    participant Redeployer
    participant Auditor
    participant MarketUSDC as Market(USDC)
    participant MarketWETH as Market(WETH)
    participant ExaFactory

    Test->>Redeployer: new Redeployer()
    Test->>Redeployer: prepare()
    Redeployer->>Auditor: _protocolOrStub(resolve or deploy StubAuditor)
    Redeployer->>MarketUSDC: _protocolOrStub(resolve or deploy StubMarketUSDC)
    Redeployer->>MarketWETH: _protocolOrStub(resolve or deploy StubMarketWETH)
    Redeployer->>Redeployer: _deployPlugins(admin) (uses auditor, marketUSDC, marketWETH)
    Test->>Redeployer: deployExaFactory(admin)
    Redeployer->>ExaFactory: create factory + plugins
    ExaFactory-->>Redeployer: return deployment info
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • ✨ contracts: redeployer #807: Prior changes to contracts/script/Redeployer.s.sol that introduced the redeployer/plugin deployment flow and are directly related to this refactor.
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title references avoiding redundant stub deployments, which directly aligns with the main changes showing refactored deployment logic in Redeployer.s.sol that introduces _protocolOrStub and _deployPlugins methods to handle stub deployments more efficiently.

✏️ 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
  • Commit unit tests in branch redeployer

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refactors the Redeployer script to improve efficiency and correctness by eliminating redundant deployments of stub contracts. It introduces conditional logic to ensure that Auditor and Market stubs are only deployed when necessary, rather than on every execution. This change streamlines the script's setup process and ensures that dependencies are handled more intelligently, leading to a more robust and potentially gas-efficient deployment flow.

Highlights

  • Optimized Stub Deployment: The Redeployer script now conditionally deploys stub contracts (StubAuditor, StubAsset, StubMarket) only if they are not already available, preventing redundant deployments and improving efficiency.
  • Refactored Dependency Handling: The _deployDependencies function in Redeployer.s.sol was renamed to _deployPlugins and updated to integrate with the new conditional stub deployment logic, ensuring dependencies are managed more intelligently.
  • Enhanced Setup Logic: The setUp function in Redeployer.s.sol was extended to load Auditor, MarketUSDC, and MarketWETH from the protocol() function, making them available for subsequent operations.
  • Improved Test Coverage: Test cases in RedeployerTest.t.sol were updated to explicitly call the prepare() function, ensuring proper initialization of the redeployer script before deployment tests.
  • Gas Snapshot Updates: Gas consumption values in contracts/.gas-snapshot were adjusted, reflecting the changes in deployment logic and potential gas optimizations.
Changelog
  • .changeset/bumpy-foxes-refuse.md
    • Added a new changeset file.
  • contracts/.gas-snapshot
    • Updated gas consumption values for several RedeployerTest functions.
  • contracts/script/Redeployer.s.sol
    • Added IAuditor, IMarket marketUSDC, and IMarket marketWETH state variables.
    • Modified setUp to initialize auditor, marketUSDC, and marketWETH using protocol().
    • Refactored _deployDependencies into _deployPlugins, which now conditionally deploys StubAuditor, StubAsset, and StubMarket if not already set.
    • Updated deployExaFactory functions to call _deployPlugins.
    • Introduced a new custom error NotPrepared.
  • contracts/test/Redeployer.t.sol
    • Added redeployer.prepare() calls in test_deployExaFactory_deploysAtSameAddress_onPolygon() and test_deployExaFactory_deploysViaCreate3AtSameAddress_onPolygon().
Activity
  • No human activity has been recorded on this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the Redeployer script to avoid redundant stub deployments. The logic for handling dependencies like Auditor and Market contracts is moved from being inside each deployment function call to a one-time setup, which improves efficiency. The changes are well-structured and the tests have been updated accordingly.

I have one suggestion to improve the robustness of the stubbing logic to better mirror the previous implementation and prevent potential issues in tests.

@sentry
Copy link

sentry bot commented Mar 4, 2026

✅ All tests passed.

Copy link

@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


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1fa9a8c4-4d26-411b-b3cf-c176698c6dad

📥 Commits

Reviewing files that changed from the base of the PR and between e2e2ba3 and 6b4ddc7.

📒 Files selected for processing (4)
  • .changeset/bumpy-foxes-refuse.md
  • contracts/.gas-snapshot
  • contracts/script/Redeployer.s.sol
  • contracts/test/Redeployer.t.sol

@itofarina itofarina changed the title ⚡ contracts: avoid redundant stub deployments in redeployer 🔨 contracts: avoid redundant stub deployments Mar 4, 2026
Copy link

@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.

♻️ Duplicate comments (1)
contracts/script/Redeployer.s.sol (1)

68-86: ⚠️ Potential issue | 🟠 Major

Guard and backfill all plugin dependencies, not just auditor.

Line 68 only backfills when auditor is zero, and Line 180 only validates auditor. If auditor is set but marketUSDC/marketWETH are zero, plugin deployment can fail at constructor-time with invalid markets.

🔧 Suggested fix
-    if (address(auditor) == address(0)) {
+    if (address(auditor) == address(0)) {
       auditor = IAuditor(
         CREATE3_FACTORY.deploy(keccak256(abi.encode("StubAuditor")), vm.getCode("Redeployer.s.sol:StubAuditor"))
       );
-      address stubAsset =
-        CREATE3_FACTORY.deploy(keccak256(abi.encode("StubAsset")), vm.getCode("Redeployer.s.sol:StubAsset"));
+    }
+
+    if (address(marketUSDC) == address(0) || address(marketWETH) == address(0)) {
+      address stubAsset = CREATE3_FACTORY.getDeployed(admin, keccak256(abi.encode("StubAsset")));
+      if (stubAsset.code.length == 0) {
+        stubAsset =
+          CREATE3_FACTORY.deploy(keccak256(abi.encode("StubAsset")), vm.getCode("Redeployer.s.sol:StubAsset"));
+      }
+
+      if (address(marketUSDC) == address(0)) {
+        address stubUSDC = CREATE3_FACTORY.getDeployed(admin, keccak256(abi.encode("StubMarketUSDC")));
+        marketUSDC = IMarket(
+          stubUSDC.code.length == 0
+            ? CREATE3_FACTORY.deploy(
+              keccak256(abi.encode("StubMarketUSDC")),
+              abi.encodePacked(vm.getCode("Redeployer.s.sol:StubMarket"), abi.encode(stubAsset))
+            )
+            : stubUSDC
+        );
+      }
+
+      if (address(marketWETH) == address(0)) {
+        address stubWETH = CREATE3_FACTORY.getDeployed(admin, keccak256(abi.encode("StubMarketWETH")));
+        marketWETH = IMarket(
+          stubWETH.code.length == 0
+            ? CREATE3_FACTORY.deploy(
+              keccak256(abi.encode("StubMarketWETH")),
+              abi.encodePacked(vm.getCode("Redeployer.s.sol:StubMarket"), abi.encode(stubAsset))
+            )
+            : stubWETH
+        );
+      }
-      marketUSDC = IMarket(
-        CREATE3_FACTORY.deploy(
-          keccak256(abi.encode("StubMarketUSDC")),
-          abi.encodePacked(vm.getCode("Redeployer.s.sol:StubMarket"), abi.encode(stubAsset))
-        )
-      );
-      marketWETH = IMarket(
-        CREATE3_FACTORY.deploy(
-          keccak256(abi.encode("StubMarketWETH")),
-          abi.encodePacked(vm.getCode("Redeployer.s.sol:StubMarket"), abi.encode(stubAsset))
-        )
-      );
     }
@@
-    if (address(auditor) == address(0)) revert NotPrepared();
+    if (address(auditor) == address(0) || address(marketUSDC) == address(0) || address(marketWETH) == address(0)) {
+      revert NotPrepared();
+    }

Also applies to: 179-180


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6b9ddd18-4e44-47bb-b692-023871716822

📥 Commits

Reviewing files that changed from the base of the PR and between 6b4ddc7 and 31a5a09.

📒 Files selected for processing (4)
  • .changeset/bumpy-foxes-refuse.md
  • contracts/.gas-snapshot
  • contracts/script/Redeployer.s.sol
  • contracts/test/Redeployer.t.sol

@itofarina itofarina force-pushed the redeployer branch 2 times, most recently from 681ea16 to 478caf9 Compare March 4, 2026 21:06
Copy link

@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.

♻️ Duplicate comments (1)
contracts/script/Redeployer.s.sol (1)

68-86: ⚠️ Potential issue | 🟠 Major

Harden readiness checks for partially resolved dependencies.

prepare() and _deployPlugins() gate on auditor only. If auditor is present but marketUSDC/marketWETH are not, deployment can fail later in ExaPlugin construction instead of reverting with NotPrepared().

🔧 Suggested fix
-    if (address(auditor).code.length == 0) {
+    if (address(auditor).code.length == 0) {
       auditor = IAuditor(
         CREATE3_FACTORY.deploy(keccak256(abi.encode("StubAuditor")), vm.getCode("Redeployer.s.sol:StubAuditor"))
       );
+    }
+    if (address(marketUSDC).code.length == 0 || address(marketWETH).code.length == 0) {
       address stubAsset =
         CREATE3_FACTORY.deploy(keccak256(abi.encode("StubAsset")), vm.getCode("Redeployer.s.sol:StubAsset"));
-      marketUSDC = IMarket(
-        CREATE3_FACTORY.deploy(
-          keccak256(abi.encode("StubMarketUSDC")),
-          abi.encodePacked(vm.getCode("Redeployer.s.sol:StubMarket"), abi.encode(stubAsset))
-        )
-      );
-      marketWETH = IMarket(
-        CREATE3_FACTORY.deploy(
-          keccak256(abi.encode("StubMarketWETH")),
-          abi.encodePacked(vm.getCode("Redeployer.s.sol:StubMarket"), abi.encode(stubAsset))
-        )
-      );
+      if (address(marketUSDC).code.length == 0) {
+        marketUSDC = IMarket(
+          CREATE3_FACTORY.deploy(
+            keccak256(abi.encode("StubMarketUSDC")),
+            abi.encodePacked(vm.getCode("Redeployer.s.sol:StubMarket"), abi.encode(stubAsset))
+          )
+        );
+      }
+      if (address(marketWETH).code.length == 0) {
+        marketWETH = IMarket(
+          CREATE3_FACTORY.deploy(
+            keccak256(abi.encode("StubMarketWETH")),
+            abi.encodePacked(vm.getCode("Redeployer.s.sol:StubMarket"), abi.encode(stubAsset))
+          )
+        );
+      }
     }
@@
-    if (address(auditor).code.length == 0) revert NotPrepared();
+    if (
+      address(auditor).code.length == 0
+        || address(marketUSDC).code.length == 0
+        || address(marketWETH).code.length == 0
+    ) revert NotPrepared();

Also applies to: 179-193


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 27201171-afe2-4d37-9f59-68bbbe95d8e4

📥 Commits

Reviewing files that changed from the base of the PR and between 31a5a09 and 478caf9.

📒 Files selected for processing (4)
  • .changeset/bumpy-foxes-refuse.md
  • contracts/.gas-snapshot
  • contracts/script/Redeployer.s.sol
  • contracts/test/Redeployer.t.sol

Copy link

@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.

♻️ Duplicate comments (1)
contracts/script/Redeployer.s.sol (1)

179-193: ⚠️ Potential issue | 🟠 Major

Expand readiness guard to cover both markets.

_deployPlugins() currently guards only auditor, but it immediately consumes marketUSDC and marketWETH as constructor inputs. Missing market code can surface as an opaque low-level revert instead of NotPrepared.

🔧 Proposed fix
   function _deployPlugins(address admin) internal returns (IPlugin, IPlugin) {
-    if (address(auditor).code.length == 0) revert NotPrepared();
+    if (
+      address(auditor).code.length == 0 || address(marketUSDC).code.length == 0 || address(marketWETH).code.length == 0
+    ) revert NotPrepared();

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7b34488d-6631-4754-8b0f-08ec0d9f6758

📥 Commits

Reviewing files that changed from the base of the PR and between 478caf9 and 88934db.

📒 Files selected for processing (4)
  • .changeset/bumpy-foxes-refuse.md
  • contracts/.gas-snapshot
  • contracts/script/Redeployer.s.sol
  • contracts/test/Redeployer.t.sol

Copy link

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
contracts/script/Redeployer.s.sol (1)

90-103: ⚠️ Potential issue | 🟡 Minor

run() NatSpec says exclusive nonce, but implementation is inclusive.

The parameter docs and loop behavior diverge (<= targetNonce). Please make these consistent to prevent incorrect caller assumptions.

📝 Minimal fix (doc alignment)
-  /// `@param` targetNonce The nonce to stop at (exclusive)
+  /// `@param` targetNonce The nonce to stop at (inclusive)
♻️ Duplicate comments (1)
contracts/script/Redeployer.s.sol (1)

68-86: ⚠️ Potential issue | 🟠 Major

NotPrepared can be skipped while markets are still unresolved.

prepare() backfills stubs only when auditor is missing, and _deployPlugins() only validates auditor. If auditor resolves but marketUSDC/marketWETH do not, plugin deployment can fail with a low-level revert instead of NotPrepared, and prepare() won’t repair that state.

🔧 Suggested fix
-    if (address(auditor).code.length == 0) {
+    if (address(auditor).code.length == 0) {
       auditor = IAuditor(
         CREATE3_FACTORY.deploy(keccak256(abi.encode("StubAuditor")), vm.getCode("Redeployer.s.sol:StubAuditor"))
       );
-      address stubAsset =
-        CREATE3_FACTORY.deploy(keccak256(abi.encode("StubAsset")), vm.getCode("Redeployer.s.sol:StubAsset"));
+    }
+
+    if (address(marketUSDC).code.length == 0 || address(marketWETH).code.length == 0) {
+      address stubAsset = CREATE3_FACTORY.getDeployed(admin, keccak256(abi.encode("StubAsset")));
+      if (stubAsset.code.length == 0) {
+        stubAsset = CREATE3_FACTORY.deploy(keccak256(abi.encode("StubAsset")), vm.getCode("Redeployer.s.sol:StubAsset"));
+      }
+      if (address(marketUSDC).code.length == 0) {
       marketUSDC = IMarket(
         CREATE3_FACTORY.deploy(
           keccak256(abi.encode("StubMarketUSDC")),
           abi.encodePacked(vm.getCode("Redeployer.s.sol:StubMarket"), abi.encode(stubAsset))
         )
       );
+      }
+      if (address(marketWETH).code.length == 0) {
       marketWETH = IMarket(
         CREATE3_FACTORY.deploy(
           keccak256(abi.encode("StubMarketWETH")),
           abi.encodePacked(vm.getCode("Redeployer.s.sol:StubMarket"), abi.encode(stubAsset))
         )
       );
+      }
     }
@@
-    if (address(auditor).code.length == 0) revert NotPrepared();
+    if (address(auditor).code.length == 0 || address(marketUSDC).code.length == 0 || address(marketWETH).code.length == 0) {
+      revert NotPrepared();
+    }

Also applies to: 179-193


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ad9fb905-5aa6-418a-9507-a42a5b07c847

📥 Commits

Reviewing files that changed from the base of the PR and between 88934db and c3afa7e.

📒 Files selected for processing (3)
  • contracts/.gas-snapshot
  • contracts/script/Redeployer.s.sol
  • contracts/test/Redeployer.t.sol

@itofarina itofarina changed the title 🔨 contracts: avoid redundant stub deployments 🔨 contracts: avoid redundant deployments Mar 5, 2026
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.

2 participants