generated from PaulRBerg/hardhat-template
-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #30 from gnosis/feat/optimism-bridge
feat: HeaderReporter, MessageRelay and Adapter for Optimism native bridge
- Loading branch information
Showing
10 changed files
with
242 additions
and
12 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
8 changes: 8 additions & 0 deletions
8
packages/evm/contracts/adapters/Optimism/ICrossDomainMessenger.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,8 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.17; | ||
|
||
interface ICrossDomainMessenger { | ||
function sendMessage(address _target, bytes calldata _message, uint32 _minGasLimit) external payable; | ||
|
||
function xDomainMessageSender() external view returns (address); | ||
} |
33 changes: 33 additions & 0 deletions
33
packages/evm/contracts/adapters/Optimism/L1CrossDomainMessengerHeaderReporter.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,33 @@ | ||
// SPDX-License-Identifier: LGPL-3.0-only | ||
pragma solidity ^0.8.17; | ||
|
||
import { ICrossDomainMessenger } from "./ICrossDomainMessenger.sol"; | ||
import { IHeaderStorage } from "../../interfaces/IHeaderStorage.sol"; | ||
|
||
contract L1CrossDomainMessengerHeaderReporter { | ||
// The first 1.92 million gas on L2 is free. See here: | ||
// https://community.optimism.io/docs/developers/bridge/messaging/#for-l1-%E2%87%92-l2-transactions | ||
uint32 internal constant GAS_LIMIT = 1_920_000; | ||
|
||
ICrossDomainMessenger public immutable l1CrossDomainMessenger; | ||
IHeaderStorage public immutable headerStorage; | ||
|
||
event HeaderReported(address indexed emitter, uint256 indexed blockNumber, bytes32 indexed blockHeader); | ||
|
||
constructor(ICrossDomainMessenger l1CrossDomainMessenger_, IHeaderStorage headerStorage_) { | ||
l1CrossDomainMessenger = l1CrossDomainMessenger_; | ||
headerStorage = headerStorage_; | ||
} | ||
|
||
/// @dev Reports the given block headers to the oracleAdapter via the L1CrossDomainMessenger. | ||
/// @param blockNumbers Uint256 array of block number to pass over the L1CrossDomainMessenger. | ||
/// @param adapter address of L2CrossDomainMessengerAdapter on the destination chain. | ||
function reportHeaders(uint256[] memory blockNumbers, address adapter) external payable { | ||
bytes32[] memory blockHeaders = headerStorage.storeBlockHeaders(blockNumbers); | ||
bytes memory message = abi.encodeWithSignature("storeHashes(uint256[],bytes32[])", blockNumbers, blockHeaders); | ||
l1CrossDomainMessenger.sendMessage(adapter, message, GAS_LIMIT); | ||
for (uint256 i = 0; i < blockNumbers.length; i++) { | ||
emit HeaderReported(address(this), blockNumbers[i], blockHeaders[i]); | ||
} | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
packages/evm/contracts/adapters/Optimism/L1CrossDomainMessengerMessageRelay.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,36 @@ | ||
// SPDX-License-Identifier: LGPL-3.0-only | ||
pragma solidity ^0.8.17; | ||
|
||
import { ICrossDomainMessenger } from "./ICrossDomainMessenger.sol"; | ||
import { IYaho } from "../../interfaces/IYaho.sol"; | ||
|
||
contract L1CrossDomainMessengerMessageRelay { | ||
// The first 1.92 million gas on L2 is free. See here: | ||
// https://community.optimism.io/docs/developers/bridge/messaging/#for-l1-%E2%87%92-l2-transactions | ||
uint32 internal constant GAS_LIMIT = 1_920_000; | ||
|
||
ICrossDomainMessenger public immutable l1CrossDomainMessenger; | ||
IYaho public immutable yaho; | ||
|
||
event MessageRelayed(address indexed emitter, uint256 indexed messageId); | ||
|
||
constructor(ICrossDomainMessenger l1CrossDomainMessenger_, IYaho yaho_) { | ||
l1CrossDomainMessenger = l1CrossDomainMessenger_; | ||
yaho = yaho_; | ||
} | ||
|
||
/// @dev Reports the given messages to the adapter via the L1CrossDomainMessenger. | ||
/// @param messageIds Uint256 array message ids to pass over the L1CrossDomainMessenger. | ||
/// @param adapter address of L2CrossDomainMessengerAdapter on the destination chain. | ||
function relayMessages(uint256[] memory messageIds, address adapter) external payable returns (bytes32 receipt) { | ||
bytes32[] memory hashes = new bytes32[](messageIds.length); | ||
for (uint256 i = 0; i < messageIds.length; i++) { | ||
uint256 id = messageIds[i]; | ||
hashes[i] = yaho.hashes(id); | ||
emit MessageRelayed(address(this), messageIds[i]); | ||
} | ||
bytes memory message = abi.encodeWithSignature("storeHashes(uint256[],bytes32[])", messageIds, hashes); | ||
l1CrossDomainMessenger.sendMessage{ value: msg.value }(adapter, message, GAS_LIMIT); | ||
return keccak256(abi.encode(true)); | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
packages/evm/contracts/adapters/Optimism/L2CrossDomainMessengerAdapter.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: LGPL-3.0-only | ||
pragma solidity ^0.8.17; | ||
|
||
import { ICrossDomainMessenger } from "./ICrossDomainMessenger.sol"; | ||
import { OracleAdapter } from "../OracleAdapter.sol"; | ||
import { BlockHashOracleAdapter } from "../BlockHashOracleAdapter.sol"; | ||
|
||
contract L2CrossDomainMessengerAdapter is OracleAdapter, BlockHashOracleAdapter { | ||
ICrossDomainMessenger public immutable l2CrossDomainMessenger; | ||
address public immutable reporter; | ||
uint256 public immutable chainId; | ||
|
||
error ArrayLengthMissmatch(address emitter); | ||
error UnauthorizedHashReporter(address emitter, address reporter); | ||
error UnauthorizedL2CrossDomainMessenger(address emitter, address sender); | ||
|
||
constructor(ICrossDomainMessenger l2CrossDomainMessenger_, address reporter_, uint256 chainId_) { | ||
l2CrossDomainMessenger = l2CrossDomainMessenger_; | ||
reporter = reporter_; | ||
chainId = chainId_; | ||
} | ||
|
||
/// @dev Check that the l2CrossDomainMessenger and xDomainMessageSender are valid. | ||
modifier onlyValid() { | ||
if (msg.sender != address(l2CrossDomainMessenger)) | ||
revert UnauthorizedL2CrossDomainMessenger(address(this), msg.sender); | ||
if (l2CrossDomainMessenger.xDomainMessageSender() != reporter) | ||
revert UnauthorizedHashReporter(address(this), reporter); | ||
_; | ||
} | ||
|
||
/// @dev Stores the hashes for a given array of ids. | ||
/// @param ids Array of ids number for which to set the hashes. | ||
/// @param hashes Array of hashes to set for the given ids. | ||
/// @notice Only callable by `l2CrossDomainMessenger` with a message passed from `reporter`. | ||
/// @notice Will revert if given array lengths do not match. | ||
function storeHashes(uint256[] memory ids, bytes32[] memory hashes) external onlyValid { | ||
if (ids.length != hashes.length) revert ArrayLengthMissmatch(address(this)); | ||
for (uint256 i = 0; i < ids.length; i++) { | ||
_storeHash(chainId, ids[i], hashes[i]); | ||
} | ||
} | ||
} |
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,12 @@ | ||
// SPDX-License-Identifier: LGPL-3.0-only | ||
pragma solidity ^0.8.17; | ||
|
||
interface IHeaderStorage { | ||
event HeaderStored(uint256 indexed blockNumber, bytes32 indexed blockHeader); | ||
|
||
error HeaderOutOfRange(address emitter, uint256 blockNumber); | ||
|
||
function storeBlockHeader(uint256 blockNumber) external returns (bytes32 blockHeader); | ||
|
||
function storeBlockHeaders(uint256[] memory blockNumbers) external returns (bytes32[] memory); | ||
} |
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,27 @@ | ||
// SPDX-License-Identifier: LGPL-3.0-only | ||
pragma solidity ^0.8.17; | ||
|
||
import { IMessageDispatcher, Message } from "./IMessageDispatcher.sol"; | ||
|
||
interface IYaho is IMessageDispatcher { | ||
error NoMessagesGiven(address emitter); | ||
error NoMessageIdsGiven(address emitter); | ||
error NoAdaptersGiven(address emitter); | ||
error UnequalArrayLengths(address emitter); | ||
|
||
function dispatchMessages(Message[] memory messages) external payable returns (bytes32[] memory); | ||
|
||
function relayMessagesToAdapters( | ||
uint256[] memory messageIds, | ||
address[] memory adapters, | ||
address[] memory destinationAdapters | ||
) external payable returns (bytes32[] memory); | ||
|
||
function dispatchMessagesToAdapters( | ||
Message[] memory messages, | ||
address[] memory adapters, | ||
address[] memory destinationAdapters | ||
) external payable returns (bytes32[] memory messageIds, bytes32[] memory); | ||
|
||
function hashes(uint256) external view returns (bytes32); | ||
} |
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
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,76 @@ | ||
import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers" | ||
import { task, types } from "hardhat/config" | ||
import type { TaskArguments } from "hardhat/types" | ||
|
||
import { verify } from "." | ||
import type { L1CrossDomainMessengerHeaderReporter } from "../../types/contracts/adapters/Optimism/L1CrossDomainMessengerHeaderReporter" | ||
import type { L1CrossDomainMessengerMessageRelay } from "../../types/contracts/adapters/Optimism/L1CrossDomainMessengerMessageRelay" | ||
import type { L2CrossDomainMessengerAdapter } from "../../types/contracts/adapters/Optimism/L2CrossDomainMessengerAdapter" | ||
import { L1CrossDomainMessengerHeaderReporter__factory } from "../../types/factories/contracts/adapters/Optimism/L1CrossDomainMessengerHeaderReporter__factory" | ||
import { L1CrossDomainMessengerMessageRelay__factory } from "../../types/factories/contracts/adapters/Optimism/L1CrossDomainMessengerMessageRelay__factory" | ||
import { L2CrossDomainMessengerAdapter__factory } from "../../types/factories/contracts/adapters/Optimism/L2CrossDomainMessengerAdapter__factory" | ||
|
||
// Deploy on destination chain | ||
task("deploy:Optimism:Adapter") | ||
.addParam("l2CrossDomainMessenger", "address of the L2CrossDomainMessenger contract", undefined, types.string) | ||
.addParam("reporter", "address of the hash reporter", undefined, types.string) | ||
.addParam("chainId", "chainId of the source chain", undefined, types.int) | ||
.addFlag("verify", "whether to verify the contract on Etherscan") | ||
.setAction(async function (taskArguments: TaskArguments, hre) { | ||
console.log("Deploying L2CrossDomainMessengerAdapter...") | ||
const signers: SignerWithAddress[] = await hre.ethers.getSigners() | ||
const l2CrossDomainMessengerAdapterFactory: L2CrossDomainMessengerAdapter__factory = < | ||
L2CrossDomainMessengerAdapter__factory | ||
>await hre.ethers.getContractFactory("L2CrossDomainMessengerAdapter") | ||
const constructorArguments = [ | ||
taskArguments.l2CrossDomainMessenger, | ||
taskArguments.reporter, | ||
taskArguments.chainId, | ||
] as const | ||
const l2CrossDomainMessengerAdapter: L2CrossDomainMessengerAdapter = <L2CrossDomainMessengerAdapter>( | ||
await l2CrossDomainMessengerAdapterFactory.connect(signers[0]).deploy(...constructorArguments) | ||
) | ||
await l2CrossDomainMessengerAdapter.deployed() | ||
console.log("L2CrossDomainMessengerAdapter deployed to:", l2CrossDomainMessengerAdapter.address) | ||
if (taskArguments.verify) await verify(hre, l2CrossDomainMessengerAdapter, constructorArguments) | ||
}) | ||
|
||
// Deploy source chain | ||
task("deploy:Optimism:HeaderReporter") | ||
.addParam("l1CrossDomainMessenger", "address of the L1CrossDomainMessenger contract", undefined, types.string) | ||
.addParam("headerStorage", "address of the header storage contract", undefined, types.string) | ||
.addFlag("verify", "whether to verify the contract on Etherscan") | ||
.setAction(async function (taskArguments: TaskArguments, hre) { | ||
console.log("Deploying L1CrossDomainMessengerHeaderReporter...") | ||
const signers: SignerWithAddress[] = await hre.ethers.getSigners() | ||
const l1CrossDomainMessengerHeaderReporterFactory: L1CrossDomainMessengerHeaderReporter__factory = < | ||
L1CrossDomainMessengerHeaderReporter__factory | ||
>await hre.ethers.getContractFactory("L1CrossDomainMessengerHeaderReporter") | ||
const constructorArguments = [taskArguments.l1CrossDomainMessenger, taskArguments.headerStorage] as const | ||
const l1CrossDomainMessengerHeaderReporter: L1CrossDomainMessengerHeaderReporter = < | ||
L1CrossDomainMessengerHeaderReporter | ||
>await l1CrossDomainMessengerHeaderReporterFactory.connect(signers[0]).deploy(...constructorArguments) | ||
await l1CrossDomainMessengerHeaderReporter.deployed() | ||
console.log("L1CrossDomainMessengerHeaderReporter deployed to:", l1CrossDomainMessengerHeaderReporter.address) | ||
if (taskArguments.verify) await verify(hre, l1CrossDomainMessengerHeaderReporter, constructorArguments) | ||
}) | ||
|
||
// Deploy source chain | ||
task("deploy:Optimism:MessageRelay") | ||
.addParam("l1CrossDomainMessenger", "address of the L1CrossDomainMessenger contract", undefined, types.string) | ||
.addParam("yaho", "address of the Yaho contract", undefined, types.string) | ||
.addFlag("verify", "whether to verify the contract on Etherscan") | ||
.setAction(async function (taskArguments: TaskArguments, hre) { | ||
console.log("Deploying L1CrossDomainMessengerMessageRelay...") | ||
const signers: SignerWithAddress[] = await hre.ethers.getSigners() | ||
const l1CrossDomainMessengerMessageRelayFactory: L1CrossDomainMessengerMessageRelay__factory = < | ||
L1CrossDomainMessengerMessageRelay__factory | ||
>await hre.ethers.getContractFactory("L1CrossDomainMessengerMessageRelay") | ||
const constructorArguments = [taskArguments.l1CrossDomainMessenger, taskArguments.yaho] as const | ||
const l1CrossDomainMessengerMessageRelay: L1CrossDomainMessengerMessageRelay = <L1CrossDomainMessengerMessageRelay>( | ||
await l1CrossDomainMessengerMessageRelayFactory.connect(signers[0]).deploy(...constructorArguments) | ||
) | ||
await l1CrossDomainMessengerMessageRelay.deployed() | ||
console.log("L1CrossDomainMessengerMessageRelay deployed to:", l1CrossDomainMessengerMessageRelay.address) | ||
if (taskArguments.verify) await verify(hre, l1CrossDomainMessengerMessageRelay, constructorArguments) | ||
}) |