Skip to content

Conversation

Amxx
Copy link
Collaborator

@Amxx Amxx commented Aug 28, 2025

Fixes #5899

PR Checklist

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

Summary by Sourcery

Migrate ERC7786Receiver from community by introducing an abstract receiver contract, accompanying mocks, and unit tests, and update the gateway interface parameter name

New Features:

  • Introduce ERC7786Receiver abstract contract for handling cross-chain message reception
  • Add ERC7786GatewayMock contract to simulate gateway behavior for testing
  • Add ERC7786ReceiverMock contract as a testable implementation of ERC7786Receiver

Enhancements:

  • Rename MessageSent event parameter from 'receiver' to 'recipient' in IERC7786GatewaySource

Tests:

  • Add unit tests for ERC7786Receiver to verify successful message reception and unauthorized access handling

Chores:

  • Add changeset entry for ERC7786Receiver migration

Summary by CodeRabbit

  • New Features

    • Added a base ERC‑7786 cross‑chain message recipient with gateway validation, replay protection, and observable message processing.
    • Added mock gateway and mock recipient to enable local testing of cross‑chain message flows and emitted events.
  • Documentation

    • Added cross‑chain interoperability docs describing ERC‑7786 receiving behavior.
  • Tests

    • Added unit tests for message reception, event emissions, replay protection, and unauthorized-call reverts.
  • Chores

    • Recorded a minor OpenZeppelin dependency update via changeset.

@Amxx Amxx added this to the 5.5 milestone Aug 28, 2025
@Amxx Amxx requested a review from a team as a code owner August 28, 2025 14:35
Copy link

changeset-bot bot commented Aug 28, 2025

🦋 Changeset detected

Latest commit: d39c18e

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

This PR includes changesets to release 1 package
Name Type
openzeppelin-solidity Minor

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

Copy link
Contributor

sourcery-ai bot commented Aug 28, 2025

Reviewer's Guide

Migrate the community ERC-7786 receiver implementation into core by updating the interface, adding a gateway mock, defining an abstract receiver contract, providing a receiver mock, and introducing accompanying tests and a changeset.

Sequence diagram for cross-chain message delivery using ERC7786GatewayMock and ERC7786ReceiverMock

sequenceDiagram
    participant Sender
    participant ERC7786GatewayMock
    participant ERC7786ReceiverMock
    Sender->>ERC7786GatewayMock: sendMessage(recipient, payload, attributes)
    ERC7786GatewayMock->>ERC7786ReceiverMock: receiveMessage(receiveId, sender, payload)
    ERC7786ReceiverMock->>ERC7786ReceiverMock: _isKnownGateway(msg.sender)
    ERC7786ReceiverMock->>ERC7786ReceiverMock: _processMessage(gateway, receiveId, sender, payload)
    ERC7786ReceiverMock-->>ERC7786GatewayMock: return magic value
    ERC7786GatewayMock-->>Sender: return sendId
Loading

Entity relationship diagram for MessageSent event attributes

erDiagram
    MESSAGE_SENT {
        bytes32 sendId
        bytes sender
        bytes recipient
        bytes payload
        uint256 value
        bytes[] attributes
    }
    MESSAGE_SENT ||--o| ATTRIBUTES : contains
    ATTRIBUTES {
        bytes attribute
    }
Loading

Class diagram for ERC7786Receiver and related contracts

classDiagram
    class IERC7786GatewaySource {
        <<interface>>
        +supportsAttribute(bytes4 selector) bool
        +sendMessage(bytes recipient, bytes payload, bytes[] attributes) bytes32
        <<event>> MessageSent
    }
    class IERC7786Receiver {
        <<interface>>
        +receiveMessage(bytes32 receiveId, bytes sender, bytes payload) bytes4
    }
    class ERC7786GatewayMock {
        <<abstract>>
        -_lastReceiveId uint256
        +supportsAttribute(bytes4 selector) bool
        +sendMessage(bytes recipient, bytes payload, bytes[] attributes) bytes32
    }
    class ERC7786Receiver {
        <<abstract>>
        +receiveMessage(bytes32 receiveId, bytes sender, bytes payload) bytes4
        -_isKnownGateway(address instance) bool
        -_processMessage(address gateway, bytes32 receiveId, bytes sender, bytes payload)
    }
    class ERC7786ReceiverMock {
        -_gateway address
        +constructor(address gateway_)
        +_isKnownGateway(address instance) bool
        +_processMessage(address gateway, bytes32 receiveId, bytes sender, bytes payload)
        <<event>> MessageReceived
    }
    IERC7786GatewaySource <|.. ERC7786GatewayMock
    IERC7786Receiver <|.. ERC7786Receiver
    ERC7786Receiver <|-- ERC7786ReceiverMock
Loading

File-Level Changes

Change Details Files
Rename 'receiver' parameter to 'recipient' in the ERC-7786 interface
  • Update event parameter name in MessageSent from 'receiver' to 'recipient'
contracts/interfaces/draft-IERC7786.sol
Add ERC7786GatewayMock implementing the gateway source logic
  • Enforce unsupported attributes revert
  • Parse and validate interoperable recipient address
  • Invoke IERC7786Receiver.receiveMessage and verify magic return
  • Emit standard MessageSent event
contracts/mocks/crosschain/ERC7786GatewayMock.sol
Introduce ERC7786Receiver abstract contract for cross-chain message handling
  • Implement receiveMessage with gateway validation
  • Declare abstract _isKnownGateway and _processMessage hooks
  • Return selector to signal successful processing
contracts/crosschain/ERC7786Receiver.sol
Provide ERC7786ReceiverMock and unit tests for end-to-end validation
  • Implement mock with fixed gateway and MessageReceived event
  • Write tests for successful message relay
  • Write tests for unauthorized gateway rejection
