Skip to content

🔨 contracts: avoid redundant deployments#865

Merged
cruzdanilo merged 9 commits intomainfrom
redeployer
Apr 2, 2026
Merged

🔨 contracts: avoid redundant deployments#865
cruzdanilo merged 9 commits intomainfrom
redeployer

Conversation

@itofarina
Copy link
Copy Markdown
Member

@itofarina itofarina commented Mar 4, 2026

Summary by CodeRabbit

  • Refactor

    • Introduced an explicit prepare step, version-aware factory deployments, reuse/skip behavior for already-upgraded proxies, and adjusted serial-proxy nonce handling.
  • Tests

    • Added idempotence and reuse coverage, new "not prepared" revert tests, and updated proxy/deployment scenarios and test names to match the new flows.
  • Chores

    • Updated gas measurements and added a placeholder changeset.

Open with Devin

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Mar 4, 2026

🦋 Changeset detected

Latest commit: 5cb98c4

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
Copy Markdown

coderabbitai Bot commented Mar 4, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: abb4c475-0a87-4f32-a120-54e2de1b5d47

📥 Commits

Reviewing files that changed from the base of the PR and between 25655e8 and 5cb98c4.

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

Walkthrough

Adds preparatory CREATE3-based deployments and cached protocol state to the Redeployer script, replaces run() with proxyThrough() (previously serialProxies() in earlier text), introduces versioned deployExaFactory(string) and deployExaFactories(), adds NotPrepared() guard, updates tests and gas snapshots, and adds a new changeset file. (49 words)

Changes

Cohort / File(s) Summary
Changeset
​.changeset/bumpy-foxes-refuse.md
New changeset file added containing placeholder front-matter delimiters.
Gas snapshots
contracts/.gas-snapshot
Updated RedeployerTest gas entries: removed some prior scenarios, added/renamed prepare/serial-proxy and factory scenarios, and adjusted recorded gas values.
Redeployer script
contracts/script/Redeployer.s.sol
Significant rewrite: added cached state fields (auditor, marketUSDC, marketWETH, ownerPlugin, exaPlugin, factory); added helpers _broadcastOrCreate3, _protocolOrStub, _factory; replaced _deployDependencies; implemented prepare() that CREATE3-deploys or stubs components and grants roles; added NotPrepared(); renamed/adjusted proxy entrypoint to proxyThrough(uint256) with nonce boundary changes; changed deployExaFactory to deployExaFactory(string version) (versioned CREATE3 salt) and added deployExaFactories(); deployExaFactory(address) now checks ERC1967 implementation slot and reuses cached factory when available.
Redeployer tests
contracts/test/Redeployer.t.sol
Tests updated to call proxyThrough(...) and adjusted nonce expectations; added tests covering prepare() idempotency and reuse; added NotPrepared revert tests for factory deployment variants; added idempotency/upgrade tests for versioned factory deployment; renamed several test functions; imports updated.

Sequence Diagram

sequenceDiagram
    participant Test
    participant Redeployer
    participant Registry
    participant CREATE3
    participant ProposalManager
    participant ExaPlugin
    participant ExaFactory

    Test->>Redeployer: call prepare()
    Redeployer->>Registry: lookup protocol components (auditor/markets/plugins)
    alt component exists
        Registry-->>Redeployer: return existing address
    else missing
        Redeployer->>CREATE3: compute salt & CREATE3-deploy stub (rgba(100,150,240,0.5))
        CREATE3-->>Redeployer: return stub address
    end
    Redeployer->>CREATE3: ensure ExaFactory implementation per version (rgba(120,200,140,0.5))
    Redeployer-->>Test: prepare completed
    Test->>Redeployer: call proxyThrough(targetNonce) or deployExaFactory(version)/deployExaFactory(proxy)
    Redeployer->>ExaFactory: deploy/upgrade or reuse implementation
    ExaFactory-->>Redeployer: result/status
    Redeployer-->>Test: return status/result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • ✨ contracts: redeployer #807: Modifies the same Redeployer implementation and tests (deployExaFactory signature/logic, proxy/nonce flow, prepare behavior, NotPrepared), indicating strong overlap.
🚥 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 accurately reflects the main objective of the PR: refactoring the Redeployer contract to avoid redundant deployments by implementing conditional deployment logic and caching mechanisms.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • 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
Copy Markdown

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.

gemini-code-assist[bot]

This comment was marked as resolved.

@sentry
Copy link
Copy Markdown

sentry Bot commented Mar 4, 2026

Codecov Report

❌ Patch coverage is 84.84848% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.71%. Comparing base (feadfa1) to head (25655e8).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
contracts/script/Redeployer.s.sol 84.84% 10 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #865      +/-   ##
==========================================
+ Coverage   71.62%   71.71%   +0.08%     
==========================================
  Files         228      228              
  Lines        8234     8277      +43     
  Branches     2642     2661      +19     
==========================================
+ Hits         5898     5936      +38     
- Misses       2106     2111       +5     
  Partials      230      230              
Flag Coverage Δ
e2e 52.42% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

coderabbitai[bot]

This comment was marked as resolved.

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

@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
Copy Markdown

@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
Copy Markdown

@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
Copy Markdown

