Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add documentation for crosschain message passing #31

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
42 changes: 42 additions & 0 deletions contracts/crosschain/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
= Crosschain

[.readme-notice]
NOTE: This document is better viewed at https://docs.openzeppelin.com/community-contracts/crosschain
ernestognw marked this conversation as resolved.
Show resolved Hide resolved

Gateways are contracts that enable cross-chain communication. These can either be a message source or a destination according to ERC-7786, which defines the following interfaces:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Worth an href to ERC-7786?


* {IERC7786GatewaySource}: A contract interface to send a message to another contract on a different chain.
* {IERC7786Receiver}: An interface that allows an smart contract to receive a crosschain message provided by a trusted destination gateway.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Couldn't the gateway itself implement IERC7786Receiver in which case it wouldn't be provided by a trusted destination gateway? Just a nit edge case.


The library provides an implementation of an ERC-7786 receiver contract as a building block:

* {ERC7786Receiver}: ERC-7786 cross-chain message receiver.

Given ERC-7786 could be enabled natively or via adapters. Developers can access interoperability protocols through gateway adapters. The library includes the following gateway adapters:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not cohesive


* {AxelarGatewayBase}: Core gateway logic for the https://www.axelar.network/[Axelar] adapter.
* {AxelarGatewaySource}: ERC-7786 source gateway adapter (sending side) for Axelar.
* {AxelarGatewayDestination}: ERC-7786 destination gateway adapter (receiving side) for Axelar.
* {AxelarGatewayDuplex}: ERC-7786 gateway adapter that operates in both directions (i.e. send and receive messages) using the Axelar network.

== Gateways

{{IERC7786GatewaySource}}

== Clients

{{IERC7786Receiver}}

{{ERC7786Receiver}}
Amxx marked this conversation as resolved.
Show resolved Hide resolved

== Adapters

=== Axelar

{{AxelarGatewayBase}}

{{AxelarGatewaySource}}

{{AxelarGatewayDestination}}

{{AxelarGatewayDuplex}}
2 changes: 1 addition & 1 deletion contracts/crosschain/axelar/AxelarGatewayDestination.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.27;

import {AxelarExecutable} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {IERC7786Receiver} from "../../interfaces/IERC7786.sol";
import {IERC7786Receiver} from "../utils/IERC7786.sol";
import {AxelarGatewayBase} from "./AxelarGatewayBase.sol";

/**
Expand Down
4 changes: 2 additions & 2 deletions contracts/crosschain/utils/ERC7786Receiver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

pragma solidity ^0.8.27;

import {IERC7786Receiver} from "../../interfaces/IERC7786.sol";
import {IERC7786Receiver} from "./IERC7786.sol";

/**
* @dev Base implementation of an ERC-7786 compliant cross-chain message receiver.
Expand Down Expand Up @@ -33,7 +33,7 @@ abstract contract ERC7786Receiver is IERC7786Receiver {
}

/// @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

Choose a reason for hiding this comment

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

why is this change here?

function _isKnownGateway(address instance) internal virtual returns (bool);

/// @dev Virtual function that should contain the logic to execute when a cross-chain message is received.
function _processMessage(
Expand Down
68 changes: 68 additions & 0 deletions contracts/crosschain/utils/IERC7786.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;
Comment on lines +1 to +3
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would it be possible to do a git mv here to maintain history?


/**
* @dev Interface for ERC-7786 source gateways.
*
* See ERC-7786 for more details
*/
interface IERC7786GatewaySource {
/**
* @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.
*/
event MessagePosted(
bytes32 indexed outboxId,
string sender, // CAIP-10 account identifier (chain identifier + ":" + account address)
string receiver, // CAIP-10 account identifier (chain identifier + ":" + account address)
bytes payload,
bytes[] attributes
);

/// @dev This error is thrown when a message creation fails because of an unsupported attribute being specified.
error UnsupportedAttribute(bytes4 selector);

/// @dev Getter to check whether an attribute is supported or not.
function supportsAttribute(bytes4 selector) external view returns (bool);

/**
* @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.
* @param destinationChain {CAIP2} chain identifier
* @param receiver {CAIP10} account address (does not include the chain identifier)
*
* * MUST emit a {MessagePosted} 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.
*/
function sendMessage(
string calldata destinationChain,
string calldata receiver,
bytes calldata payload,
bytes[] calldata attributes
) external payable returns (bytes32 outboxId);
}

