Crosschain

Gateways are contracts that enable cross-chain communication. These can either be a message source or a destination according to ERC-7786.

  • ERC7786OpenBridge: ERC-7786 "N out of M" gateway. Sends a message through M gateways and executes on the destination if N received it.

  • ERC7786Attributes: Library of helpers to parse and process ERC-7786 attributes.

Developers can access interoperability protocols through gateway adapters. The library includes the following gateway adapters:

Gateways

ERC7786OpenBridge

import "@openzeppelin/community-contracts/crosschain/ERC7786OpenBridge.sol";

N of M gateway: Sends your message through M independent gateways. It will be delivered to the recipient 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
  • paused()

  • _requireNotPaused()

  • _requirePaused()

  • _pause()

  • _unpause()

Ownable
  • owner()

  • _checkOwner()

  • renounceOwnership()

  • transferOwnership(newOwner)

  • _transferOwnership(newOwner)

Events
  • OutboxDetails(sendId, outbox)

  • Received(receiveId, gateway)

  • ExecutionSuccess(receiveId)

  • ExecutionFailed(receiveId)

  • GatewayAdded(gateway)

  • GatewayRemoved(gateway)

  • ThresholdUpdated(threshold)

  • RemoteRegistered(remote)

Pausable
  • Paused(account)

  • Unpaused(account)

Ownable
  • OwnershipTransferred(previousOwner, newOwner)

IERC7786GatewaySource
  • MessageSent(sendId, sender, recipient, payload, value, attributes)

Errors
  • UnsupportedNativeTransfer()

  • ERC7786OpenBridgeInvalidCrosschainSender()

  • ERC7786OpenBridgeAlreadyExecuted()

  • ERC7786OpenBridgeRemoteNotRegistered(chainType, chainReference)

  • ERC7786OpenBridgeGatewayAlreadyRegistered(gateway)

  • ERC7786OpenBridgeGatewayNotRegistered(gateway)

  • ERC7786OpenBridgeThresholdViolation()

  • ERC7786OpenBridgeInvalidExecutionReturnValue()

  • RemoteAlreadyRegistered(remote)

Pausable
  • EnforcedPause()

  • ExpectedPause()

Ownable
  • OwnableUnauthorizedAccount(account)

  • OwnableInvalidOwner(owner)

IERC7786GatewaySource
  • UnsupportedAttribute(selector)

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 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 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 {IERC7786Recipient} recipient) 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 {IERC7786Recipient} recipient) reverts. In that case a ExecutionFailed event is emitted.

This function emits:

  • Received when a known ERC-7786 gateway delivers a message for the first time.

  • ExecutionSuccess when a message is successfully delivered to the recipient.

  • ExecutionFailed when a message delivery to the recipient 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 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 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

Utilities

ERC7786Attributes

import "@openzeppelin/community-contracts/crosschain/utils/ERC7786Attributes.sol";

Library of helper to parse/process ERC-7786 attributes

Functions
  • tryDecodeRequestRelay(attribute)

tryDecodeRequestRelay(bytes attribute) → bool success, uint256 value, uint256 gasLimit, address refundRecipient internal

Parse the requestRelay(uint256,uint256,address) (0x4cbb573a) attribute into its components.

Adapters

Axelar

AxelarGatewayAdapter

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 _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
  • execute(commandId, sourceChain, sourceAddress, payload)

  • gateway()

Ownable
  • owner()

  • _checkOwner()

  • renounceOwnership()

  • transferOwnership(newOwner)

  • _transferOwnership(newOwner)

Events
  • RegisteredRemoteGateway(remote)

  • RegisteredChainEquivalence(erc7930binary, axelar)

Ownable
  • OwnershipTransferred(previousOwner, newOwner)

IERC7786GatewaySource
  • MessageSent(sendId, sender, recipient, payload, value, attributes)

Errors
  • UnsupportedNativeTransfer()

  • InvalidOriginGateway(axelarSourceChain, axelarSourceAddress)

  • RecipientExecutionFailed()

  • UnsupportedChainType(chainType)

  • UnsupportedERC7930Chain(erc7930binary)

  • UnsupportedAxelarChain(axelar)

  • InvalidChainIdentifier(erc7930binary)

  • ChainEquivalenceAlreadyRegistered(erc7930binary, axelar)

  • RemoteGatewayAlreadyRegistered(chainType, chainReference)

IAxelarExecutable
  • InvalidAddress()

  • NotApprovedByGateway()

Ownable
  • OwnableUnauthorizedAccount(account)

  • OwnableInvalidOwner(owner)

IERC7786GatewaySource
  • UnsupportedAttribute(selector)

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 sendId must be returned. Otherwise, the message MUST be sent and this function must return 0.

  • MUST emit a {MessageSent} event.

If any of the attributes is not supported, this function SHOULD revert with an {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 format

  • axelarSourceAddress 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

RecipientExecutionFailed() 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

Wormhole

WormholeGatewayAdapter

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
  • onlyWormholeRelayer()

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
  • owner()

  • _checkOwner()

  • renounceOwnership()

  • transferOwnership(newOwner)

  • _transferOwnership(newOwner)

Events
  • MessageRelayed(sendId)

  • RegisteredRemoteGateway(chainId, remote)

  • RegisteredChainEquivalence(chainId, wormholeId)

Ownable
  • OwnershipTransferred(previousOwner, newOwner)

IERC7786GatewaySource
  • MessageSent(sendId, sender, recipient, payload, value, attributes)

Errors
  • InvalidAttributeEncoding(attribute)

  • DuplicatedAttribute()

  • UnauthorizedCaller()

  • InvalidOriginGateway(wormholeSourceChain, wormholeSourceAddress)

  • RecipientExecutionFailed()

  • UnsupportedChainId(chainId)

  • UnsupportedWormholeChain(wormholeId)

  • ChainEquivalenceAlreadyRegistered(chainId, wormhole)

  • RemoteGatewayAlreadyRegistered(chainId)

  • InvalidSendId(sendId)

  • AdditionalMessagesNotSupported()

  • MessageAlreadyExecuted(chainId, outboxId)

Ownable
  • OwnableUnauthorizedAccount(account)

  • OwnableInvalidOwner(owner)

IERC7786GatewaySource
  • UnsupportedAttribute(selector)

Internal Variables
  • contract IWormholeRelayer _wormholeRelayer

  • uint16 _wormholeChainId

onlyWormholeRelayer() modifier

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 sendId must be returned. Otherwise, the message MUST be sent and this function must return 0.

  • MUST emit a {MessageSent} event.

If any of the attributes is not supported, this function SHOULD revert with an {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 requestRelay

requestRelay(bytes32 sendId, uint256 gasLimit, address refundRecipient) external

Relay a message that was initiated by 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 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

RecipientExecutionFailed() 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

contract IWormholeRelayer _wormholeRelayer internal

uint16 _wormholeChainId internal