contracts/mocks/crosschain/ERC7786ReceiverMock.sol
test/crosschain/ERC7786Receiver.test.js
Add changeset entry for ERC7786Receiver migration
  • Create .changeset/silent-zebras-press.md with version bump and description
.changeset/silent-zebras-press.md

Assessment against linked issues

Issue Objective Addressed Explanation
#5899 Implement the ERC7786Receiver contract in the codebase.
#5899 Provide tests for the ERC7786Receiver contract to verify its correct behavior.
#5899 Add a changeset entry documenting the addition of ERC7786Receiver.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

coderabbitai bot commented Aug 28, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds ERC-7786 recipient base and mocks, renames IERC7786ReceiverIERC7786Recipient, updates MessageSent event parameter, introduces a mock gateway that relays messages and verifies selector, adds tests and README, and includes a changeset entry. (≤50 words)

Changes

Cohort / File(s) Summary
Interface rename & event param
contracts/interfaces/draft-IERC7786.sol
Rename IERC7786ReceiverIERC7786Recipient; rename MessageSent event parameter receiverrecipient.
Core recipient base
contracts/crosschain/ERC7786Recipient.sol
Add abstract ERC7786Recipient with receiveMessage(bytes32, bytes calldata, bytes calldata) external payable returns (bytes4), per-gateway BitMaps for replay protection, errors ERC7786RecipientInvalidGateway(address) and ERC7786RecipientMessageAlreadyProcessed(address, bytes32), and internal hooks _isAuthorizedGateway(...) and _processMessage(...).
Mocks: gateway & recipient
contracts/mocks/crosschain/ERC7786GatewayMock.sol, contracts/mocks/crosschain/ERC7786RecipientMock.sol
Add ERC7786GatewayMock (implements IERC7786GatewaySource) with sendMessage including attribute rejection, destination parsing, selector verification, errors InvalidDestination/ReceiverError, and MessageSent emission; add ERC7786RecipientMock binding a gateway and emitting MessageReceived.
Tests
test/crosschain/ERC7786Recipient.test.js
Add tests validating gateway-delivered messages, multiple receiveIds, replay rejection (ERC7786RecipientMessageAlreadyProcessed), and unauthorized caller revert (ERC7786RecipientInvalidGateway).
Docs
contracts/crosschain/README.adoc
Add/update README describing ERC-7786 cross-chain interoperability and IERC7786Recipient.
Changeset
.changeset/silent-zebras-press.md
Add changeset noting minor OpenZeppelin dependency update and new ERC-7786 recipient boilerplate.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant G as ERC7786GatewayMock
  participant R as ERC7786Recipient (base)
  participant C as Recipient (concrete/mock)

  G->>R: call receiveMessage(receiveId, sender, payload) [+value]
  Note right of R: msg.sender == gateway
  alt gateway authorized
    R->>R: check replay via per-gateway BitMap
    R->>C: _processMessage(gateway, receiveId, sender, payload)
    C-->>R: (emit MessageReceived)
    R-->>G: return IERC7786Recipient.receiveMessage.selector
    Note right of G: verify selector → emit MessageSent
  else unauthorized
    R-->>G: revert ERC7786RecipientInvalidGateway(gateway)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
Migrate ERC7786Receiver (#5899)

Poem

In tunnels of code where carrots gleam,
I hop through fixtures, selectors, and dream.
Gateways knock, IDs take flight,
A recipient listens through day and night.
Hooray — messages land safe and bright 🥕

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments

### Comment 1
<location> `contracts/mocks/crosschain/ERC7786GatewayMock.sol:54` </location>
<code_context>
+            attributes
+        );
+
+        return 0;
+    }
+}
</code_context>

<issue_to_address>
Returning a constant zero for sendId may limit traceability in tests.

Using a fixed sendId may hinder identifying individual messages during testing. Suggest returning a unique or incremented value for better traceability.

Suggested implementation:

```
        emit MessageSent(
            bytes32(_sendIdCounter + 1),
            InteroperableAddress.formatEvmV1(block.chainid, msg.sender),
            recipient,
            payload,
            msg.value,
            attributes
        );

```

```
        _sendIdCounter += 1;
        return bytes32(_sendIdCounter);
    }
}

```

```
    // Counter for unique sendId values
    uint256 private _sendIdCounter;

        // emit standard event

```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

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

🧹 Nitpick comments (7)
.changeset/silent-zebras-press.md (1)

5-5: Hyphenate “cross‑chain”.

Minor wording polish for consistency with the rest of the repo.

-`ERC7786Receiver`: Boilerplate contract for receiving ERC-7786 crosschain messages.
+`ERC7786Receiver`: Boilerplate contract for receiving ERC-7786 cross-chain messages.
contracts/interfaces/draft-IERC7786.sol (1)

12-14: Docs still mention outboxId; update to sendId.

Comment blocks reference outboxId while the event/return use sendId. Align to prevent confusion.

- * @dev Event emitted when a message is created. If `outboxId` is zero, no further processing is necessary. If
- * `outboxId` is not zero, then further (gateway specific, and non-standardized) action is required.
+ * @dev Event emitted when a message is created. If `sendId` is zero, no further processing is necessary. If
+ * `sendId` is not zero, then further (gateway specific, and non-standardized) action is required.
@@
- * @dev Endpoint for creating a new message. If the message requires further (gateway specific) processing before
- * it can be sent to the destination chain, then a non-zero `outboxId` must be returned. Otherwise, the
- * message MUST be sent and this function must return 0.
+ * @dev Endpoint for creating a new message. If the message requires further (gateway specific) processing before
+ * it can be sent to the destination chain, then a non-zero `sendId` must be returned. Otherwise, the message
+ * MUST be sent and this function must return 0x00...00.