/**
* @dev Interface for the ERC-7786 client contract (receiver).
*
* See ERC-7786 for more details
*/
interface IERC7786Receiver {
/**
* @dev Endpoint for receiving cross-chain message.
* @param sourceChain {CAIP2} chain identifier
* @param sender {CAIP10} account address (does not include the chain identifier)
*
* This function may be called directly by the gateway.
*/
function executeMessage(
string calldata sourceChain, // CAIP-2 chain identifier
string calldata sender, // CAIP-10 account address (does not include the chain identifier)
bytes calldata payload,
bytes[] calldata attributes
) external payable returns (bytes4);
}
65 changes: 1 addition & 64 deletions contracts/interfaces/IERC7786.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,67 +2,4 @@

pragma solidity ^0.8.0;

/**
* @dev Interface for ERC-7786 source gateways.
*
* See ERC-7786 for more details
*/
interface IERC7786GatewaySource {
/**
* @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.
*/
event MessagePosted(
bytes32 indexed outboxId,
string sender, // CAIP-10 account identifier (chain identifier + ":" + account address)
string receiver, // CAIP-10 account identifier (chain identifier + ":" + account address)
bytes payload,
bytes[] attributes
);

/// @dev This error is thrown when a message creation fails because of an unsupported attribute being specified.
error UnsupportedAttribute(bytes4 selector);

/// @dev Getter to check whether an attribute is supported or not.
function supportsAttribute(bytes4 selector) external view returns (bool);

/**
* @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.
* @param destinationChain {CAIP2} chain identifier
* @param receiver {CAIP10} account address (does not include the chain identifier)
*
* * MUST emit a {MessagePosted} 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.
*/
function sendMessage(
string calldata destinationChain,
string calldata receiver,
bytes calldata payload,
bytes[] calldata attributes
) external payable returns (bytes32 outboxId);
}

/**
* @dev Interface for the ERC-7786 client contract (receiver).
*
* See ERC-7786 for more details
*/
interface IERC7786Receiver {
/**
* @dev Endpoint for receiving cross-chain message.
* @param sourceChain {CAIP2} chain identifier
* @param sender {CAIP10} account address (does not include the chain identifier)
*
* This function may be called directly by the gateway.
*/
function executeMessage(
string calldata sourceChain, // CAIP-2 chain identifier
string calldata sender, // CAIP-10 account address (does not include the chain identifier)
bytes calldata payload,
bytes[] calldata attributes
) external payable returns (bytes4);
}
import {IERC7786GatewaySource, IERC7786Receiver} from "../crosschain/utils/IERC7786.sol";
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// contracts/MyCustomAxelarGatewayDestination.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {AxelarGatewayDestination, AxelarExecutable} from "../../../crosschain/axelar/AxelarGatewayDestination.sol";
import {IAxelarGateway} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol";

abstract contract MyCustomAxelarGatewayDestination is AxelarGatewayDestination {
/// @dev Initializes the contract with the Axelar gateway and the initial owner.
constructor(IAxelarGateway gateway, address initialOwner) AxelarExecutable(address(gateway)) {}
}
11 changes: 11 additions & 0 deletions contracts/mocks/docs/crosschain/MyCustomAxelarGatewayDuplex.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// contracts/MyCustomAxelarGatewayDuplex.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {AxelarGatewayDuplex, AxelarExecutable} from "../../../crosschain/axelar/AxelarGatewayDuplex.sol";
import {IAxelarGateway} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol";

