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

Arbitrum support #39

Merged
merged 22 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
34 changes: 34 additions & 0 deletions script/Deploy_L1ToArbitrumMessageSender.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.9;

import "forge-std/Script.sol";

import {L1ToArbitrumMessagesSender} from "../src/core/x-rollup-messaging/L1ToArbitrumMessagesSender.sol";
import {IArbitrumInbox} from "../src/core/x-rollup-messaging/interfaces/IArbitrumInbox.sol";
import {IParentHashFetcher} from "../src/core/x-rollup-messaging/interfaces/IParentHashFetcher.sol";
import {ISharpProofsAggregatorsFactory} from "../src/core/interfaces/ISharpProofsAggregatorsFactory.sol";
import {console2} from "forge-std/console2.sol";

contract Deploy_L1ToArbitrumMessagesSender is Script {
function run() public {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");

address proofsAggregatorsFactory = vm.envAddress("PROOFS_AGGREGATORS_FACTORY");
address parentHashFetcher = vm.envAddress("PARENT_HASH_FETCHER");
address l2Target = vm.envAddress("L2_TARGET");
address arbitrumMessenger = vm.envAddress("ARBITRUM_INBOX");

vm.startBroadcast(deployerPrivateKey);

L1ToArbitrumMessagesSender l1ToArbitrumMessagesSender = new L1ToArbitrumMessagesSender(
ISharpProofsAggregatorsFactory(proofsAggregatorsFactory),
IParentHashFetcher(parentHashFetcher),
l2Target,
IArbitrumInbox(arbitrumMessenger)
);

console2.log("L1ToArbitrumMessagesSender address: %s", address(l1ToArbitrumMessagesSender));

vm.stopBroadcast();
}
}
4 changes: 2 additions & 2 deletions src/core/x-rollup-messaging/AbstractMessagesSender.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ abstract contract AbstractMessagesSender {
/// @param _parentHashFetcherCtx ABI encoded context for the parent hash fetcher
/// @param _xDomainMsgGasData the gas data for the cross-domain message, depends on the destination L2
function sendExactParentHashToL2(bytes calldata _parentHashFetcherCtx, bytes calldata _xDomainMsgGasData)
external
external payable
{
(uint256 parentHashFetchedForBlock, bytes32 parentHash) =
parentHashFetcher.fetchParentHash(_parentHashFetcherCtx);
Expand All @@ -36,7 +36,7 @@ abstract contract AbstractMessagesSender {
);
}

function sendKeccakMMRTreeToL2(uint256 aggregatorId, bytes calldata _xDomainMsgGasData) external {
function sendKeccakMMRTreeToL2(uint256 aggregatorId, bytes calldata _xDomainMsgGasData) external payable {
address existingAggregatorAddr = proofsAggregatorsFactory.aggregatorsById(aggregatorId);
require(existingAggregatorAddr != address(0), "Unknown aggregator");
ISharpProofsAggregator aggregator = ISharpProofsAggregator(existingAggregatorAddr);
Expand Down
28 changes: 28 additions & 0 deletions src/core/x-rollup-messaging/L1ToArbitrumMessagesSender.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;

import {AbstractMessagesSender} from "./AbstractMessagesSender.sol";
import {IArbitrumInbox} from "./interfaces/IArbitrumInbox.sol";
import {ISharpProofsAggregatorsFactory} from "../interfaces/ISharpProofsAggregatorsFactory.sol";
import {IParentHashFetcher} from "./interfaces/IParentHashFetcher.sol";

contract L1ToArbitrumMessagesSender is AbstractMessagesSender {
IArbitrumInbox public immutable arbitrumInbox;

constructor(
ISharpProofsAggregatorsFactory _proofsAggregatorsFactory,
IParentHashFetcher _parentHashFetcher,
address _l2Target,
IArbitrumInbox _arbitrumInbox
) AbstractMessagesSender(_proofsAggregatorsFactory, _parentHashFetcher, _l2Target) {
arbitrumInbox = _arbitrumInbox;
}

function _sendMessage(address _l2Target, bytes memory _data, bytes memory _xDomainMsgGasData) internal override {
(uint256 l2GasLimit, uint256 maxFeePerGas, uint256 maxSubmissionCost) =
abi.decode(_xDomainMsgGasData, (uint256, uint256, uint256));
arbitrumInbox.createRetryableTicket(
_l2Target, 0, maxSubmissionCost, msg.sender, address(0), l2GasLimit, maxFeePerGas, _data
);
}
}
29 changes: 29 additions & 0 deletions src/core/x-rollup-messaging/interfaces/IArbitrumInbox.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;

interface IArbitrumInbox {
/**
* @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts
* @dev all msg.value will deposited to callValueRefundAddress on L2
* @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error
* @param to destination L2 contract address
* @param l2CallValue call value for retryable L2 message
* @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee
* @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance
* @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled
* @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
* @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
* @param data ABI encoded data of L2 message
* @return unique message number of the retryable transaction
*/
function createRetryableTicket(
address to,
uint256 l2CallValue,
uint256 maxSubmissionCost,
address excessFeeRefundAddress,
address callValueRefundAddress,
uint256 gasLimit,
uint256 maxFeePerGas,
bytes calldata data
) external payable returns (uint256);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;

import {IParentHashFetcher} from "../interfaces/IParentHashFetcher.sol";
import {IOutbox} from "./interfaces/IOutbox.sol";
import {EVMHeaderRLP} from "../../../lib/EVMHeaderRLP.sol";

/// @title ArbitrumParentHashesFetcher
/// @notice Fetches parent hashes for the an Arbitrum chain, settling to the chain where this contract is deployed
/// @notice for example if deployed on Ethereum, it will fetch parent hashes for Arbitrum
contract ArbitrumParentHashesFetcher is IParentHashFetcher {
using EVMHeaderRLP for bytes;

IOutbox public immutable outbox;
uint256 public immutable chainId;

constructor(IOutbox _outbox, uint256 _chainId) {
outbox = _outbox;
chainId = _chainId;
}

function fetchParentHash(bytes memory ctx)
external
view
override
returns (uint256 fetchedForBlock, bytes32 parentHash)
{
(bytes32 outputRoot, bytes memory rlpHeader) = abi.decode(ctx, (bytes32, bytes));
// Get the block hash from the outbox
bytes32 l2BlockHash = outbox.roots(outputRoot);
require(l2BlockHash != bytes32(0), "ERR_INVALID_OUTPUT_ROOT");
// Validate the header against the parent hash
require(keccak256(rlpHeader) == l2BlockHash, "ERR_INVALID_HEADER");
// Get the block number from the header
uint256 l2BlockNumber = rlpHeader.getBlockNumber();
fetchedForBlock = l2BlockNumber + 1;
parentHash = l2BlockHash;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import {IL2OutputOracle} from "./interfaces/IL2OutputOracle.sol";
/// @notice for example if deployed on Ethereum, it will fetch parent hashes for Optimism/Base
contract OpStackParentHashesFetcher is IParentHashFetcher {
IL2OutputOracle public immutable l2OutputOracle;
uint256 public immutable chainId;

constructor(IL2OutputOracle _l2OutputOracle) {
constructor(IL2OutputOracle _l2OutputOracle, uint256 _chainId) {
l2OutputOracle = _l2OutputOracle;
chainId = _chainId;
}

function fetchParentHash(bytes memory ctx)
Expand All @@ -36,8 +38,4 @@ contract OpStackParentHashesFetcher is IParentHashFetcher {
fetchedForBlock = outputProposal.l2BlockNumber + 1;
parentHash = latestBlockhash;
}

function chainId() external view override returns (uint256) {
return block.chainid;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;

interface IOutbox {
function roots(bytes32) external view returns (bytes32); // maps root hashes => L2 block hash
}