Also applies to: 31-35

contracts/crosschain/ERC7786Receiver.sol (1)

23-27: Optional: make the function external to match the interface and save gas.

Matches IERC7786Receiver and avoids an extra ABI-to-memory copy.

-    ) public payable virtual returns (bytes4) {
+    ) external payable virtual returns (bytes4) {
contracts/mocks/crosschain/ERC7786GatewayMock.sol (4)

45-52: Confirm event parameter semantics for destination.

You emit destination as bytes32(0). If the interface expects a concrete destination identifier even on same-chain mocks, consider using a deterministic value (e.g., bytes32(block.chainid)) or document that zero is intentional.

Would you like me to adjust the event emission and tests accordingly?


14-15: Counter type choice.

_lastReceiveId can be narrower (e.g., uint64) for cheaper storage if you want to signal mock-only intent. Not required.


21-55: Optional: return a meaningful sendId for better testability.

Currently always returns zero. Returning the receive id improves correlation in tests.

-    ) public payable virtual override returns (bytes32 sendId) {
+    ) public payable virtual override returns (bytes32 sendId) {
         // attributes are not supported
         if (attributes.length > 0) {
             revert IERC7786GatewaySource.UnsupportedAttribute(bytes4(attributes[0]));
         }
         // parse recipient
         (bool success, uint256 chainid, address target) = recipient.tryParseEvmV1Calldata();
         if (!(success && chainid == block.chainid)) {
             revert InvalidDestination();
         }
         // perform call
-        bytes4 magic = IERC7786Receiver(target).receiveMessage{value: msg.value}(
-            bytes32(++_lastReceiveId),
-            InteroperableAddress.formatEvmV1(block.chainid, msg.sender),
-            payload
-        );
+        bytes32 id = bytes32(++_lastReceiveId);
+        bytes memory sender = InteroperableAddress.formatEvmV1(block.chainid, msg.sender);
+        bytes4 magic = IERC7786Receiver(target).receiveMessage{value: msg.value}(id, sender, payload);
         if (magic != IERC7786Receiver.receiveMessage.selector) {
             revert ReceiverError();
         }
         // emit standard event
         emit MessageSent(
-            bytes32(0),
-            InteroperableAddress.formatEvmV1(block.chainid, msg.sender),
+            bytes32(0),
+            sender,
             recipient,
             payload,
             msg.value,
             attributes
         );
-        return bytes32(0);
+        return id; // or keep bytes32(0) if intentional
     }

If tests rely on zero, keep the current behavior and add a brief note in NatSpec.


16-19: Minor: state mutability.

Interface says view, so keep as view. If interface ever changes to pure, this function can be tightened.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 53bb340 and 4a33948.

📒 Files selected for processing (6)
  • .changeset/silent-zebras-press.md (1 hunks)
  • contracts/crosschain/ERC7786Receiver.sol (1 hunks)
  • contracts/interfaces/draft-IERC7786.sol (1 hunks)
  • contracts/mocks/crosschain/ERC7786GatewayMock.sol (1 hunks)
  • contracts/mocks/crosschain/ERC7786ReceiverMock.sol (1 hunks)
  • test/crosschain/ERC7786Receiver.test.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
test/crosschain/ERC7786Receiver.test.js (1)
test/helpers/random.js (1)
  • generators (3-10)
🪛 LanguageTool
.changeset/silent-zebras-press.md

[grammar] ~5-~5: There might be a mistake here.
Context: ... receiving ERC-7786 crosschain messages.

(QB_NEW_EN)

⏰ 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). (9)
  • GitHub Check: Redirect rules - solidity-contracts
  • GitHub Check: tests
  • GitHub Check: tests-foundry
  • GitHub Check: slither
  • GitHub Check: coverage
  • GitHub Check: tests-upgradeable
  • GitHub Check: halmos
  • GitHub Check: Header rules - solidity-contracts
  • GitHub Check: Pages changed - solidity-contracts
🔇 Additional comments (5)
.changeset/silent-zebras-press.md (1)

2-3: Confirm semver bump for event parameter rename

  • Package name ‘openzeppelin-solidity’ matches package.json, so no update needed.
  • Renaming the event parameter (receiver → recipient) doesn’t change the ABI signature but will break generated typings (e.g., TypeChain); confirm whether a minor bump is sufficient or if this deserves a major bump.
contracts/interfaces/draft-IERC7786.sol (1)

18-18: Param rename aligns terminology, but watch downstream typings.

Changing receiverrecipient is good for clarity, but it alters the “name” field in the ABI and may break typed consumers. Ensure the release note calls this out and the changeset version is appropriate.

contracts/mocks/crosschain/ERC7786ReceiverMock.sol (1)

7-27: Mock looks correct and minimal.

Gateway whitelisting and event emission mirror the base contract’s expectations. Good for tests.

test/crosschain/ERC7786Receiver.test.js (1)

9-9: No changes required for generators.hexBytes usage
Verified in test/helpers/random.js (line 20) that generators.hexBytes is aliased to generators.bytes, so the fallback is unnecessary.

contracts/mocks/crosschain/ERC7786GatewayMock.sol (1)

8-13: Good: minimal mock surface and explicit custom errors.

Clean separation of concerns; mock keeps observable behavior tight.

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4a33948 and fdb2e77.

📒 Files selected for processing (1)
  • contracts/crosschain/README.adoc (1 hunks)
⏰ 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: halmos
  • GitHub Check: coverage

ernestognw
ernestognw previously approved these changes Aug 28, 2025
Copy link
Member

