diff --git a/.github/workflows/test.yml b/.github/workflows/test.yaml similarity index 100% rename from .github/workflows/test.yml rename to .github/workflows/test.yaml diff --git a/script/Deploy_L1ToArbitrumMessageSender.s.sol b/script/Deploy_L1ToArbitrumMessageSender.s.sol new file mode 100644 index 0000000..32043ae --- /dev/null +++ b/script/Deploy_L1ToArbitrumMessageSender.s.sol @@ -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/outbox/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(); + } +} diff --git a/script/Deploy_OpStackParentHashesFetcher.s.sol b/script/Deploy_OpStackParentHashesFetcher.s.sol index ebb389a..35874a5 100644 --- a/script/Deploy_OpStackParentHashesFetcher.s.sol +++ b/script/Deploy_OpStackParentHashesFetcher.s.sol @@ -15,7 +15,7 @@ contract Deploy_OpStackParentHashesFetcher is Script { address l2OutputOracle = vm.envAddress("L2_OUTPUT_ORACLE"); - OpStackParentHashesFetcher opStackParentHashesFetcher = new OpStackParentHashesFetcher(IL2OutputOracle(l2OutputOracle)); + OpStackParentHashesFetcher opStackParentHashesFetcher = new OpStackParentHashesFetcher(IL2OutputOracle(l2OutputOracle), 11155111); console2.log("OpStackParentHashesFetcher address: %s", address(opStackParentHashesFetcher)); diff --git a/src/core/x-rollup-messaging/interfaces/IArbitrumInbox.sol b/src/core/x-rollup-messaging/interfaces/IArbitrumInbox.sol new file mode 100644 index 0000000..4541dbc --- /dev/null +++ b/src/core/x-rollup-messaging/interfaces/IArbitrumInbox.sol @@ -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); +} diff --git a/src/core/x-rollup-messaging/outbox/L1ToArbitrumMessagesSender.sol b/src/core/x-rollup-messaging/outbox/L1ToArbitrumMessagesSender.sol new file mode 100644 index 0000000..86b072e --- /dev/null +++ b/src/core/x-rollup-messaging/outbox/L1ToArbitrumMessagesSender.sol @@ -0,0 +1,25 @@ +// 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); + } +} diff --git a/src/core/x-rollup-messaging/parent-hashes-fetchers/OpStackParentHashesFetcher.sol b/src/core/x-rollup-messaging/parent-hashes-fetchers/OpStackParentHashesFetcher.sol index d4cb90c..f8f1e39 100644 --- a/src/core/x-rollup-messaging/parent-hashes-fetchers/OpStackParentHashesFetcher.sol +++ b/src/core/x-rollup-messaging/parent-hashes-fetchers/OpStackParentHashesFetcher.sol @@ -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) @@ -36,8 +38,4 @@ contract OpStackParentHashesFetcher is IParentHashFetcher { fetchedForBlock = outputProposal.l2BlockNumber + 1; parentHash = latestBlockhash; } - - function chainId() external view override returns (uint256) { - return block.chainid; - } } diff --git a/src/core/x-rollup-messaging/parent-hashes-fetchers/interfaces/IOutbox.sol b/src/core/x-rollup-messaging/parent-hashes-fetchers/interfaces/IOutbox.sol new file mode 100644 index 0000000..d02ee7e --- /dev/null +++ b/src/core/x-rollup-messaging/parent-hashes-fetchers/interfaces/IOutbox.sol @@ -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 +}