Skip to content

Conversation

Amxx
Copy link
Collaborator

@Amxx Amxx commented Oct 1, 2025

Summary by CodeRabbit

  • New Features
    • Added a modular proxy with function-level routing and fallback handling.
    • Enabled dynamic module updates (add/replace/remove) via a standardized cutting interface.
    • Introduced ownership controls: view owner, transfer, and renounce with strict access checks.
    • Provided read-only introspection to resolve a selector’s module; broader listings deferred to off‑chain indexing.
    • Events now emitted on routing updates and ownership changes.
  • Tests
    • Comprehensive coverage for missing implementations, authorized/unauthorized updates, ETH receive/fallback behavior, and ownership workflows.
  • Chores
    • Added supporting mocks and imports for testing.

@Amxx Amxx requested a review from a team as a code owner October 1, 2025 15:42
Copy link

coderabbitai bot commented Oct 1, 2025

Walkthrough

Adds a dispatch-based proxy system: a Dispatch library with per-selector routing and ownership storage; a DispatchProxy using it; facet modules for updating the dispatch table, ownership, diamond cut/loupe interfaces and facets; a mock dispatch module; expanded mocks import; and comprehensive tests validating dispatch, ownership, fallback/receive, and update flows.

Changes

Cohort / File(s) Summary
Dispatch core library
contracts/proxy/dispatch/utils/Dispatch.sol
New library implementing fixed-slot VMT storage, owner management, selector→implementation mapping, and VMTUpdate event.
Proxy
contracts/proxy/dispatch/DispatchProxy.sol
New proxy that routes via Dispatch VMT; initializes owner and update selector; reverts on missing implementation; supports fallback selector.
Diamond interfaces
contracts/proxy/dispatch/interfaces/IDiamondCut.sol, contracts/proxy/dispatch/interfaces/IDiamondLoupe.sol
New interfaces defining DiamondCut actions, structs, event, function; Loupe read-only query interface.
Diamond facets
contracts/proxy/dispatch/modules/DiamondCutFacet.sol, contracts/proxy/dispatch/modules/DiamondLoupeFacet.sol
New facets: Cut implements diamondCut with add/replace/remove and init call; Loupe stubs for facets/selectors/addresses and selector→facet resolution.
Dispatch management modules
contracts/proxy/dispatch/modules/DispatchOwnershipModule.sol, contracts/proxy/dispatch/modules/DispatchUpdateModule.sol
New modules: Ownership (owner, transfer, renounce with onlyOwner via Dispatch); Update (owner-gated batch set of selector mappings).
Mocks
contracts/mocks/DispatchModuleMock.sol, contracts/mocks/import.sol
New mock module emitting Call event via fallback; added import for EtherReceiverMock.
Tests
test/proxy/DispatchProxy.test.js
New tests covering dispatch routing, ownership flows, update authorization, ETH receive/fallback behavior, and events.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Caller
  participant Proxy as DispatchProxy
  participant VMT as Dispatch.VMT
  participant Module as Target Facet (delegatecall)

  Caller->>Proxy: call fn(args)/send ETH
  Proxy->>VMT: lookup msg.sig
  alt exact match
    VMT-->>Proxy: impl address
  else no match
    VMT-->>Proxy: lookup _FALLBACK_SIG
  end
  alt impl found
    Proxy->>Module: delegatecall(args/value)
    Module-->>Proxy: return/revert
    Proxy-->>Caller: bubble result
  else none found
    Proxy-->>Caller: revert DispatchProxyMissingImplementation(msg.sig)
  end
Loading
sequenceDiagram
  autonumber
  actor Owner
  participant Proxy as DispatchProxy
  participant Update as DispatchUpdateModule (facet)
  participant VMT as Dispatch.VMT

  Owner->>Proxy: updateDispatchTable(modules)
  Proxy->>Update: delegatecall updateDispatchTable
  Update->>VMT: enforceOwner(Owner)
  loop for each module and selector
    Update->>VMT: setFunction(selector, impl)
    Note right of VMT: emit VMTUpdate
  end
  Update-->>Proxy: done
  Proxy-->>Owner: success