@ernestognw ernestognw left a comment

Choose a reason for hiding this comment

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

Minor comments. Nothing blocking

Copy link
Member

@ernestognw ernestognw left a comment

Choose a reason for hiding this comment

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

I just noticed that EIP-7786 refers to this contract as a "Recipient". We should update it too

@ernestognw ernestognw dismissed their stale review August 28, 2025 16:43

Consider renaming to ERC7786Recipient

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

Caution

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

⚠️ Outside diff range comments (2)
contracts/interfaces/draft-IERC7786.sol (2)

12-16: Doc references “outboxId” but the ABI exposes “sendId.”

Update wording to avoid confusion.

- * @dev Event emitted when a message is created. If `outboxId` is zero, no further processing is necessary. If
- * `outboxId` is not zero, then further (gateway specific, and non-standardized) action is required.
+ * @dev Event emitted when a message is created. If `sendId` is zero, no further processing is necessary. If
+ * `sendId` is not zero, then further (gateway specific, and non-standardized) action is required.

31-39: Clarify return semantics in sendMessage docs.

Align with the return type and event.

- * @dev Endpoint for creating a new message. If the message requires further (gateway specific) processing before
- * it can be sent to the destination chain, then a non-zero `outboxId` must be returned. Otherwise, the
- * message MUST be sent and this function must return 0.
+ * @dev Endpoint for creating a new message. If the message requires further (gateway‑specific) processing before
+ * it can be sent to the destination chain, a non‑zero `sendId` MUST be returned. Otherwise, the message MUST be
+ * sent and this function MUST return 0x00..00.
♻️ Duplicate comments (2)
contracts/crosschain/README.adoc (2)

10-12: Replace placeholder with resolvable xref and adjust heading.

-== Helpers
-
-{{IERC7786Recipient}}
+== Helper contracts
+
+- xref:api/crosschain.adoc#IERC7786Recipient[IERC7786Recipient]

6-6: Fix agreement and hyphenation in body text.

-This directory contains contracts for sending and receiving cross chain messages that follows the ERC-7786 standard.
+This directory contains contracts for sending and receiving cross-chain messages that follow the ERC-7786 standard.
🧹 Nitpick comments (7)
contracts/interfaces/draft-IERC7786.sol (1)

48-52: Use “recipient” consistently in the interface docs.

- * @dev Interface for the ERC-7786 client contract (receiver).
+ * @dev Interface for the ERC-7786 client contract (recipient).
contracts/crosschain/README.adoc (1)

8-8: Hyphenate “crosschain” and tighten wording.

-- {IERC7786Recipient}: generic ERC-7786 crosschain contract that receives messages from a trusted gateway
+- {IERC7786Recipient}: generic ERC-7786 cross-chain contract that receives messages from a trusted gateway
contracts/mocks/crosschain/ERC7786RecipientMock.sol (2)

10-10: Index key event fields for better test/dev UX.

-    event MessageReceived(address gateway, bytes32 receiveId, bytes sender, bytes payload, uint256 value);
+    event MessageReceived(address indexed gateway, bytes32 indexed receiveId, bytes sender, bytes payload, uint256 value);

8-9: Optional: add a getter for _gateway.

Improves introspection and debugging without exposing mutability.

function gateway() external view returns (address) { return _gateway; }
contracts/crosschain/ERC7786Recipient.sol (3)

33-43: Consider reentrancy posture for _processMessage

Depending on concrete implementations, _processMessage may transfer value or interact with untrusted contracts. Consider documenting reentrancy expectations here, or guarding in derived contracts (e.g., OZ ReentrancyGuard) and adding tests for reentrancy attempts via the gateway.


13-16: Nit: wording/grammar in NatSpec

Minor clarity and US spelling.

- * * {_isKnownGateway}, an internal getter used to verify whether an address is recognised by the contract as a valid
- * ERC-7786 destination gateway. One or multiple gateway can be supported. Note that any malicious address for which
+ * * {_isKnownGateway}, an internal getter used to verify whether an address is recognized by the contract as a valid
+ * ERC-7786 destination gateway. One or multiple gateways can be supported. Note that any malicious address for which

34-34: Nit: parameter name

Rename instance to gateway for consistency with usage elsewhere in this file.

-    function _isKnownGateway(address instance) internal view virtual returns (bool);
+    function _isKnownGateway(address gateway) internal view virtual returns (bool);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3651638 and d3e2223.

📒 Files selected for processing (7)
  • .changeset/silent-zebras-press.md (1 hunks)
  • contracts/crosschain/ERC7786Recipient.sol (1 hunks)
  • contracts/crosschain/README.adoc (1 hunks)
  • contracts/interfaces/draft-IERC7786.sol (2 hunks)
  • contracts/mocks/crosschain/ERC7786GatewayMock.sol (1 hunks)
  • contracts/mocks/crosschain/ERC7786RecipientMock.sol (1 hunks)
  • test/crosschain/ERC7786Recipient.test.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • contracts/mocks/crosschain/ERC7786GatewayMock.sol
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-28T15:48:30.684Z
Learnt from: ernestognw
PR: OpenZeppelin/openzeppelin-contracts#5904
File: contracts/crosschain/README.adoc:1-1
Timestamp: 2025-08-28T15:48:30.684Z
Learning: ernestognw prefers "Cross chain" without hyphenation rather than "Cross-chain" in documentation titles.

Applied to files:

  • contracts/crosschain/README.adoc
