⚠️ DEPRECATED – DO NOT USEThis repository is no longer maintained and is deprecated.
It may contain outdated, insecure, or vulnerable code and should not be used in production or as a dependency in any project.
The repository is retained solely for historical reference. No support, updates, or security patches will be provided.
-
The Interoperability Mock System serves as a sandbox for emulating cross-chain interactions. The system replicates how messages move across chains and verifies the integrity of this communication through permissioned relayers, which are trusted entities managed by the contract owner(s).
The following contracts are not intended for use in production environments. Please exercise caution and conduct thorough due diligence before utilizing them.
- Install Foundry by following the instructions from their repository.
- Copy the
.env.examplefile to.envand fill in the variables. - Install the dependencies by running:
yarn install. In case there is an error with the commands, runfoundryupand try them again.
The default way to build the code is suboptimal but fast, you can run it via:
yarn buildIn order to build a more optimized code (via IR), run:
yarn build:optimizedUnit tests should be isolated from any externalities, while Integration usually run in a fork of the blockchain. In this boilerplate you will find example of both.
In order to run both unit and integration tests, run:
yarn testIn order to just run unit tests, run:
yarn test:unitIn order to run unit tests and run way more fuzzing than usual (5x), run:
yarn test:unit:deepIn order to just run integration tests, run:
yarn test:integrationIn order to check your current code coverage, run:
yarn coverageNote: In order to deploy contracts with the same address in every chain is important to use the same SALT and DEPLOYER for every deployment.
yarn deploy:optimism:sepoliayarn deploy:unichain:sepoliaCrossL2Inbox: https://sepolia-optimism.etherscan.io/address/0x20fce9a9c640bd99a0edb14613356cf8d54c2bb3
L2ToL2CrossDomainMessenger: https://sepolia-optimism.etherscan.io/address/0x16cb4762e5f36f0cf5d8503a0771f048c9f8c460
CrossL2Inbox: https://unichain-sepolia.blockscout.com/address/0x20FcE9A9c640bd99a0eDB14613356cf8D54C2BB3
L2ToL2CrossDomainMessenger: https://unichain-sepolia.blockscout.com/address/0x16cB4762e5f36f0cF5D8503A0771f048C9f8c460
CrossL2Inbox: https://sepolia.explorer.mode.network/address/0x20FcE9A9c640bd99a0eDB14613356cf8D54C2BB3
L2ToL2CrossDomainMessenger: https://sepolia.explorer.mode.network/address/0x16cB4762e5f36f0cF5D8503A0771f048C9f8c460
Contracts are UUPS proxies and can be upgraded calling the following function from Owner's account:
function upgradeToAndCall(address newImplementation, bytes memory data)With sponsored relay
sequenceDiagram
actor A1 as Alice
participant P1 as OP CDM
participant P2 as Hash Relayer
participant P3 as Message Relayer
participant P4 as Uni CrossL2Inbox
participant P5 as Uni CDM
actor A2 as Bob
A1 ->> P1: sendMessage()
P1 --> P2: SentMessage()
P2 ->> P4: postValidHash()
P3 ->> P5: relayMessage()
P5 ->> P4: validateMessage()
P5 ->> A2: call()
Without sponsored relay
sequenceDiagram
actor A1 as Alice
participant P1 as OP CDM
participant P2 as Hash Relayer
participant P4 as Uni CrossL2Inbox
participant P5 as Uni CDM
actor A2 as Bob
A1 ->> P1: sendMessage()
P1 --> P2: SentMessage()
P2 ->> P4: postValidHash()
A1 ->> P5: relayMessage()
P5 ->> P4: validateMessage()
P5 ->> A2: call()
As illustrated in the diagram, four core components are required:
L2ToL2CrossDomainMessengeremits aSentMessageevent.Hash Relayerlistens for theSentMessageevent and posts ahashonCrossL2Inbox.Message Relayeror an EOA callsrelayMessage()onL2ToL2CrossDomainMessenger.CrossL2Inboxis used byL2ToL2CrossDomainMessengerto verify that the message is valid.
Sending Message
On L2ToL2CrossDomainMessanger from Origin Chain:
- User calls
sendMessage(uint256 _destination, address _target, bytes calldata _message) external returns (bytes32 hash_)1.1._destinationis the ID of the destination chain. 1.2._targetis the address that will receive the_message. 1.3._messageencoded call. - Contract emits
SentMessage(_destination, _target, nonce, **msg.sender**, _message);
Posting Hash on Destination
With a service listening at L2ToL2CrossDomainMessenger events from origin chain, when a SentMessage event is emitted this service should use the log topics, log data and blockchain data to generate a hash.
-
Generate hash
Message Hash: is composed by log topics encoded, log data encoded and the
keccak256of theSentMessagesignature.keccak256( abi.encodePacked( keccak256('SentMessage(uint256,address,uint256,address,bytes)'), abi.encode(_destination, _target, _nonce), abi.encode(_sender, _message) ) );
> cast keccak256 'SentMessage(uint256,address,uint256,address,bytes)' 0x382409ac69001e11931a28435afef442cbfd20d9891907e8fa373ba7d351f320
Identifier: is a struct that uses blockchain data.
originin this case the address of theL2ToL2CrossDomainMessenger.struct Identifier { address origin; uint256 blockNumber; uint256 logIndex; uint256 timestamp; uint256 chainId; }
Hash: this is the hash that will be posted on the destination chain.
bytes32 _hash = keccak256(abi.encode(_id, _msgHash));
-
Call
postValidHash()inCrossL2Inboxusing the relayer’s address that has been previously enabled inCrossL2Inbox.
Relaying Message
Here we have two options:
- With sponsored relay, once the hash is posted, a Message Relayer that is listening
RegisteredHash()events can callrelayMessage(Identifier calldata _id, bytes calldata _sentMessage)function on the destination chainL2ToL2CrossDomainMessengerusing the same data that was used to post the hash. - Without sponsored relay, the user can call
relayMessage(Identifier calldata _id, bytes calldata _sentMessage)in the same way the relayer does.
It's important to note that whether or not relaying is sponsored, the system will always require that the relayer posts the hash before the relay happens.
Owner of CrossL2Inbox, as the system administrator, can approve new relayers to join the list of trusted entities for message transmission across the network by calling enableRelayer() function.
If a relayer no longer meets the trust requirements or wants to stop acting as one, Owner of CrossL2Inbox has the authority to revoke its access and remove it from the approved relayers list.
The primary license for the boilerplate is MIT, see LICENSE