Loading
sequenceDiagram
  autonumber
  actor Owner
  participant Proxy as DispatchProxy
  participant Cut as DiamondCutFacet
  participant VMT as Dispatch.VMT
  participant Init as _init (optional)

  Owner->>Proxy: diamondCut(cuts, _init, _calldata)
  Proxy->>Cut: delegatecall diamondCut
  Cut->>VMT: enforceOwner(Owner)
  loop each FacetCut
    alt Add/Replace/Remove
      Cut->>VMT: setFunction/remove per selector
    end
  end
  Cut-->>Proxy: emit DiamondCut
  alt _init != address(0)
    Proxy->>Init: delegatecall _calldata
  end
  Proxy-->>Owner: success/revert bubbled
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • frangio
  • cjcobb23
  • arr00
  • luiz-lvj
  • ernestognw

Poem

A hop, a skip, a proxy leap—
Selectors map where modules keep.
VMTs hum, events go “ping,”
Ownership shifts with gentle spring.
Cut and Loupe, we route with cheer—
I thump approval: dispatch is here! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ 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 clearly describes the primary change: the introduction of a DispatchProxy implementing a non-enumerable diamond proxy pattern. It references the main contract name and its key characteristic, matching the addition of DispatchProxy.sol, the diamond cut and loupe facets, and the supporting dispatch modules and utilities. The phrasing is concise, specific, and informative without extraneous detail.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/diamondProxy

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.

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: 7

🧹 Nitpick comments (5)
contracts/proxy/dispatch/modules/DiamondCutFacet.sol (1)

1-16: Consider improving error naming.

The error names have some issues:

  • Line 14: DiamondCutFacetAlreadyExist should be DiamondCutFacetAlreadyExists (grammatically correct)
  • Line 16: DiamondCutFacetAlreadyDoesNotExit has a typo - should be "Exist" not "Exit"
  • Line 15: DiamondCutFacetAlreadySet is unclear for the Replace action validation failure

Consider renaming for clarity:

-    error DiamondCutFacetAlreadyExist(bytes4 selector);
-    error DiamondCutFacetAlreadySet(bytes4 selector);
-    error DiamondCutFacetAlreadyDoesNotExit(bytes4 selector);
+    error DiamondCutFacetAlreadyExists(bytes4 selector);
+    error DiamondCutFacetNotChanged(bytes4 selector);
+    error DiamondCutFacetDoesNotExist(bytes4 selector);
contracts/proxy/dispatch/modules/DiamondLoupeFacet.sol (4)

3-3: Consider using a locked pragma version.

Floating pragmas can lead to inconsistent behavior across deployments. For production contracts, it's recommended to lock the Solidity version.

-pragma solidity ^0.8.0;
+pragma solidity 0.8.28;

14-14: Remove unnecessary statement.

The standalone this; statement is used to suppress the "unused variable" warning for the implicit this in a view function, but this is non-idiomatic. Since the function unconditionally reverts, this statement serves no purpose and can be removed.

     function facets() public view override returns (Facet[] memory) {
-        this;
         revert("This implementation doesn't keep an index, use an offchain index instead");
     }

13-27: Consider using custom errors for gas efficiency.

The three stub functions use string revert messages. Custom errors are more gas-efficient and provide better error handling in production contracts.

Example implementation:

error IndexingNotSupported();

function facets() public view override returns (Facet[] memory) {
    revert IndexingNotSupported();
}

function facetFunctionSelectors(address) public view override returns (bytes4[] memory) {
    revert IndexingNotSupported();
}

function facetAddresses() public view override returns (address[] memory) {
    revert IndexingNotSupported();
}

5-10: Remove Context inheritance from DiamondLoupeFacet.sol. The contract doesn’t call _msgSender() or _msgData(), so you can drop the import {Context} and Context base to reduce unnecessary bytecode.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0ddd27 and 6fde1d7.