📚 Learning: 2025-08-28T16:58:18.790Z
Learnt from: Amxx
PR: OpenZeppelin/openzeppelin-contracts#5904
File: contracts/mocks/crosschain/ERC7786GatewayMock.sol:27-31
Timestamp: 2025-08-28T16:58:18.790Z
Learning: In the OpenZeppelin ERC7786 implementation, the `UnsupportedAttribute(bytes4 selector)` error is defined in the `IERC7786GatewaySource` interface, making it directly accessible to inheriting contracts without needing interface qualification.

Applied to files:

  • contracts/crosschain/ERC7786Recipient.sol
🧬 Code graph analysis (1)
test/crosschain/ERC7786Recipient.test.js (1)
test/helpers/random.js (1)
  • generators (3-10)
🪛 LanguageTool
.changeset/silent-zebras-press.md

[grammar] ~5-~5: There might be a mistake here.
Context: ...ic crosschain message receiver contract.

(QB_NEW_EN)

⏰ 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). (5)
  • GitHub Check: halmos
  • GitHub Check: coverage
  • GitHub Check: tests-upgradeable
  • GitHub Check: tests-foundry
  • GitHub Check: tests
🔇 Additional comments (4)
contracts/interfaces/draft-IERC7786.sol (1)

18-22: Event param rename LGTM.

“receiver” → “recipient” matches the rest of the PR.

contracts/crosschain/README.adoc (1)

1-1: Title style acknowledged.

Keeping “Cross chain” (no hyphen) per documented preference.

test/crosschain/ERC7786Recipient.test.js (1)

35-35: Second arg to ethers.toBeHex must be a number, not BigInt.

-      .withArgs(this.gateway, ethers.toBeHex(1n, 32n), this.toErc7930(this.sender), payload, value);
+      .withArgs(this.gateway, ethers.toBeHex(1n, 32), this.toErc7930(this.sender), payload, value);

Likely an incorrect or invalid review comment.

contracts/crosschain/ERC7786Recipient.sol (1)

28-28: Fix: invalid use of require with a custom error

require cannot take a custom error as its second argument. Use an explicit revert.

-        require(_isKnownGateway(msg.sender), ERC7786RecipientInvalidGateway(msg.sender));
+        if (!_isKnownGateway(msg.sender)) {
+            revert ERC7786RecipientInvalidGateway(msg.sender);
+        }
⛔ Skipped due to learnings
Learnt from: ernestognw
PR: OpenZeppelin/openzeppelin-contracts#5904
File: contracts/crosschain/ERC7786Receiver.sol:28-31
Timestamp: 2025-08-28T15:48:27.419Z
Learning: Since Solidity 0.8.26, the require() function supports a new overload require(bool, Error) that allows using custom errors as the second parameter instead of just string messages. This feature was initially available only in the via-ir pipeline in 0.8.26, but became available in the legacy pipeline starting from Solidity 0.8.27.
Learnt from: Amxx
PR: OpenZeppelin/openzeppelin-contracts#5904
File: contracts/mocks/crosschain/ERC7786GatewayMock.sol:27-31
Timestamp: 2025-08-28T16:58:18.790Z
Learning: In the OpenZeppelin ERC7786 implementation, the `UnsupportedAttribute(bytes4 selector)` error is defined in the `IERC7786GatewaySource` interface, making it directly accessible to inheriting contracts without needing interface qualification.

@Amxx Amxx changed the title Migrate ERC7786Receiver from community Migrate ERC7786Recipient from community Sep 1, 2025
@Amxx Amxx requested review from ernestognw and luiz-lvj September 3, 2025 07:07
}

/// @dev Virtual getter that returns whether an address is a valid ERC-7786 gateway.
function _isKnownGateway(address instance) internal view virtual returns (bool);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

While this function supports multiple gateways, it doesn't allow to selectivelly disable gateways based on the sender origin chain (or similar). For more complex receiver, this won't work.

Copy link

Choose a reason for hiding this comment

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

I think it makes sense but I don't see which parameter could be added to enhance that. If developers want to do it, they're still able to in the _processMessagefunction.

Copy link
Contributor

Choose a reason for hiding this comment

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

If developers want to do it, they're still able to in the _processMessagefunction.

I think at this point they should just implement the IERC7786Recipient interface separately.

Unless we think this will be a common requirement?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Unless we think this will be a common requirement?

At first I didn't, but all the recent development I've been doing had some kind of sender restriction, and a gateway restriction that depends on the sender.

Therefore, I'd consider replacing _isKnownGateway(address instance) with something like _isAuthorizedGateway(address instance, bytes memory sender). Devs can still ignore the sender if they want to just whitelist the gateway and accept any sender for it. Or they can extract the "chain" part of the sender to check where the message comes from.

luiz-lvj
luiz-lvj previously approved these changes Sep 3, 2025
}

/// @dev Virtual getter that returns whether an address is a valid ERC-7786 gateway.
function _isKnownGateway(address instance) internal view virtual returns (bool);
Copy link

Choose a reason for hiding this comment

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

I think it makes sense but I don't see which parameter could be added to enhance that. If developers want to do it, they're still able to in the _processMessagefunction.

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

♻️ Duplicate comments (1)
contracts/crosschain/ERC7786Recipient.sol (1)

22-31: Fix: add missing override and use custom error with revert (compile blockers)

The function implements the interface but lacks override. Also, require(..., ERC7786RecipientInvalidGateway(...)) is invalid Solidity; use revert with the custom error.

