Crosschain
Smart contract crosschain utilities and implementations
Gateways are contracts that enable cross-chain communication. These can either be a message source or a destination according to ERC-7786.
ERC7786Receiver
: ERC-7786 cross-chain message receiver.ERC7786OpenBridge
: ERC-7786 "N out of M" gateway. Sends a message through M gateways and executes on the destination if N received it.
Developers can access interoperability protocols through gateway adapters. The library includes the following gateway adapters:
AxelarGatewayAdapter
: ERC-7786 gateway adapter for Axelar.
Gateways
Clients
Adapters
Axelar
import "@openzeppelin/community-contracts/crosschain/ERC7786OpenBridge.sol";
N of M gateway: Sends your message through M independent gateways. It will be delivered to the receiver by an equivalent bridge on the destination chain if N of the M gateways agree.
Functions
- constructor(owner_, gateways_, threshold_)
- supportsAttribute()
- sendMessage(recipient, payload, attributes)
- receiveMessage(, sender, payload)
- getGateways()
- getThreshold()
- getRemoteBridge(chain)
- getRemoteBridge(chainType, chainReference)
- addGateway(gateway)
- removeGateway(gateway)
- setThreshold(newThreshold)
- registerRemoteBridge(bridge)
- pause()
- unpause()
- sweep(to)
- _addGateway(gateway)
- _removeGateway(gateway)
- _setThreshold(newThreshold)
- _registerRemoteBridge(bridge)
Pausable
Ownable
IERC7786Receiver
IERC7786GatewaySource
Events
Errors
- UnsupportedNativeTransfer()
- ERC7786OpenBridgeInvalidCrosschainSender()
- ERC7786OpenBridgeAlreadyExecuted()
- ERC7786OpenBridgeRemoteNotRegistered(chainType, chainReference)
- ERC7786OpenBridgeGatewayAlreadyRegistered(gateway)
- ERC7786OpenBridgeGatewayNotRegistered(gateway)
- ERC7786OpenBridgeThresholdViolation()
- ERC7786OpenBridgeInvalidExecutionReturnValue()
- RemoteAlreadyRegistered(remote)
Pausable
Ownable
IERC7786Receiver
IERC7786GatewaySource
constructor(address owner_, address[] gateways_, uint8 threshold_)
public
#supportsAttribute(bytes4) → bool
public
#Getter to check whether an attribute is supported or not.
sendMessage(bytes recipient, bytes payload, bytes[] attributes) → bytes32 sendId
public
#Using memory instead of calldata avoids stack too deep errors
receiveMessage(bytes32, bytes sender, bytes payload) → bytes4
public
#This function serves a dual purpose:
It will be called by ERC-7786 gateways with message coming from the the corresponding bridge on the source chain. These "signals" are tracked until the threshold is reached. At that point the message is sent to the destination.
It can also be called by anyone (including an ERC-7786 gateway) to retry the execution. This can be useful if the automatic execution (that is triggered when the threshold is reached) fails, and someone wants to retry it.
When a message is forwarded by a known gateway, a ERC7786OpenBridge.Received
event is emitted. If a known gateway calls this
function more than once (for a given message), only the first call is counts toward the threshold and emits an
ERC7786OpenBridge.Received
event.
This function revert if:
- the message is not properly formatted or does not originate from the registered bridge on the source chain.
- someone tries re-execute a message that was already successfully delivered. This includes gateways that call this function a second time with a message that was already executed.
- the execution of the message (on the
IERC7786Receiver
receiver) is successful but fails to return the executed value.
This function does not revert if:
- A known gateway delivers a message for the first time, and that message was already executed. In that case the message is NOT re-executed, and the correct "magic value" is returned.
- The execution of the message (on the
IERC7786Receiver
receiver) reverts. In that case aERC7786OpenBridge.ExecutionFailed
event is emitted.
This function emits:
ERC7786OpenBridge.Received
when a known ERC-7786 gateway delivers a message for the first time.ERC7786OpenBridge.ExecutionSuccess
when a message is successfully delivered to the receiver.ERC7786OpenBridge.ExecutionFailed
when a message delivery to the receiver reverted (for example because of OOG error).
interface requires this function to be payable. Even if we don't expect any value, a gateway may pass
some value for unknown reason. In that case we want to register this gateway having delivered the message and
not revert. Any value accrued that way can be recovered by the admin using the ERC7786OpenBridge.sweep
function.
getGateways() → address[]
public
#getThreshold() → uint8
public
#getRemoteBridge(bytes chain) → bytes
public
#getRemoteBridge(bytes2 chainType, bytes chainReference) → bytes
public
#addGateway(address gateway)
public
#removeGateway(address gateway)
public
#setThreshold(uint8 newThreshold)
public
#registerRemoteBridge(bytes bridge)
public
#pause()
public
#unpause()
public
#sweep(address payable to)
public
#Recovery method in case value is ever received through ERC7786OpenBridge.receiveMessage
_addGateway(address gateway)
internal
#_removeGateway(address gateway)
internal
#_setThreshold(uint8 newThreshold)
internal
#_registerRemoteBridge(bytes bridge)
internal
#OutboxDetails(bytes32 indexed sendId, struct ERC7786OpenBridge.Outbox[] outbox)
event
#Received(bytes32 indexed receiveId, address gateway)
event
#ExecutionSuccess(bytes32 indexed receiveId)
event
#ExecutionFailed(bytes32 indexed receiveId)
event
#GatewayAdded(address indexed gateway)
event
#GatewayRemoved(address indexed gateway)
event
#ThresholdUpdated(uint8 threshold)
event
#RemoteRegistered(bytes remote)
event
#UnsupportedNativeTransfer()
error
#ERC7786OpenBridgeInvalidCrosschainSender()
error
#ERC7786OpenBridgeAlreadyExecuted()
error
#ERC7786OpenBridgeRemoteNotRegistered(bytes2 chainType, bytes chainReference)
error
#ERC7786OpenBridgeGatewayAlreadyRegistered(address gateway)
error
#ERC7786OpenBridgeGatewayNotRegistered(address gateway)
error
#ERC7786OpenBridgeThresholdViolation()
error
#ERC7786OpenBridgeInvalidExecutionReturnValue()
error
#RemoteAlreadyRegistered(bytes remote)
error
#import "@openzeppelin/community-contracts/crosschain/axelar/AxelarGatewayAdapter.sol";
Implementation of an ERC-7786 gateway destination adapter for the Axelar Network in dual mode.
The contract implements AxelarExecutable's ERC7579DelayedExecutor._execute
function to execute the message, converting Axelar's native
workflow into the standard ERC-7786.
While both ERC-7786 and Axelar do support non-evm chains, this adaptor does not. This limitation comes from the translation of the ERC-7930 interoperable address (binary objects -- bytes) to strings. This is necessary because Axelar uses string to represent addresses. For EVM network, this adapter uses a checksum hex string representation. Other networks would require a different encoding. Ideally we would have a single encoding for all networks (could be base58, base64, ...) but Axelar doesn't support that.
Functions
- constructor(gateway, initialOwner)
- getAxelarChain(input)
- getErc7930Chain(input)
- getRemoteGateway(chain)
- getRemoteGateway(chainType, chainReference)
- registerChainEquivalence(chain, axelar)
- registerRemoteGateway(remote)
- supportsAttribute()
- sendMessage(recipient, payload, attributes)
- _execute(commandId, axelarSourceChain, axelarSourceAddress, adapterPayload)
- _stringifyAddress(chainType, addr)
AxelarExecutable
IAxelarExecutable
Ownable
IERC7786GatewaySource
Events
Errors
- UnsupportedNativeTransfer()
- InvalidOriginGateway(axelarSourceChain, axelarSourceAddress)
- ReceiverExecutionFailed()
- UnsupportedChainType(chainType)
- UnsupportedERC7930Chain(erc7930binary)
- UnsupportedAxelarChain(axelar)
- InvalidChainIdentifier(erc7930binary)
- ChainEquivalenceAlreadyRegistered(erc7930binary, axelar)
- RemoteGatewayAlreadyRegistered(chainType, chainReference)
AxelarExecutable
IAxelarExecutable
Ownable
IERC7786GatewaySource
constructor(contract IAxelarGateway gateway, address initialOwner)
public
#Initializes the contract with the Axelar gateway and the initial owner.
getAxelarChain(bytes input) → string output
public
#Returns the Axelar chain identifier for a given binary interoperable chain id.
getErc7930Chain(string input) → bytes output
public
#Returns the binary interoperable chain id for a given Axelar chain identifier.
getRemoteGateway(bytes chain) → bytes
public
#Returns the address of the remote gateway for a given binary interoperable chain id.
getRemoteGateway(bytes2 chainType, bytes chainReference) → bytes
public
#Returns the address of the remote gateway for a given chainType and chainReference.
registerChainEquivalence(bytes chain, string axelar)
public
#Registers a chain equivalence between a binary interoperable chain id and an Axelar chain identifier.
registerRemoteGateway(bytes remote)
public
#Registers the address of a remote gateway.
supportsAttribute(bytes4) → bool
public
#Getter to check whether an attribute is supported or not.
sendMessage(bytes recipient, bytes payload, bytes[] attributes) → bytes32
external
#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.
- MUST emit a
IERC7786GatewaySource.MessageSent
event.
If any of the attributes
is not supported, this function SHOULD revert with an IERC7786GatewaySource.UnsupportedAttribute
error.
Other errors SHOULD revert with errors not specified in ERC-7786.
_execute(bytes32 commandId, string axelarSourceChain, string axelarSourceAddress, bytes adapterPayload)
internal
#Execution of a cross-chain message.
In this function:
axelarSourceChain
is in the Axelar format. It should not be expected to be a proper ERC-7930 formataxelarSourceAddress
is the sender of the Axelar message. That should be the remote gateway on the chain which the message originates from. It is NOT the sender of the ERC-7786 crosschain message.
Proper ERC-7930 encoding of the crosschain message sender can be found in the message
_stringifyAddress(bytes2 chainType, bytes addr) → string
internal
#ERC-7930 to Axelar address translation. Currently only supports EVM chains.
RegisteredRemoteGateway(bytes remote)
event
#A remote gateway has been registered for a chain.
RegisteredChainEquivalence(bytes erc7930binary, string axelar)
event
#A chain equivalence has been registered.
UnsupportedNativeTransfer()
error
#InvalidOriginGateway(string axelarSourceChain, string axelarSourceAddress)
error
#ReceiverExecutionFailed()
error
#UnsupportedChainType(bytes2 chainType)
error
#UnsupportedERC7930Chain(bytes erc7930binary)
error
#UnsupportedAxelarChain(string axelar)
error
#InvalidChainIdentifier(bytes erc7930binary)
error
#ChainEquivalenceAlreadyRegistered(bytes erc7930binary, string axelar)
error
#RemoteGatewayAlreadyRegistered(bytes2 chainType, bytes chainReference)
error
#import "@openzeppelin/community-contracts/crosschain/utils/ERC7786Attributes.sol";
Library of helper to parse/process ERC-7786 attributes
Functions
tryDecodeRequestRelay(bytes attribute) → bool success, uint256 value, uint256 gasLimit, address refundRecipient
internal
#Parse the requestRelay(uint256,uint256,address)
(0x4cbb573a) attribute into its components.
import "@openzeppelin/community-contracts/crosschain/utils/ERC7786Receiver.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:
ERC7786Receiver._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
this function returns true would be able to impersonate any account on any other chain sending any message.
ERC7786Receiver._processMessage
, the internal function that will be called with any message that has been validated.
Functions
receiveMessage(bytes32 receiveId, bytes sender, bytes payload) → bytes4
public
#Endpoint for receiving cross-chain message.
This function may be called directly by the gateway.
_isKnownGateway(address instance) → bool
internal
#Virtual getter that returns whether an address is a valid ERC-7786 gateway.
_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.
ERC7786ReceiverInvalidGateway(address gateway)
error
#ERC7786ReceivePassiveModeValue()
error
#import "@openzeppelin/community-contracts/crosschain/wormhole/WormholeGatewayAdapter.sol";
An ERC-7786 compliant adapter to send and receive messages via Wormhole.
Note: only EVM chains are currently supported
Modifiers
Functions
- constructor(wormholeRelayer, wormholeChainId, initialOwner)
- relayer()
- supportedChain(chain)
- supportedChain(chainId)
- getWormholeChain(chain)
- getWormholeChain(chainId)
- getChainId(wormholeId)
- getRemoteGateway(chain)
- getRemoteGateway(chainId)
- registerChainEquivalence(chain, wormholeId)
- registerChainEquivalence(chainId, wormholeId)
- registerRemoteGateway(remote)
- registerRemoteGateway(chainId, addr)
- supportsAttribute(selector)
- sendMessage(recipient, payload, attributes)
- quoteRelay(recipient, , , value, gasLimit, )
- requestRelay(sendId, gasLimit, refundRecipient)
- receiveWormholeMessages(adapterPayload, additionalMessages, wormholeSourceAddress, wormholeSourceChain, deliveryHash)
Ownable
IWormholeReceiver
IERC7786GatewaySource
Events
Errors
- InvalidAttributeEncoding(attribute)
- DuplicatedAttribute()
- UnauthorizedCaller()
- InvalidOriginGateway(wormholeSourceChain, wormholeSourceAddress)
- ReceiverExecutionFailed()
- UnsupportedChainId(chainId)
- UnsupportedWormholeChain(wormholeId)
- ChainEquivalenceAlreadyRegistered(chainId, wormhole)
- RemoteGatewayAlreadyRegistered(chainId)
- InvalidSendId(sendId)
- AdditionalMessagesNotSupported()
- MessageAlreadyExecuted(chainId, outboxId)
Ownable
IWormholeReceiver
IERC7786GatewaySource
onlyWormholeRelayer()
internal
#constructor(contract IWormholeRelayer wormholeRelayer, uint16 wormholeChainId, address initialOwner)
public
#Initializes the contract with the Wormhole gateway and the initial owner.
relayer() → address
public
#Returns the local Wormhole relayer
supportedChain(bytes chain) → bool
public
#Returns whether a binary interoperable chain id is supported.
supportedChain(uint256 chainId) → bool
public
#Returns whether an EVM chain id is supported.
getWormholeChain(bytes chain) → uint16
public
#Returns the Wormhole chain id that correspond to a given binary interoperable chain id.
getWormholeChain(uint256 chainId) → uint16
public
#Returns the Wormhole chain id that correspond to a given EVM chain id.
getChainId(uint16 wormholeId) → uint256
public
#Returns the EVM chain id for a given Wormhole chain id.
getRemoteGateway(bytes chain) → address
public
#Returns the address of the remote gateway for a given binary interoperable chain id.
getRemoteGateway(uint256 chainId) → address
public
#Returns the address of the remote gateway for a given EVM chain id.
registerChainEquivalence(bytes chain, uint16 wormholeId)
public
#Registers a chain equivalence between a binary interoperable chain id and a Wormhole chain id.
registerChainEquivalence(uint256 chainId, uint16 wormholeId)
public
#Registers a chain equivalence between an EVM chain id and a Wormhole chain id.
registerRemoteGateway(bytes remote)
public
#Registers the address of a remote gateway (binary interoperable address version).
registerRemoteGateway(uint256 chainId, address addr)
public
#Registers the address of a remote gateway (EVM version).
supportsAttribute(bytes4 selector) → bool
public
#Getter to check whether an attribute is supported or not.
sendMessage(bytes recipient, bytes payload, bytes[] attributes) → bytes32 sendId
external
#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.
- MUST emit a
IERC7786GatewaySource.MessageSent
event.
If any of the attributes
is not supported, this function SHOULD revert with an IERC7786GatewaySource.UnsupportedAttribute
error.
Other errors SHOULD revert with errors not specified in ERC-7786.
quoteRelay(bytes recipient, bytes, bytes[], uint256 value, uint256 gasLimit, address) → uint256
external
#Returns a quote for the value that must be passed to WormholeGatewayAdapter.requestRelay
requestRelay(bytes32 sendId, uint256 gasLimit, address refundRecipient)
external
#Relay a message that was initiated by ERC7786OpenBridge.sendMessage
.
receiveWormholeMessages(bytes adapterPayload, bytes[] additionalMessages, bytes32 wormholeSourceAddress, uint16 wormholeSourceChain, bytes32 deliveryHash)
public
#MessageRelayed(bytes32 sendId)
event
#A message was relayed to Wormhole (part of the post processing of the outbox ids created by ERC7786OpenBridge.sendMessage
)
RegisteredRemoteGateway(uint256 chainId, address remote)
event
#A remote gateway has been registered for a chain.
RegisteredChainEquivalence(uint256 chainId, uint16 wormholeId)
event
#A chain equivalence has been registered.
InvalidAttributeEncoding(bytes attribute)
error
#DuplicatedAttribute()
error
#UnauthorizedCaller(address)
error
#InvalidOriginGateway(uint16 wormholeSourceChain, bytes32 wormholeSourceAddress)
error
#ReceiverExecutionFailed()
error
#UnsupportedChainId(uint256 chainId)
error
#UnsupportedWormholeChain(uint16 wormholeId)
error
#ChainEquivalenceAlreadyRegistered(uint256 chainId, uint16 wormhole)
error
#RemoteGatewayAlreadyRegistered(uint256 chainId)
error
#InvalidSendId(bytes32 sendId)
error
#AdditionalMessagesNotSupported()
error
#MessageAlreadyExecuted(uint256 chainId, bytes32 outboxId)
error
#