@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
@itofarina itofarina force-pushed the redeployer branch 5 times, most recently from 76965b3 to 7789af5 Compare March 11, 2026 20:55
@itofarina itofarina force-pushed the redeployer branch 2 times, most recently from 9345b59 to 13997b4 Compare March 12, 2026 20:24
@itofarina itofarina marked this pull request as ready for review March 12, 2026 20:25
@itofarina itofarina requested a review from cruzdanilo as a code owner March 12, 2026 20:25
devin-ai-integration[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

@itofarina itofarina force-pushed the redeployer branch 2 times, most recently from 78d7b91 to 449ada9 Compare March 27, 2026 17:03
chatgpt-codex-connector[bot]

This comment was marked as resolved.

Copy link
Copy Markdown

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

205-224: 🧹 Nitpick | 🔵 Trivial

Consider adding ownerPlugin to the NotPrepared check.

Line 210 only verifies exaPlugin.code.length, but line 219 also uses ownerPlugin in the factory constructor. While the intended flow (prepare() deploys ownerPlugin before exaPlugin) should guarantee both are available, an explicit check would make the invariant clearer and prevent misuse.

🔧 Suggested improvement
   function deployExaFactory(string calldata version) external returns (ExaAccountFactory f) {
     bytes32 salt = keccak256(abi.encode("Exa Plugin", version));
     f = ExaAccountFactory(payable(CREATE3_FACTORY.getDeployed(acct("admin"), salt)));
     if (address(f).code.length != 0) return f;
-    if (address(exaPlugin).code.length == 0) revert NotPrepared();
+    if (address(ownerPlugin).code.length == 0 || address(exaPlugin).code.length == 0) revert NotPrepared();

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ac128abc-2a58-4311-946b-ef16e079f4d3

📥 Commits

Reviewing files that changed from the base of the PR and between 78d7b91 and 449ada9.

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

coderabbitai[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

Copy link
Copy Markdown

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

148-150: 🧹 Nitpick | 🔵 Trivial

Consider using ProposalManager.PROPOSER_ROLE constant.

The inline keccak256("PROPOSER_ROLE") works but is less maintainable. ProposalManager already exposes this as a public constant.

♻️ Suggested improvement
-    if (!ProposalManager(proposalManagerAddr).hasRole(keccak256("PROPOSER_ROLE"), address(exaPlugin))) {
-      ProposalManager(proposalManagerAddr).grantRole(keccak256("PROPOSER_ROLE"), address(exaPlugin));
+    ProposalManager pm = ProposalManager(proposalManagerAddr);
+    if (!pm.hasRole(pm.PROPOSER_ROLE(), address(exaPlugin))) {
+      pm.grantRole(pm.PROPOSER_ROLE(), address(exaPlugin));
     }

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e21e6346-9d52-44dc-b3b4-6ec268b11725

📥 Commits

Reviewing files that changed from the base of the PR and between 26eefc4 and dad0474.

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

chatgpt-codex-connector[bot]

This comment was marked as resolved.

Copy link
Copy Markdown

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

198-202: ⚠️ Potential issue | 🟠 Major

Guard the upgrade path against undeployed addresses.

Line 202 can still look successful while doing nothing if either proxyAdmin or proxy has no code. In that state deployExaFactory(address) — and, downstream, deployExaFactories() — silently no-op instead of surfacing a bad precondition.

🔧 Proposed guard
 function deployExaFactory(address proxy) external {
   if (address(factory).code.length == 0) revert NotPrepared();
+  if (address(proxyAdmin).code.length == 0) revert ProxyAdminNotDeployed();
+  if (proxy.code.length == 0) revert ProxyNotDeployed();
   if (address(uint160(uint256(vm.load(proxy, ERC1967Utils.IMPLEMENTATION_SLOT)))) == address(factory)) return;
   vm.broadcast(acct("admin"));
   proxyAdmin.upgradeAndCall(ITransparentUpgradeableProxy(proxy), address(factory), "");
 }
+
+error ProxyNotDeployed();
In Solidity/OpenZeppelin, does calling a no-return external function on an address with no code revert, or can it succeed with empty returndata? Specifically, how do `ProxyAdmin.upgradeAndCall(...)` and `TransparentUpgradeableProxy.upgradeToAndCall(...)` behave when the `proxyAdmin` or `proxy` address has no code?

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3d2d24fc-832c-4b3e-ac29-281aab463114

📥 Commits

Reviewing files that changed from the base of the PR and between dad0474 and 25655e8.

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

@cruzdanilo cruzdanilo merged commit 5cb98c4 into main Apr 2, 2026
2 of 6 checks passed
@cruzdanilo cruzdanilo deleted the redeployer branch April 2, 2026 01:03
Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 14 additional findings in Devin Review.

Open in Devin Review

Comment on lines +227 to +234
function deployExaFactories() external {
this.deployExaFactory(0x8D493AF799162Ac3f273e8918B2842447f702163);
this.deployExaFactory(0x6E1b5A67adD32E8dC034c23b8022b54821ED297b);
this.deployExaFactory(0x3427a595eD6E05Cc2D8115e28BAd151cB879616e);
this.deployExaFactory(0xcbeaAF42Cc39c17e84cBeFe85160995B515A9668);
this.deployExaFactory("1.0.0");
this.deployExaFactory("1.1.0");
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚩 deployExaFactories hardcodes proxy addresses without chain-awareness

The deployExaFactories() function at contracts/script/Redeployer.s.sol:227-234 hardcodes four proxy addresses and two version strings. These addresses are presumably factory proxies deployed on specific chains. If called on a chain where these addresses don't correspond to valid proxies owned by the proxyAdmin, the proxyAdmin.upgradeAndCall inside deployExaFactory(address) would revert. The function doesn't check which chain it's running on, relying on the caller to invoke it on the correct chain.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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