-    /// @inheritdoc IERC7786Recipient
-    function receiveMessage(
+    /// @inheritdoc IERC7786Recipient
+    function receiveMessage(
         bytes32 receiveId,
         bytes calldata sender, // Binary Interoperable Address
         bytes calldata payload
-    ) external payable returns (bytes4) {
-        require(_isAuthorizedGateway(msg.sender, sender), ERC7786RecipientInvalidGateway(msg.sender));
+    ) external payable override returns (bytes4) {
+        if (!_isAuthorizedGateway(msg.sender, sender)) {
+            revert ERC7786RecipientInvalidGateway(msg.sender);
+        }
         _processMessage(msg.sender, receiveId, sender, payload);
         return IERC7786Recipient.receiveMessage.selector;
     }
🧹 Nitpick comments (3)
contracts/crosschain/ERC7786Recipient.sol (3)

33-35: Gas/consistency: prefer calldata and consistent param naming

Avoid copying sender to memory in the auth hook and align the param name with _processMessage.

-    /// @dev Virtual getter that returns whether an address is a valid ERC-7786 gateway for a given sender.
-    function _isAuthorizedGateway(address instance, bytes memory sender) internal view virtual returns (bool);
+    /// @dev Virtual getter that returns whether an address is a valid ERC-7786 gateway for a given sender.
+    function _isAuthorizedGateway(address gateway, bytes calldata sender) internal view virtual returns (bool);

37-42: Optional: drop gateway param from _processMessage (smaller stack, simpler API)

You can read msg.sender inside overrides to get the gateway. This reduces stack usage and nudges devs to authorize based on msg.sender.

-    function _processMessage(
-        address gateway,
-        bytes32 receiveId,
-        bytes calldata sender,
-        bytes calldata payload
-    ) internal virtual;
+    function _processMessage(
+        bytes32 receiveId,
+        bytes calldata sender,
+        bytes calldata payload
+    ) internal virtual;

If you adopt this, update all overrides and the call site:

-        _processMessage(msg.sender, receiveId, sender, payload);
+        _processMessage(receiveId, sender, payload);

7-18: Nit: terminology and grammar in NatSpec

Align terminology with “Recipient” and fix minor grammar.

- * @dev Base implementation of an ERC-7786 compliant cross-chain message receiver.
+ * @dev Base implementation of an ERC-7786 compliant cross-chain message recipient.
@@
- * * {_isAuthorizedGateway}, an internal getter used to verify whether an address is recognised by the contract as a
- * valid ERC-7786 destination gateway. One or multiple gateway can be supported. Note that any malicious address for
+ * * {_isAuthorizedGateway}, an internal getter used to verify whether an address is recognized by the contract as a
+ * valid ERC-7786 destination gateway. One or multiple gateways can be supported. Note that any malicious address for
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9b015d3 and c0c421c.

📒 Files selected for processing (2)
  • contracts/crosschain/ERC7786Recipient.sol (1 hunks)
  • contracts/mocks/crosschain/ERC7786RecipientMock.sol (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • contracts/mocks/crosschain/ERC7786RecipientMock.sol
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: Amxx
PR: OpenZeppelin/openzeppelin-contracts#5904
File: contracts/mocks/crosschain/ERC7786RecipientMock.sol:12-14
Timestamp: 2025-08-29T13:16:08.640Z
Learning: In OpenZeppelin contracts, mock contracts (like ERC7786RecipientMock) don't require input validation such as zero-address checks in constructors, as they are only used for testing purposes in controlled environments.
📚 Learning: 2025-08-29T13:16:08.640Z
Learnt from: Amxx
PR: OpenZeppelin/openzeppelin-contracts#5904
File: contracts/mocks/crosschain/ERC7786RecipientMock.sol:12-14
Timestamp: 2025-08-29T13:16:08.640Z
Learning: In OpenZeppelin contracts, mock contracts (like ERC7786RecipientMock) don't require input validation such as zero-address checks in constructors, as they are only used for testing purposes in controlled environments.

Applied to files:

  • contracts/crosschain/ERC7786Recipient.sol
📚 Learning: 2025-08-29T13:17:07.068Z
Learnt from: Amxx
PR: OpenZeppelin/openzeppelin-contracts#5904
File: contracts/crosschain/ERC7786Recipient.sol:22-31
Timestamp: 2025-08-29T13:17:07.068Z
Learning: In OpenZeppelin contracts, virtual functions that implement interfaces should use `public` visibility instead of `external`, even for calldata-heavy functions. This is because external functions cannot be called with `super` in derived contracts, which would limit extensibility. OpenZeppelin has strong guidelines to keep all public-facing virtual functions as `public` to maintain inheritance flexibility.

Applied to files:

  • contracts/crosschain/ERC7786Recipient.sol
⏰ 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). (6)
  • GitHub Check: tests
  • GitHub Check: tests-foundry
  • GitHub Check: coverage
  • GitHub Check: halmos
  • GitHub Check: tests-upgradeable
  • GitHub Check: slither

@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.27;
Copy link

@coderabbitai coderabbitai bot Sep 4, 2025

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Confirm pragma aligns with repo toolchain

Ensure ^0.8.27 matches the repo’s configured compiler(s) to avoid CI failures.


🏁 Script executed:

#!/bin/bash
# List distinct pragmas and common config files to confirm alignment.
rg -nP '^\s*pragma\s+solidity\s+[^;]+;' -g '!**/node_modules/**' -S | awk -F: '{print $3}' | sort -u
fd -a 'foundry.toml' 'hardhat.config.*' 'remappings.txt' 'package.json' 2>/dev/null

Length of output: 498


🏁 Script executed:

#!/bin/bash
# Check the Solidity compiler version in Foundry config
rg -n 'solidity_version' foundry.toml || echo "foundry.toml: no solidity_version field found"
# Display relevant lines around version settings in foundry.toml
sed -n '1,50p' foundry.toml | rg -n 'version'
# Check the Solidity compiler settings in Hardhat config
rg -nP 'solidity\s*:\s*\"[0-9]+\.[0-9]+\.[0-9]+\"' hardhat.config.* || echo "hardhat.config.*: no explicit solidity version found"

Length of output: 419


Align Solidity compiler version across tools

  • foundry.toml sets solc_version = '0.8.27', matching the pragma ^0.8.27 in contracts/crosschain/ERC7786Recipient.sol.
  • hardhat.config.js lacks a solidity field; add solidity: "0.8.27" (or a matching semver range) under module.exports to prevent mismatches in Hardhat tasks/CI.
🤖 Prompt for AI Agents
In contracts/crosschain/ERC7786Recipient.sol around line 3 and project configs,
the Solidity pragma is ^0.8.27 but hardhat.config.js lacks a solidity setting;
open hardhat.config.js and add a solidity entry under module.exports (e.g.,
solidity: "0.8.27" or solidity: { compilers: [{ version: "0.8.27" }] }) so
Hardhat uses the same compiler version/range as foundry.toml and the contract
pragma; ensure the version/string you add matches the semver range you intend to
support.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Our hardhat.config.js does have a solidity field (line 76 to 87). Is uses a argv.compile value from yargs. The default is 0.8.27 (line 21)

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

@Amxx, thank you for the correction! You're absolutely right. I can see that hardhat.config.js does indeed have a sophisticated configuration:

  • Line 21 sets the default compile version to '0.8.27' via yargs: compile: { default: '0.8.27', type: 'string', describe: 'compile solidity version' }
  • Lines 76-87 contain the solidity configuration block that uses argv.compile to dynamically set the compiler version

I apologize for the oversight in my initial analysis. My grep patterns were looking for static hardcoded solidity versions, but your project uses a more flexible approach with command-line argument parsing through yargs. The pragma ^0.8.27 in the contract is properly aligned with your toolchain configuration.


🏁 Script executed:

#!/bin/bash
# Examine the hardhat.config.js file to verify the user's claims
cat hardhat.config.js | head -n 100 | nl

Length of output: 135


🏁 Script executed:

#!/bin/bash
# Examine the hardhat.config.js file with line numbers
cat hardhat.config.js | grep -n "."

# Also look specifically at the lines the user mentioned
echo "=== Lines around 21 ==="
sed -n '18,25p' hardhat.config.js

echo "=== Lines 76-87 ==="
sed -n '76,87p' hardhat.config.js

Length of output: 4120


@Amxx, you are absolutely correct, and thank you for the correction! I can now see the full picture in hardhat.config.js:

  • Line 21 indeed sets default: '0.8.27' for the compiler configuration via yargs
  • Lines 76-87 contain the solidity configuration block that uses argv.compiler (line 77: version: argv.compiler)
  • This creates a sophisticated, flexible configuration system where the Solidity compiler version can be specified via command-line arguments while defaulting to 0.8.27

My initial analysis was flawed because I was looking for static hardcoded solidity version declarations and completely missed this dynamic yargs-based configuration approach. The pragma ^0.8.27 in the ERC7786Recipient contract is perfectly aligned with your toolchain setup.

I apologize for the oversight and appreciate you taking the time to correct my analysis with the specific line references.


✏️ Learnings added
Learnt from: Amxx
PR: OpenZeppelin/openzeppelin-contracts#5904
File: contracts/crosschain/ERC7786Recipient.sol:3-3
Timestamp: 2025-09-04T09:13:21.248Z
Learning: In OpenZeppelin contracts, hardhat.config.js uses yargs to configure the Solidity compiler version dynamically via command line arguments, with a default of '0.8.27' set on line 21 and the solidity configuration using argv.compile around lines 76-87, rather than hardcoded version strings.

Learnt from: Amxx
PR: OpenZeppelin/openzeppelin-contracts#5904
File: contracts/crosschain/ERC7786Recipient.sol:3-3
Timestamp: 2025-09-04T09:13:21.248Z
Learning: In OpenZeppelin contracts, hardhat.config.js uses a sophisticated yargs-based configuration where the Solidity compiler version is set via argv.compiler (line 77) with a default of '0.8.27' defined in the yargs options (line 21), allowing flexible command-line overrides while maintaining a consistent default.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: Amxx
PR: OpenZeppelin/openzeppelin-contracts#5904
File: contracts/mocks/crosschain/ERC7786RecipientMock.sol:12-14
Timestamp: 2025-08-29T13:16:08.640Z
Learning: In OpenZeppelin contracts, mock contracts (like ERC7786RecipientMock) don't require input validation such as zero-address checks in constructors, as they are only used for testing purposes in controlled environments.

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

♻️ Duplicate comments (3)
contracts/crosschain/ERC7786Recipient.sol (3)

31-36: Consider public virtual override to allow super in derived contracts.

Per OZ guideline for extensible bases, prefer public visibility for virtual entrypoints so children can super.receiveMessage(...). Keep external only if you intentionally want to prevent overrides.

-    ) external payable override returns (bytes4) {
+    ) public payable virtual override returns (bytes4) {

53-58: Optional: drop the gateway param and read msg.sender inside _processMessage.

This reduces stack usage and matches how the gateway is already sourced. Apply only if you don’t need to pass a different address for testing/mocking.

-    function _processMessage(
-        address gateway,
-        bytes32 receiveId,
-        bytes calldata sender,
-        bytes calldata payload
-    ) internal virtual;
+    function _processMessage(
+        bytes32 receiveId,
+        bytes calldata sender,
+        bytes calldata payload
+    ) internal virtual;

31-36: Add missing override on the interface implementation (compile error).

This function implements IERC7786Recipient but lacks the override specifier, which is required by Solidity.

-    ) external payable returns (bytes4) {
+    ) external payable override returns (bytes4) {
🧹 Nitpick comments (2)
contracts/crosschain/ERC7786Recipient.sol (2)

15-16: Fix NatSpec typos/wording.

Minor grammar and typo fixes improve docs clarity (“gateways”, “meaning”, “recognized”).

- * valid ERC-7786 destination gateway. One or multiple gateway can be supported. Note that any malicious address for
- * which this function returns true would be able to impersonate any account on any other chain sending any message.
+ * valid ERC-7786 destination gateway. One or multiple gateways can be supported. Note that any malicious address for
+ * which this function returns true would be able to impersonate any account on any other chain, sending any message.
@@
- * This contract implements replay protection, manning that if two messages are received from the same gateway with the
+ * This contract implements replay protection, meaning that if two messages are received from the same gateway with the

Also applies to: 20-22


49-51: Rename param instancegateway for consistency.

Elsewhere you use “gateway”; aligning names improves readability.

-    function _isAuthorizedGateway(address instance, bytes calldata sender) internal view virtual returns (bool);
+    function _isAuthorizedGateway(address gateway, bytes calldata sender) internal view virtual returns (bool);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c938d68 and d39c18e.

📒 Files selected for processing (3)
  • contracts/crosschain/ERC7786Recipient.sol (1 hunks)
  • contracts/mocks/crosschain/ERC7786RecipientMock.sol (1 hunks)
  • test/crosschain/ERC7786Recipient.test.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • contracts/mocks/crosschain/ERC7786RecipientMock.sol
  • test/crosschain/ERC7786Recipient.test.js
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: Amxx
PR: OpenZeppelin/openzeppelin-contracts#5904
File: contracts/mocks/crosschain/ERC7786RecipientMock.sol:12-14
Timestamp: 2025-08-29T13:16:08.640Z
Learning: In OpenZeppelin contracts, mock contracts (like ERC7786RecipientMock) don't require input validation such as zero-address checks in constructors, as they are only used for testing purposes in controlled environments.
📚 Learning: 2025-08-29T13:16:08.640Z
Learnt from: Amxx
PR: OpenZeppelin/openzeppelin-contracts#5904
File: contracts/mocks/crosschain/ERC7786RecipientMock.sol:12-14
Timestamp: 2025-08-29T13:16:08.640Z
Learning: In OpenZeppelin contracts, mock contracts (like ERC7786RecipientMock) don't require input validation such as zero-address checks in constructors, as they are only used for testing purposes in controlled environments.

Applied to files:

  • contracts/crosschain/ERC7786Recipient.sol
📚 Learning: 2025-08-29T13:17:07.068Z
Learnt from: Amxx
PR: OpenZeppelin/openzeppelin-contracts#5904
File: contracts/crosschain/ERC7786Recipient.sol:22-31
Timestamp: 2025-08-29T13:17:07.068Z
Learning: In OpenZeppelin contracts, virtual functions that implement interfaces should use `public` visibility instead of `external`, even for calldata-heavy functions. This is because external functions cannot be called with `super` in derived contracts, which would limit extensibility. OpenZeppelin has strong guidelines to keep all public-facing virtual functions as `public` to maintain inheritance flexibility.

Applied to files:

  • contracts/crosschain/ERC7786Recipient.sol
⏰ 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). (6)
  • GitHub Check: tests-foundry
  • GitHub Check: tests-upgradeable
  • GitHub Check: coverage
  • GitHub Check: halmos
  • GitHub Check: slither
  • GitHub Check: tests