📒 Files selected for processing (11)
  • contracts/mocks/DispatchModuleMock.sol (1 hunks)
  • contracts/mocks/import.sol (1 hunks)
  • contracts/proxy/dispatch/DispatchProxy.sol (1 hunks)
  • contracts/proxy/dispatch/interfaces/IDiamondCut.sol (1 hunks)
  • contracts/proxy/dispatch/interfaces/IDiamondLoupe.sol (1 hunks)
  • contracts/proxy/dispatch/modules/DiamondCutFacet.sol (1 hunks)
  • contracts/proxy/dispatch/modules/DiamondLoupeFacet.sol (1 hunks)
  • contracts/proxy/dispatch/modules/DispatchOwnershipModule.sol (1 hunks)
  • contracts/proxy/dispatch/modules/DispatchUpdateModule.sol (1 hunks)
  • contracts/proxy/dispatch/utils/Dispatch.sol (1 hunks)
  • test/proxy/DispatchProxy.test.js (1 hunks)
🧰 Additional context used
🪛 GitHub Check: codespell
contracts/proxy/dispatch/modules/DiamondLoupeFacet.sol

[failure] 26-26:
doesnt ==> doesn't, does not


[failure] 21-21:
doesnt ==> doesn't, does not


[failure] 15-15:
doesnt ==> doesn't, does not

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: slither
  • GitHub Check: coverage
🔇 Additional comments (13)
contracts/proxy/dispatch/interfaces/IDiamondCut.sol (4)

1-3: LGTM!

Standard license and pragma declarations are appropriate.


5-10: LGTM!

The interface and enum correctly implement the EIP-2535 Diamond standard for facet management actions.


12-18: LGTM!

The struct and event correctly implement the EIP-2535 Diamond standard specifications.


20-27: LGTM!

Function signature and documentation correctly implement the EIP-2535 Diamond Cut standard.

contracts/proxy/dispatch/modules/DispatchOwnershipModule.sol (4)

1-11: LGTM!

Imports and contract declaration are appropriate. The @custom:stateless marker correctly indicates that state is managed via the Dispatch VMT.


16-19: LGTM!

The modifier correctly delegates ownership enforcement to the Dispatch VMT system using the caller's address from Context.


24-26: LGTM!

The function correctly reads ownership from the Dispatch VMT.


35-37: LGTM!

The function correctly implements ownership renunciation by setting the owner to the zero address, with appropriate access control.

contracts/proxy/dispatch/interfaces/IDiamondLoupe.sol (3)

1-9: LGTM!

Interface declaration and Facet struct correctly implement the EIP-2535 Diamond Loupe standard.


11-18: LGTM!

Functions correctly define the Diamond Loupe standard for querying facet information.


20-29: LGTM!

Functions correctly implement the Diamond Loupe standard with appropriate documentation about the zero address return for missing facets.

contracts/proxy/dispatch/modules/DiamondCutFacet.sol (1)

18-21: LGTM!

Function setup correctly retrieves the Dispatch VMT and enforces ownership before any modifications.

contracts/proxy/dispatch/modules/DiamondLoupeFacet.sol (1)

29-31: Approve facetAddress implementation. Dispatch.getFunction returns address(0) for unregistered selectors per Solidity mapping defaults and EIP-2535 semantics; no additional error handling required.