abstract contract MyCustomAxelarGatewayDuplex is AxelarGatewayDuplex {
/// @dev Initializes the contract with the Axelar gateway and the initial owner.
constructor(IAxelarGateway gateway, address initialOwner) AxelarGatewayDuplex(gateway, initialOwner) {}
}
13 changes: 13 additions & 0 deletions contracts/mocks/docs/crosschain/MyCustomAxelarGatewaySource.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// contracts/MyERC7786ReceiverContract.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {AxelarGatewaySource} from "../../../crosschain/axelar/AxelarGatewaySource.sol";
import {AxelarGatewayBase} from "../../../crosschain/axelar/AxelarGatewayBase.sol";
import {IAxelarGateway} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol";

abstract contract MyCustomAxelarGatewaySource is AxelarGatewaySource {
/// @dev Initializes the contract with the Axelar gateway and the initial owner.
constructor(IAxelarGateway gateway, address initialOwner) Ownable(initialOwner) AxelarGatewayBase(gateway) {}
}
47 changes: 47 additions & 0 deletions contracts/mocks/docs/crosschain/MyERC7786GatewaySource.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// contracts/MyERC7786GatewaySource.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {CAIP2} from "@openzeppelin/contracts/utils/CAIP2.sol";
import {CAIP10} from "@openzeppelin/contracts/utils/CAIP10.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {IERC7786GatewaySource} from "../../../interfaces/IERC7786.sol";

abstract contract MyERC7786GatewaySource is IERC7786GatewaySource {
using Strings for address;

error UnsupportedNativeTransfer();

/// @inheritdoc IERC7786GatewaySource
function supportsAttribute(bytes4 /*selector*/) public pure returns (bool) {
return false;
}

/// @inheritdoc IERC7786GatewaySource
function sendMessage(
string calldata destinationChain, // CAIP-2 chain identifier
string calldata receiver, // CAIP-10 account address (does not include the chain identifier)
bytes calldata payload,
bytes[] calldata attributes
) external payable returns (bytes32 outboxId) {
require(msg.value == 0, UnsupportedNativeTransfer());
// Use of `if () revert` syntax to avoid accessing attributes[0] if it's empty
if (attributes.length > 0)
revert UnsupportedAttribute(attributes[0].length < 0x04 ? bytes4(0) : bytes4(attributes[0][0:4]));

// Emit event
outboxId = bytes32(0); // Explicitly set to 0. Can be used for post-processing
emit MessagePosted(
outboxId,
CAIP10.format(CAIP2.local(), msg.sender.toChecksumHexString()),
CAIP10.format(destinationChain, receiver),
payload,
attributes
);

// Optionally: If this is an adapter, send the message to a protocol gateway for processing
// This may require the logic for tracking destination gateway addresses and chain identifiers

return outboxId;
}
}
29 changes: 29 additions & 0 deletions contracts/mocks/docs/crosschain/MyERC7786ReceiverContract.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// contracts/MyERC7786ReceiverContract.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {AccessManaged} from "@openzeppelin/contracts/access/manager/AccessManaged.sol";
import {ERC7786Receiver} from "../../../crosschain/utils/ERC7786Receiver.sol";

contract MyERC7786ReceiverContract is ERC7786Receiver, AccessManaged {
constructor(address initialAuthority) AccessManaged(initialAuthority) {}

/// @dev Check if the given instance is a known gateway.
function _isKnownGateway(address /* instance */) internal virtual override restricted returns (bool) {
// The restricted modifier ensures that this function is only called by a known authority.
return true;
}

/// @dev Internal endpoint for receiving cross-chain message.
/// @param sourceChain {CAIP2} chain identifier
/// @param sender {CAIP10} account address (does not include the chain identifier)
function _processMessage(
address gateway,
string calldata sourceChain,
string calldata sender,
bytes calldata payload,
bytes[] calldata attributes
) internal virtual override restricted {
// Process the message here
}
}
1 change: 1 addition & 0 deletions docs/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
* xref:index.adoc[Overview]
* xref:account-abstraction.adoc[Account Abstraction]
* xref:crosschain.adoc[Crosschain]
* xref:utilities.adoc[Utilities]
Loading
Loading