-
Notifications
You must be signed in to change notification settings - Fork 11.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add utilities for CrossChain messaging (#3183)
Co-authored-by: Francisco Giordano <[email protected]>
- Loading branch information
Showing
36 changed files
with
1,477 additions
and
73 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.4; | ||
|
||
import "./AccessControl.sol"; | ||
import "../crosschain/CrossChainEnabled.sol"; | ||
|
||
/** | ||
* @dev An extension to {AccessControl} with support for cross-chain access management. | ||
* For each role, is extension implements an equivalent "aliased" role that is used for | ||
* restricting calls originating from other chains. | ||
* | ||
* For example, if a function `myFunction` is protected by `onlyRole(SOME_ROLE)`, and | ||
* if an address `x` has role `SOME_ROLE`, it would be able to call `myFunction` directly. | ||
* A wallet or contract at the same address on another chain would however not be able | ||
* to call this function. In order to do so, it would require to have the role | ||
* `_crossChainRoleAlias(SOME_ROLE)`. | ||
* | ||
* This aliasing is required to protect against multiple contracts living at the same | ||
* address on different chains but controlled by conflicting entities. | ||
* | ||
* _Available since v4.6._ | ||
*/ | ||
abstract contract AccessControlCrossChain is AccessControl, CrossChainEnabled { | ||
bytes32 public constant CROSSCHAIN_ALIAS = keccak256("CROSSCHAIN_ALIAS"); | ||
|
||
/** | ||
* @dev See {AccessControl-_checkRole}. | ||
*/ | ||
function _checkRole(bytes32 role) internal view virtual override { | ||
if (_isCrossChain()) { | ||
_checkRole(_crossChainRoleAlias(role), _crossChainSender()); | ||
} else { | ||
super._checkRole(role); | ||
} | ||
} | ||
|
||
/** | ||
* @dev Returns the aliased role corresponding to `role`. | ||
*/ | ||
function _crossChainRoleAlias(bytes32 role) internal pure virtual returns (bytes32) { | ||
return role ^ CROSSCHAIN_ALIAS; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.4; | ||
|
||
import "./errors.sol"; | ||
|
||
/** | ||
* @dev Provides information for building cross-chain aware contracts. This | ||
* abstract contract provides accessors and modifiers to control the execution | ||
* flow when receiving cross-chain messages. | ||
* | ||
* Actual implementations of cross-chain aware contracts, which are based on | ||
* this abstraction, will have to inherit from a bridge-specific | ||
* specialization. Such specializations are provided under | ||
* `crosschain/<chain>/CrossChainEnabled<chain>.sol`. | ||
* | ||
* _Available since v4.6._ | ||
*/ | ||
abstract contract CrossChainEnabled { | ||
/** | ||
* @dev Throws if the current function call is not the result of a | ||
* cross-chain execution. | ||
*/ | ||
modifier onlyCrossChain() { | ||
if (!_isCrossChain()) revert NotCrossChainCall(); | ||
_; | ||
} | ||
|
||
/** | ||
* @dev Throws if the current function call is not the result of a | ||
* cross-chain execution initiated by `account`. | ||
*/ | ||
modifier onlyCrossChainSender(address expected) { | ||
address actual = _crossChainSender(); | ||
if (expected != actual) revert InvalidCrossChainSender(actual, expected); | ||
_; | ||
} | ||
|
||
/** | ||
* @dev Returns whether the current function call is the result of a | ||
* cross-chain message. | ||
*/ | ||
function _isCrossChain() internal view virtual returns (bool); | ||
|
||
/** | ||
* @dev Returns the address of the sender of the cross-chain message that | ||
* triggered the current function call. | ||
* | ||
* IMPORTANT: Should revert with `NotCrossChainCall` if the current function | ||
* call is not the result of a cross-chain message. | ||
*/ | ||
function _crossChainSender() internal view virtual returns (address); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
= Cross Chain Awareness | ||
|
||
[.readme-notice] | ||
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/crosschain | ||
|
||
This directory provides building blocks to improve cross-chain awareness of smart contracts. | ||
|
||
- {CrossChainEnabled} is an abstraction that contains accessors and modifiers to control the execution flow when receiving cross-chain messages. | ||
== CrossChainEnabled specializations | ||
|
||
The following specializations of {CrossChainEnabled} provide implementations of the {CrossChainEnabled} abstraction for specific bridges. This can be used to complexe cross-chain aware components such as {AccessControlCrossChain}. | ||
|
||
{{CrossChainEnabledAMB}} | ||
|
||
{{CrossChainEnabledArbitrumL1}} | ||
|
||
{{CrossChainEnabledArbitrumL2}} | ||
|
||
{{CrossChainEnabledOptimism}} | ||
|
||
{{CrossChainEnabledPolygonChild}} | ||
|
||
== Libraries for cross-chain | ||
|
||
In addition to the {CrossChainEnable} abstraction, cross-chain awareness is also available through libraries. These libraries can be used to build complex designs such as contracts with the ability to interact with multiple bridges. | ||
|
||
{{LibAMB}} | ||
|
||
{{LibArbitrumL1}} | ||
|
||
{{LibArbitrumL2}} | ||
|
||
{{LibOptimism}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.4; | ||
|
||
import "../CrossChainEnabled.sol"; | ||
import "./LibAMB.sol"; | ||
|
||
/** | ||
* @dev [AMB](https://docs.tokenbridge.net/amb-bridge/about-amb-bridge) | ||
* specialization or the {CrossChainEnabled} abstraction. | ||
* | ||
* As of february 2020, AMB bridges are available between the following chains: | ||
* - [ETH <> xDai](https://docs.tokenbridge.net/eth-xdai-amb-bridge/about-the-eth-xdai-amb) | ||
* - [ETH <> qDai](https://docs.tokenbridge.net/eth-qdai-bridge/about-the-eth-qdai-amb) | ||
* - [ETH <> ETC](https://docs.tokenbridge.net/eth-etc-amb-bridge/about-the-eth-etc-amb) | ||
* - [ETH <> BSC](https://docs.tokenbridge.net/eth-bsc-amb/about-the-eth-bsc-amb) | ||
* - [ETH <> POA](https://docs.tokenbridge.net/eth-poa-amb-bridge/about-the-eth-poa-amb) | ||
* - [BSC <> xDai](https://docs.tokenbridge.net/bsc-xdai-amb/about-the-bsc-xdai-amb) | ||
* - [POA <> xDai](https://docs.tokenbridge.net/poa-xdai-amb/about-the-poa-xdai-amb) | ||
* - [Rinkeby <> xDai](https://docs.tokenbridge.net/rinkeby-xdai-amb-bridge/about-the-rinkeby-xdai-amb) | ||
* - [Kovan <> Sokol](https://docs.tokenbridge.net/kovan-sokol-amb-bridge/about-the-kovan-sokol-amb) | ||
* | ||
* _Available since v4.6._ | ||
*/ | ||
contract CrossChainEnabledAMB is CrossChainEnabled { | ||
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable | ||
address private immutable _bridge; | ||
|
||
/// @custom:oz-upgrades-unsafe-allow constructor | ||
constructor(address bridge) { | ||
_bridge = bridge; | ||
} | ||
|
||
/** | ||
* @dev see {CrossChainEnabled-_isCrossChain} | ||
*/ | ||
function _isCrossChain() internal view virtual override returns (bool) { | ||
return LibAMB.isCrossChain(_bridge); | ||
} | ||
|
||
/** | ||
* @dev see {CrossChainEnabled-_crossChainSender} | ||
*/ | ||
function _crossChainSender() internal view virtual override onlyCrossChain returns (address) { | ||
return LibAMB.crossChainSender(_bridge); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.4; | ||
|
||
import {IAMB as AMB_Bridge} from "../../vendor/amb/IAMB.sol"; | ||
import "../errors.sol"; | ||
|
||
/** | ||
* @dev Primitives for cross-chain aware contracts using the | ||
* [AMB](https://docs.tokenbridge.net/amb-bridge/about-amb-bridge) | ||
* family of bridges. | ||
*/ | ||
library LibAMB { | ||
/** | ||
* @dev Returns whether the current function call is the result of a | ||
* cross-chain message relayed by `bridge`. | ||
*/ | ||
function isCrossChain(address bridge) internal view returns (bool) { | ||
return msg.sender == bridge; | ||
} | ||
|
||
/** | ||
* @dev Returns the address of the sender that triggered the current | ||
* cross-chain message through `bridge`. | ||
* | ||
* NOTE: {isCrossChain} should be checked before trying to recover the | ||
* sender, as it will revert with `NotCrossChainCall` if the current | ||
* function call is not the result of a cross-chain message. | ||
*/ | ||
function crossChainSender(address bridge) internal view returns (address) { | ||
if (!isCrossChain(bridge)) revert NotCrossChainCall(); | ||
return AMB_Bridge(bridge).messageSender(); | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
contracts/crosschain/arbitrum/CrossChainEnabledArbitrumL1.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.4; | ||
|
||
import "../CrossChainEnabled.sol"; | ||
import "./LibArbitrumL1.sol"; | ||
|
||
/** | ||
* @dev [Arbitrum](https://arbitrum.io/) specialization or the | ||
* {CrossChainEnabled} abstraction the L1 side (mainnet). | ||
* | ||
* This version should only be deployed on L1 to process cross-chain messages | ||
* originating from L2. For the other side, use {CrossChainEnabledArbitrumL2}. | ||
* | ||
* The bridge contract is provided and maintained by the arbitrum team. You can | ||
* find the address of this contract on the rinkeby testnet in | ||
* [Arbitrum's developer documentation](https://developer.offchainlabs.com/docs/useful_addresses). | ||
* | ||
* _Available since v4.6._ | ||
*/ | ||
abstract contract CrossChainEnabledArbitrumL1 is CrossChainEnabled { | ||
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable | ||
address private immutable _bridge; | ||
|
||
/// @custom:oz-upgrades-unsafe-allow constructor | ||
constructor(address bridge) { | ||
_bridge = bridge; | ||
} | ||
|
||
/** | ||
* @dev see {CrossChainEnabled-_isCrossChain} | ||
*/ | ||
function _isCrossChain() internal view virtual override returns (bool) { | ||
return LibArbitrumL1.isCrossChain(_bridge); | ||
} | ||
|
||
/** | ||
* @dev see {CrossChainEnabled-_crossChainSender} | ||
*/ | ||
function _crossChainSender() internal view virtual override onlyCrossChain returns (address) { | ||
return LibArbitrumL1.crossChainSender(_bridge); | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
contracts/crosschain/arbitrum/CrossChainEnabledArbitrumL2.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.4; | ||
|
||
import "../CrossChainEnabled.sol"; | ||
import "./LibArbitrumL2.sol"; | ||
|
||
/** | ||
* @dev [Arbitrum](https://arbitrum.io/) specialization or the | ||
* {CrossChainEnabled} abstraction the L2 side (arbitrum). | ||
* | ||
* This version should only be deployed on L2 to process cross-chain messages | ||
* originating from L1. For the other side, use {CrossChainEnabledArbitrumL1}. | ||
* | ||
* Arbitrum L2 includes the `ArbSys` contract at a fixed address. Therefore, | ||
* this specialization of {CrossChainEnabled} does not include a constructor. | ||
* | ||
* _Available since v4.6._ | ||
*/ | ||
abstract contract CrossChainEnabledArbitrumL2 is CrossChainEnabled { | ||
/** | ||
* @dev see {CrossChainEnabled-_isCrossChain} | ||
*/ | ||
function _isCrossChain() internal view virtual override returns (bool) { | ||
return LibArbitrumL2.isCrossChain(LibArbitrumL2.ARBSYS); | ||
} | ||
|
||
/** | ||
* @dev see {CrossChainEnabled-_crossChainSender} | ||
*/ | ||
function _crossChainSender() internal view virtual override onlyCrossChain returns (address) { | ||
return LibArbitrumL2.crossChainSender(LibArbitrumL2.ARBSYS); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.4; | ||
|
||
import {IBridge as ArbitrumL1_Bridge} from "../../vendor/arbitrum/IBridge.sol"; | ||
import {IInbox as ArbitrumL1_Inbox} from "../../vendor/arbitrum/IInbox.sol"; | ||
import {IOutbox as ArbitrumL1_Outbox} from "../../vendor/arbitrum/IOutbox.sol"; | ||
import "../errors.sol"; | ||
|
||
/** | ||
* @dev Primitives for cross-chain aware contracts for | ||
* [Arbitrum](https://arbitrum.io/). | ||
* | ||
* This version should only be used on L1 to process cross-chain messages | ||
* originating from L2. For the other side, use {LibArbitrumL2}. | ||
*/ | ||
library LibArbitrumL1 { | ||
/** | ||
* @dev Returns whether the current function call is the result of a | ||
* cross-chain message relayed by the `bridge`. | ||
*/ | ||
function isCrossChain(address bridge) internal view returns (bool) { | ||
return msg.sender == bridge; | ||
} | ||
|
||
/** | ||
* @dev Returns the address of the sender that triggered the current | ||
* cross-chain message through the `bridge`. | ||
* | ||
* NOTE: {isCrossChain} should be checked before trying to recover the | ||
* sender, as it will revert with `NotCrossChainCall` if the current | ||
* function call is not the result of a cross-chain message. | ||
*/ | ||
function crossChainSender(address bridge) internal view returns (address) { | ||
if (!isCrossChain(bridge)) revert NotCrossChainCall(); | ||
|
||
address sender = ArbitrumL1_Outbox(ArbitrumL1_Bridge(bridge).activeOutbox()).l2ToL1Sender(); | ||
require(sender != address(0), "LibArbitrumL1: system messages without sender"); | ||
|
||
return sender; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.4; | ||
|
||
import {IArbSys as ArbitrumL2_Bridge} from "../../vendor/arbitrum/IArbSys.sol"; | ||
import "../errors.sol"; | ||
|
||
/** | ||
* @dev Primitives for cross-chain aware contracts for | ||
* [Arbitrum](https://arbitrum.io/). | ||
* | ||
* This version should only be used on L2 to process cross-chain messages | ||
* originating from L1. For the other side, use {LibArbitrumL1}. | ||
*/ | ||
library LibArbitrumL2 { | ||
/** | ||
* @dev Returns whether the current function call is the result of a | ||
* cross-chain message relayed by `arbsys`. | ||
*/ | ||
address public constant ARBSYS = 0x0000000000000000000000000000000000000064; | ||
|
||
function isCrossChain(address arbsys) internal view returns (bool) { | ||
return ArbitrumL2_Bridge(arbsys).isTopLevelCall(); | ||
} | ||
|
||
/** | ||
* @dev Returns the address of the sender that triggered the current | ||
* cross-chain message through `arbsys`. | ||
* | ||
* NOTE: {isCrossChain} should be checked before trying to recover the | ||
* sender, as it will revert with `NotCrossChainCall` if the current | ||
* function call is not the result of a cross-chain message. | ||
*/ | ||
function crossChainSender(address arbsys) internal view returns (address) { | ||
if (!isCrossChain(arbsys)) revert NotCrossChainCall(); | ||
|
||
return | ||
ArbitrumL2_Bridge(arbsys).wasMyCallersAddressAliased() | ||
? ArbitrumL2_Bridge(arbsys).myCallersAddressWithoutAliasing() | ||
: msg.sender; | ||
} | ||
} |
Oops, something went wrong.