Comment on lines +22 to +36
for (uint256 i = 0; i < _diamondCut.length; ++i) {
FacetCut memory facetcut = _diamondCut[i];
for (uint256 j = 0; j < facetcut.functionSelectors.length; ++j) {
bytes4 selector = facetcut.functionSelectors[j];
address currentFacet = store.getFunction(selector);
if (facetcut.action == FacetCutAction.Add && currentFacet != address(0)) {
revert DiamondCutFacetAlreadyExist(selector);
} else if (facetcut.action == FacetCutAction.Replace && currentFacet != facetcut.facetAddress) {
revert DiamondCutFacetAlreadySet(selector);
} else if (facetcut.action == FacetCutAction.Remove && currentFacet == address(0)) {
revert DiamondCutFacetAlreadyDoesNotExit(selector);
}
store.setFunction(selector, facetcut.facetAddress);
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix critical validation and update logic errors.

The implementation has multiple critical issues:

  1. Replace validation is incomplete (lines 29-30): It only checks if the new facet differs from the current one but doesn't verify that a facet exists at all. This allows "replacing" non-existent selectors, which should fail or be treated as Add.

  2. Remove action uses wrong address (line 34): For FacetCutAction.Remove, the function should set the selector to address(0), not facetcut.facetAddress. The current code would set it to whatever address is in the struct, which is incorrect.

Apply this diff to fix both issues:

                 bytes4 selector = facetcut.functionSelectors[j];
                 address currentFacet = store.getFunction(selector);
                 if (facetcut.action == FacetCutAction.Add && currentFacet != address(0)) {
                     revert DiamondCutFacetAlreadyExist(selector);
-                } else if (facetcut.action == FacetCutAction.Replace && currentFacet != facetcut.facetAddress) {
+                } else if (facetcut.action == FacetCutAction.Replace && (currentFacet == address(0) || currentFacet == facetcut.facetAddress)) {
                     revert DiamondCutFacetAlreadySet(selector);
                 } else if (facetcut.action == FacetCutAction.Remove && currentFacet == address(0)) {
                     revert DiamondCutFacetAlreadyDoesNotExit(selector);
                 }
-                store.setFunction(selector, facetcut.facetAddress);
+                store.setFunction(
+                    selector,
+                    facetcut.action == FacetCutAction.Remove ? address(0) : facetcut.facetAddress
+                );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (uint256 i = 0; i < _diamondCut.length; ++i) {
FacetCut memory facetcut = _diamondCut[i];
for (uint256 j = 0; j < facetcut.functionSelectors.length; ++j) {
bytes4 selector = facetcut.functionSelectors[j];
address currentFacet = store.getFunction(selector);
if (facetcut.action == FacetCutAction.Add && currentFacet != address(0)) {
revert DiamondCutFacetAlreadyExist(selector);
} else if (facetcut.action == FacetCutAction.Replace && currentFacet != facetcut.facetAddress) {
revert DiamondCutFacetAlreadySet(selector);
} else if (facetcut.action == FacetCutAction.Remove && currentFacet == address(0)) {
revert DiamondCutFacetAlreadyDoesNotExit(selector);
}
store.setFunction(selector, facetcut.facetAddress);
}
}
for (uint256 i = 0; i < _diamondCut.length; ++i) {
FacetCut memory facetcut = _diamondCut[i];
for (uint256 j = 0; j < facetcut.functionSelectors.length; ++j) {
bytes4 selector = facetcut.functionSelectors[j];
address currentFacet = store.getFunction(selector);
if (facetcut.action == FacetCutAction.Add && currentFacet != address(0)) {
revert DiamondCutFacetAlreadyExist(selector);
} else if (
facetcut.action == FacetCutAction.Replace &&
(currentFacet == address(0) || currentFacet == facetcut.facetAddress)
) {
revert DiamondCutFacetAlreadySet(selector);
} else if (facetcut.action == FacetCutAction.Remove && currentFacet == address(0)) {
revert DiamondCutFacetAlreadyDoesNotExit(selector);
}
store.setFunction(
selector,
facetcut.action == FacetCutAction.Remove
? address(0)
: facetcut.facetAddress
);
}
}
🤖 Prompt for AI Agents
In contracts/proxy/dispatch/modules/DiamondCutFacet.sol around lines 22 to 36,
the Replace validation only checks equality against the new facet but doesn't
ensure a current facet exists, and the Remove branch incorrectly writes
facetcut.facetAddress instead of clearing the selector. Change the Replace logic
to first require currentFacet != address(0) (revert if missing) and also revert
if currentFacet == facetcut.facetAddress (already set), then call
store.setFunction with the new facet as before; for Remove, keep the existence
check but call store.setFunction(selector, address(0)) to clear the selector
instead of writing facetcut.facetAddress.

Comment on lines +38 to +41
emit DiamondCut(_diamondCut, _init, _calldata);

Address.functionCall(_init, _calldata);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix initialization call logic and use delegatecall.

Two critical issues:

  1. Missing zero-address check: Address.functionCall will revert if _init is address(0). According to the Diamond standard, initialization should be optional and skipped when _init is zero.

  2. Wrong call type: The function should use functionDelegateCall instead of functionCall, as specified in the IDiamondCut interface documentation (line 25: "_calldata is executed with delegatecall on _init").

Apply this diff:

         emit DiamondCut(_diamondCut, _init, _calldata);
 
-        Address.functionCall(_init, _calldata);
+        if (_init != address(0)) {
+            Address.functionDelegateCall(_init, _calldata);
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
emit DiamondCut(_diamondCut, _init, _calldata);
Address.functionCall(_init, _calldata);
}
emit DiamondCut(_diamondCut, _init, _calldata);
if (_init != address(0)) {
Address.functionDelegateCall(_init, _calldata);
}
🤖 Prompt for AI Agents
In contracts/proxy/dispatch/modules/DiamondCutFacet.sol around lines 38 to 41,
the initialization call is incorrect: it currently always invokes
Address.functionCall which reverts on address(0) and uses the wrong call type;
change the logic to skip any call when _init == address(0) and, when _init is
non-zero, invoke Address.functionDelegateCall(_init, _calldata) instead (also
validate that _calldata is non-empty when performing the delegatecall to avoid
no-op calls).

Comment on lines +13 to +16
function facets() public view override returns (Facet[] memory) {
this;
revert("This implementation doesnt keep an index, use an offchain index instead");
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix spelling error in revert message.

The revert message contains "doesnt" which should be "doesn't" or "does not".

-        revert("This implementation doesnt keep an index, use an offchain index instead");
+        revert("This implementation doesn't keep an index, use an offchain index instead");

Based on static analysis hints.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function facets() public view override returns (Facet[] memory) {
this;
revert("This implementation doesnt keep an index, use an offchain index instead");
}
function facets() public view override returns (Facet[] memory) {
this;
revert("This implementation doesn't keep an index, use an offchain index instead");
}
🧰 Tools
🪛 GitHub Check: codespell

[failure] 15-15:
doesnt ==> doesn't, does not

🤖 Prompt for AI Agents
In contracts/proxy/dispatch/modules/DiamondLoupeFacet.sol around lines 13 to 16,
the revert message uses the misspelled contraction "doesnt"; update the revert
string to use proper spelling such as "doesn't" or "does not" (e.g., change
"This implementation doesnt keep an index, use an offchain index instead" to
"This implementation doesn't keep an index, use an offchain index instead" or
the equivalent with "does not").

Comment on lines +18 to +22
function facetFunctionSelectors(address _facet) public view override returns (bytes4[] memory) {
this;
_facet;
revert("This implementation doesnt keep an index, use an offchain index instead");
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix spelling error and remove unnecessary statements.

The revert message contains "doesnt" which should be "doesn't" or "does not". Additionally, the standalone this; and _facet; statements can be removed.

     function facetFunctionSelectors(address _facet) public view override returns (bytes4[] memory) {
-        this;
-        _facet;
-        revert("This implementation doesnt keep an index, use an offchain index instead");
+        revert("This implementation doesn't keep an index, use an offchain index instead");
     }

Based on static analysis hints.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function facetFunctionSelectors(address _facet) public view override returns (bytes4[] memory) {
this;
_facet;
revert("This implementation doesnt keep an index, use an offchain index instead");
}
function facetFunctionSelectors(address _facet) public view override returns (bytes4[] memory) {
revert("This implementation doesn't keep an index, use an offchain index instead");
}
🧰 Tools
🪛 GitHub Check: codespell

[failure] 21-21:
doesnt ==> doesn't, does not

🤖 Prompt for AI Agents
In contracts/proxy/dispatch/modules/DiamondLoupeFacet.sol around lines 18 to 22,
remove the unnecessary standalone statements `this;` and `_facet;` and update
the revert message to correct spelling/grammar (e.g., change "This
implementation doesnt keep an index, use an offchain index instead" to "This
implementation does not keep an index; use an off-chain index instead" or "This
implementation doesn't keep an index; use an off-chain index instead").

Comment on lines +24 to +27
function facetAddresses() public view override returns (address[] memory) {
this;
revert("This implementation doesnt keep an index, use an offchain index instead");
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix spelling error and remove unnecessary statement.

The revert message contains "doesnt" which should be "doesn't" or "does not". Additionally, the standalone this; statement can be removed.

     function facetAddresses() public view override returns (address[] memory) {
-        this;
-        revert("This implementation doesnt keep an index, use an offchain index instead");
+        revert("This implementation doesn't keep an index, use an offchain index instead");
     }

Based on static analysis hints.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function facetAddresses() public view override returns (address[] memory) {
this;
revert("This implementation doesnt keep an index, use an offchain index instead");
}
function facetAddresses() public view override returns (address[] memory) {
revert("This implementation doesn't keep an index, use an offchain index instead");
}
🧰 Tools
🪛 GitHub Check: codespell

[failure] 26-26:
doesnt ==> doesn't, does not

🤖 Prompt for AI Agents
In contracts/proxy/dispatch/modules/DiamondLoupeFacet.sol around lines 24 to 27,
remove the unnecessary standalone "this;" statement and fix the revert string
spelling and formatting: replace "doesnt" with "does not" (or "doesn't") and
change "offchain" to "off-chain" so the revert reads e.g. "This implementation
does not keep an index; use an off-chain index instead."

Comment on lines +43 to +46
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), Ownable.OwnableInvalidOwner(newOwner));
Dispatch.instance().setOwner(newOwner);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix incorrect error syntax.

Line 44 uses invalid syntax. Custom errors cannot be used as the second argument to require. Use revert with the custom error instead.

Apply this diff:

-        require(newOwner != address(0), Ownable.OwnableInvalidOwner(newOwner));
+        if (newOwner == address(0)) revert Ownable.OwnableInvalidOwner(newOwner);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), Ownable.OwnableInvalidOwner(newOwner));
Dispatch.instance().setOwner(newOwner);
}
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) revert Ownable.OwnableInvalidOwner(newOwner);
Dispatch.instance().setOwner(newOwner);
}
🤖 Prompt for AI Agents
In contracts/proxy/dispatch/modules/DispatchOwnershipModule.sol around lines
43–46, the require call uses a custom error as its second argument which is
invalid; replace the require with an explicit conditional check for the zero
address and, when detected, revert using the custom Ownable error (i.e., use an
if that checks newOwner == address(0) and call revert
Ownable.OwnableInvalidOwner(newOwner)); keep the rest of the function logic
(Dispatch.instance().setOwner(newOwner); and onlyOwner modifier) unchanged.

