Crosschain
Smart contract crosschain utilities and implementations
This directory contains contracts for sending and receiving cross chain messages that follows the ERC-7786 standard.
CrosschainLinked: helper to facilitate communication between a contract on one chain and counterparts on remote chains through ERC-7786 gateways.CrosschainRemoteExecutor: executor contract that relays transaction from a controller on a remote chain.ERC7786Recipient: generic ERC-7786 crosschain contract that receives messages from a trusted gateway.
Additionally there are multiple bridge constructions:
BridgeFungible: Core bridging logic for crosschain ERC-20 transfer. Used byBridgeERC20,BridgeERC7802andERC20Crosschain,BridgeNonFungible: Core bridging logic for crosschain ERC-721 transfer. Used byBridgeERC721andERC721Crosschain,BridgeMultiToken: Core bridging logic for crosschain ERC-1155 transfer. Used byBridgeERC1155andERC1155Crosschain,BridgeERC20: Standalone bridge contract to connect an ERC-20 token contract with counterparts on remote chains,BridgeERC721: Standalone bridge contract to connect an ERC-721 token contract with counterparts on remote chains,BridgeERC1155: Standalone bridge contract to connect an ERC-1155 token contract with counterparts on remote chains,BridgeERC7802: Standalone bridge contract to connect an ERC-7802 token contract with counterparts on remote chains.
Helpers
Bridges
import "@openzeppelin/contracts/crosschain/CrosschainLinked.sol";Core bridging mechanism.
This contract contains the logic to register and send messages to counterparts on remote chains using ERC-7786
gateways. It ensure received messages originate from a counterpart. This is the base of token bridges such as
BridgeFungible.
Contracts that inherit from this contract can use the internal CrosschainLinked._sendMessageToCounterpart to send messages to their
counterpart on a foreign chain. They must override the CrosschainRemoteExecutor._processMessage function to handle messages that have
been verified.
Functions
Errors
constructor(struct CrosschainLinked.Link[] links)
internal
#getLink(bytes chain) → address gateway, bytes counterpart
public
#Returns the ERC-7786 gateway used for sending and receiving cross-chain messages to a given chain.
Note: The chain parameter is a "chain-only" InteroperableAddress (empty address) and the counterpart returns
the full InteroperableAddress (chain ref + address) that is on chain.
_setLink(address gateway, bytes counterpart, bool allowOverride)
internal
#Internal setter to change the ERC-7786 gateway and counterpart for a given chain. Called at construction.
Note: The counterpart parameter is the full InteroperableAddress (chain ref + address).
_sendMessageToCounterpart(bytes chain, bytes payload, bytes[] attributes) → bytes32
internal
#Internal messaging function
Note: The chain parameter is a "chain-only" InteroperableAddress (empty address).
_isAuthorizedGateway(address instance, bytes sender) → bool
internal
#Virtual getter that returns whether an address is a valid ERC-7786 gateway for a given sender.
The sender parameter is an interoperable address that include the source chain. The chain part can be
extracted using the InteroperableAddress library to selectively authorize gateways based on the origin chain
of a message.
LinkRegistered(address gateway, bytes counterpart)
event
#Emitted when a new link is registered.
Note: the counterpart argument is a full InteroperableAddress (chain ref + address).
LinkAlreadyRegistered(bytes chain)
error
#Reverted when trying to register a link for a chain that is already registered.
Note: the chain argument is a "chain-only" InteroperableAddress (empty address).
import "@openzeppelin/contracts/crosschain/CrosschainRemoteExecutor.sol";Helper contract used to relay transactions received from a controller through an ERC-7786 gateway. This is
used by the GovernorCrosschain governance module for the execution of cross-chain actions.
A CrosschainRemoteExecutor address can be seen as the local identity of a remote executor on another chain. It
holds assets and permissions for the sake of its controller.
Functions
Errors
ERC7786Recipient
constructor(address initialGateway, bytes initialController)
public
#gateway() → address
public
#Accessor that returns the address of the gateway used by this remote executor.
controller() → bytes
public
#Accessor that returns the interoperable address of the controller allowed to relay instructions to this remote executor.
reconfigure(address newGateway, bytes newController)
public
#Endpoint allowing the controller to reconfigure the executor. This must be called by the executor itself following an instruction from the controller.
setup(address gateway, bytes controller_)
internal
#Internal setter to reconfigure the gateway and controller.
_isAuthorizedGateway(address instance, bytes sender) → bool
internal
#Virtual getter that returns whether an address is a valid ERC-7786 gateway for a given sender.
The sender parameter is an interoperable address that include the source chain. The chain part can be
extracted using the InteroperableAddress library to selectively authorize gateways based on the origin chain
of a message.
_processMessage(address, bytes32, bytes, bytes payload)
internal
#Virtual function that should contain the logic to execute when a cross-chain message is received.
This function should revert on failure. Any silent failure from this function will result in the message being marked as received and not being retryable.
CrosschainControllerSet(address gateway, bytes controller)
event
#Emitted when the gateway or controller of this remote executor is updated.
AccessRestricted()
error
#Reverted when a non-controller tries to relay instructions to this executor.
import "@openzeppelin/contracts/crosschain/ERC7786Recipient.sol";Base implementation of an ERC-7786 compliant cross-chain message receiver.
This abstract contract exposes the receiveMessage function that is used for communication with (one or multiple)
destination gateways. This contract leaves two functions unimplemented:
-
CrosschainLinked._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 which this function returns true would be able to impersonate any account on any other chain sending any message. -
CrosschainRemoteExecutor._processMessage, the internal function that will be called with any message that has been validated.
ERC-7786 requires the gateway to ensure messages are not delivered more than once. Therefore, we don't need to keep track of the processed receiveId.
@custom:stateless
Functions
receiveMessage(bytes32 receiveId, bytes sender, bytes payload) → bytes4
external
#Endpoint for receiving cross-chain message.
This function may be called directly by the gateway.
_isAuthorizedGateway(address gateway, bytes sender) → bool
internal
#Virtual getter that returns whether an address is a valid ERC-7786 gateway for a given sender.
The sender parameter is an interoperable address that include the source chain. The chain part can be
extracted using the InteroperableAddress library to selectively authorize gateways based on the origin chain
of a message.
_processMessage(address gateway, bytes32 receiveId, bytes sender, bytes payload)
internal
#Virtual function that should contain the logic to execute when a cross-chain message is received.
This function should revert on failure. Any silent failure from this function will result in the message being marked as received and not being retryable.
ERC7786RecipientUnauthorizedGateway(address gateway, bytes sender)
error
#Error thrown if the gateway is not authorized to send messages to this contract on behalf of the sender.
import "@openzeppelin/contracts/crosschain/bridges/BridgeERC1155.sol";This is a variant of BridgeMultiToken that implements the bridge logic for ERC-1155 tokens that do not expose
a crosschain mint and burn mechanism. Instead, it takes custody of bridged assets.
Functions
Events
Errors
CrosschainLinked
ERC7786Recipient
constructor(contract IERC1155 token_)
internal
#token() → contract IERC1155
public
#crosschainTransferFrom(address from, bytes to, uint256 id, uint256 value) → bytes32
public
#Transfer amount tokens to a crosschain receiver.
Note: The to parameter is the full InteroperableAddress (chain ref + address).
crosschainTransferFrom(address from, bytes to, uint256[] ids, uint256[] values) → bytes32
public
#Transfer amount tokens to a crosschain receiver.
Note: The to parameter is the full InteroperableAddress (chain ref + address).
_onSend(address from, uint256[] ids, uint256[] values)
internal
#"Locking" tokens is done by taking custody
_onReceive(address to, uint256[] ids, uint256[] values)
internal
#"Unlocking" tokens is done by releasing custody
onERC1155Received(address operator, address, uint256, uint256, bytes) → bytes4
public
#Support receiving tokens only if the transfer was initiated by the bridge itself.
onERC1155BatchReceived(address operator, address, uint256[], uint256[], bytes) → bytes4
public
#Support receiving tokens only if the transfer was initiated by the bridge itself.
import "@openzeppelin/contracts/crosschain/bridges/BridgeERC20.sol";This is a variant of BridgeFungible that implements the bridge logic for ERC-20 tokens that do not expose a
crosschain mint and burn mechanism. Instead, it takes custody of bridged assets.
Functions
BridgeFungible
CrosschainLinked
ERC7786Recipient
Events
Errors
CrosschainLinked
ERC7786Recipient
constructor(contract IERC20 token_)
internal
#token() → contract IERC20
public
#_onSend(address from, uint256 amount)
internal
#"Locking" tokens is done by taking custody
_onReceive(address to, uint256 amount)
internal
#"Unlocking" tokens is done by releasing custody
import "@openzeppelin/contracts/crosschain/bridges/BridgeERC721.sol";This is a variant of BridgeNonFungible that implements the bridge logic for ERC-721 tokens that do not expose
a crosschain mint and burn mechanism. Instead, it takes custody of bridged assets.
Functions
- constructor(token_)
- token()
- crosschainTransferFrom(from, to, tokenId)
- _onSend(from, tokenId)
- _onReceive(to, tokenId)
CrosschainLinked
ERC7786Recipient
Events
Errors
CrosschainLinked
ERC7786Recipient
constructor(contract IERC721 token_)
internal
#token() → contract IERC721
public
#crosschainTransferFrom(address from, bytes to, uint256 tokenId) → bytes32
public
#Transfer tokenId from from (on this chain) to to (on a different chain).
The to parameter is the full InteroperableAddress that references both the destination chain and the account
on that chain. Similarly to the underlying token's ERC721.transferFrom function, this function can be called
either by the token holder or by anyone that is approved by the token holder. It reuses the token's allowance
system, meaning that an account that is "approved for all" or "approved for tokenId" can perform the crosschain
transfer directly without having to take temporary custody of the token.
_onSend(address from, uint256 tokenId)
internal
#"Locking" tokens is done by taking custody
_onReceive(address to, uint256 tokenId)
internal
#"Unlocking" tokens is done by releasing custody
import "@openzeppelin/contracts/crosschain/bridges/BridgeERC7802.sol";This is a variant of BridgeFungible that implements the bridge logic for ERC-7802 compliant tokens.
Functions
BridgeFungible
CrosschainLinked
ERC7786Recipient
Events
Errors
CrosschainLinked
ERC7786Recipient
constructor(contract IERC7802 token_)
internal
#token() → contract IERC7802
public
#_onSend(address from, uint256 amount)
internal
#"Locking" tokens using an ERC-7802 crosschain burn
_onReceive(address to, uint256 amount)
internal
#"Unlocking" tokens using an ERC-7802 crosschain mint
import "@openzeppelin/contracts/crosschain/bridges/abstract/BridgeFungible.sol";Base contract for bridging ERC-20 between chains using an ERC-7786 gateway.
In order to use this contract, two functions must be implemented to link it to the token:
BridgeERC1155._onSend: called when a crosschain transfer is going out. Must take the sender tokens or revert.BridgeERC1155._onReceive: called when a crosschain transfer is coming in. Must give tokens to the receiver.
This base contract is used by the BridgeERC20, which interfaces with legacy ERC-20 tokens, and BridgeERC7802,
which interface with ERC-7802 to provide an approve-free user experience. It is also used by the ERC20Crosschain
extension, which embeds the bridge logic directly in the token contract.
Functions
Events
Errors
CrosschainLinked
ERC7786Recipient
crosschainTransfer(bytes to, uint256 amount) → bytes32
public
#Transfer amount tokens to a crosschain receiver.
Note: The to parameter is the full InteroperableAddress (chain ref + address).
_crosschainTransfer(address from, bytes to, uint256 amount) → bytes32
internal
#Internal crosschain transfer function.
Note: The to parameter is the full InteroperableAddress (chain ref + address).
_processMessage(address, bytes32 receiveId, bytes, bytes payload)
internal
#Virtual function that should contain the logic to execute when a cross-chain message is received.
This function should revert on failure. Any silent failure from this function will result in the message being marked as received and not being retryable.
_onSend(address from, uint256 amount)
internal
#Virtual function: implementation is required to handle token being burnt or locked on the source chain.
_onReceive(address to, uint256 amount)
internal
#Virtual function: implementation is required to handle token being minted or unlocked on the destination chain.
CrosschainFungibleTransferSent(bytes32 indexed sendId, address indexed from, bytes to, uint256 amount)
event
#Emitted when a crosschain ERC-20 transfer is sent.
CrosschainFungibleTransferReceived(bytes32 indexed receiveId, bytes from, address indexed to, uint256 amount)
event
#Emitted when a crosschain ERC-20 transfer is received.
import "@openzeppelin/contracts/crosschain/bridges/abstract/BridgeMultiToken.sol";Base contract for bridging ERC-1155 between chains using an ERC-7786 gateway.
In order to use this contract, two functions must be implemented to link it to the token:
BridgeERC1155._onSend: called when a crosschain transfer is going out. Must take the sender tokens or revert.BridgeERC1155._onReceive: called when a crosschain transfer is coming in. Must give tokens to the receiver.
This base contract is used by the BridgeERC1155, which interfaces with legacy ERC-1155 tokens. It is also used by
the ERC1155Crosschain extension, which embeds the bridge logic directly in the token contract.
This base contract implements the crosschain transfer operation though internal functions. It is for the the "child contracts" that inherit from this to implement the external interfaces and make this functions accessible.
Functions
- _crosschainTransfer(from, to, ids, values)
- _processMessage(, receiveId, , payload)
- _onSend(from, ids, values)
- _onReceive(to, ids, values)
CrosschainLinked
ERC7786Recipient
Events
Errors
CrosschainLinked
ERC7786Recipient
_crosschainTransfer(address from, bytes to, uint256[] ids, uint256[] values) → bytes32
internal
#Internal crosschain transfer function.
Note: The to parameter is the full InteroperableAddress (chain ref + address).
_processMessage(address, bytes32 receiveId, bytes, bytes payload)
internal
#Virtual function that should contain the logic to execute when a cross-chain message is received.
This function should revert on failure. Any silent failure from this function will result in the message being marked as received and not being retryable.
_onSend(address from, uint256[] ids, uint256[] values)
internal
#Virtual function: implementation is required to handle token being burnt or locked on the source chain.
_onReceive(address to, uint256[] ids, uint256[] values)
internal
#Virtual function: implementation is required to handle token being minted or unlocked on the destination chain.
CrosschainMultiTokenTransferSent(bytes32 indexed sendId, address indexed from, bytes to, uint256[] ids, uint256[] values)
event
#CrosschainMultiTokenTransferReceived(bytes32 indexed receiveId, bytes from, address indexed to, uint256[] ids, uint256[] values)
event
#import "@openzeppelin/contracts/crosschain/bridges/abstract/BridgeNonFungible.sol";Base contract for bridging ERC-721 between chains using an ERC-7786 gateway.
In order to use this contract, two functions must be implemented to link it to the token:
BridgeERC1155._onSend: called when a crosschain transfer is going out. Must take the sender tokens or revert.BridgeERC1155._onReceive: called when a crosschain transfer is coming in. Must give tokens to the receiver.
This base contract is used by the BridgeERC721, which interfaces with legacy ERC-721 tokens. It is also used by
the ERC721Crosschain extension, which embeds the bridge logic directly in the token contract.
Functions
- _crosschainTransfer(from, to, tokenId)
- _processMessage(, receiveId, , payload)
- _onSend(from, tokenId)
- _onReceive(to, tokenId)
CrosschainLinked
ERC7786Recipient
Events
Errors
CrosschainLinked
ERC7786Recipient
_crosschainTransfer(address from, bytes to, uint256 tokenId) → bytes32
internal
#Internal crosschain transfer function.
The to parameter is the full InteroperableAddress (chain ref + address).
_processMessage(address, bytes32 receiveId, bytes, bytes payload)
internal
#Virtual function that should contain the logic to execute when a cross-chain message is received.
This function should revert on failure. Any silent failure from this function will result in the message being marked as received and not being retryable.
_onSend(address from, uint256 tokenId)
internal
#Virtual function: implementation is required to handle token being burnt or locked on the source chain.
_onReceive(address to, uint256 tokenId)
internal
#Virtual function: implementation is required to handle token being minted or unlocked on the destination chain.
CrosschainNonFungibleTransferSent(bytes32 indexed sendId, address indexed from, bytes to, uint256 tokenId)
event
#Emitted when a crosschain ERC-721 transfer is sent.
CrosschainNonFungibleTransferReceived(bytes32 indexed receiveId, bytes from, address indexed to, uint256 tokenId)
event
#Emitted when a crosschain ERC-721 transfer is received.