🔇 Additional comments (3)
contracts/crosschain/ERC7786Recipient.sol (3)

42-46: Replay-protection order is correct.

Marking the receiveId as processed before delegating to _processMessage prevents reentrant double-processing and will roll back on revert. LGTM.


26-30: Errors are well-scoped and informative.

Clear, specific custom errors for invalid gateway and replay. Nice.


37-41: Use custom errors with revert, not as require messages.

require only accepts a boolean and a string message. Passing a custom error to require won’t compile. Use if (!cond) revert Error(...).

-        require(_isAuthorizedGateway(msg.sender, sender), ERC7786RecipientInvalidGateway(msg.sender));
-        require(
-            !_received[msg.sender].get(uint256(receiveId)),
-            ERC7786RecipientMessageAlreadyProcessed(msg.sender, receiveId)
-        );
+        if (!_isAuthorizedGateway(msg.sender, sender)) {
+            revert ERC7786RecipientInvalidGateway(msg.sender);
+        }
+        if (_received[msg.sender].get(uint256(receiveId))) {
+            revert ERC7786RecipientMessageAlreadyProcessed(msg.sender, receiveId);
+        }
⛔ Skipped due to learnings
Learnt from: ernestognw
PR: OpenZeppelin/openzeppelin-contracts#5904
File: contracts/crosschain/ERC7786Receiver.sol:28-31
Timestamp: 2025-08-28T15:48:27.477Z
Learning: Since Solidity 0.8.26, the require() function supports a new overload require(bool, Error) that allows using custom errors as the second parameter instead of just string messages. This feature was initially available only in the via-ir pipeline in 0.8.26, but became available in the legacy pipeline starting from Solidity 0.8.27.

@Amxx Amxx mentioned this pull request Sep 4, 2025
3 tasks

mapping(address gateway => BitMaps.BitMap) private _received;

error ERC7786RecipientInvalidGateway(address gateway);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Should we do

Suggested change
error ERC7786RecipientInvalidGateway(address gateway);
error ERC7786RecipientUnauthorizedGateway(address gateway, bytes sender);

Copy link

@luiz-lvj luiz-lvj Sep 4, 2025

Choose a reason for hiding this comment

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

Since the function was changed to _isAuthorizedGateway, I'd change

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.

Migrate ERC7786Receiver
4 participants