Comment on lines +42 to +44
function enforceOwner(VMT storage store, address account) internal view {
require(getOwner(store) == account, Ownable.OwnableUnauthorizedAccount(account));
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix custom error handling in enforceOwner.

require does not accept custom errors as its second argument, so require(..., Ownable.OwnableUnauthorizedAccount(account)) fails to compile. Wrap the check in an if and revert explicitly with the custom error instead.

-        require(getOwner(store) == account, Ownable.OwnableUnauthorizedAccount(account));
+        if (getOwner(store) != account) {
+            revert Ownable.OwnableUnauthorizedAccount(account);
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function enforceOwner(VMT storage store, address account) internal view {
require(getOwner(store) == account, Ownable.OwnableUnauthorizedAccount(account));
}
function enforceOwner(VMT storage store, address account) internal view {
if (getOwner(store) != account) {
revert Ownable.OwnableUnauthorizedAccount(account);
}
}
🤖 Prompt for AI Agents
In contracts/proxy/dispatch/utils/Dispatch.sol around lines 42-44, the require
call uses a custom error as the second argument which is invalid; replace the
require with an explicit conditional and revert using the custom error: check if
getOwner(store) != account and if so revert
Ownable.OwnableUnauthorizedAccount(account), keeping the function internal view
and otherwise doing nothing.

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