From b6ce510c899f1d582b03dade9222e2db8c81f20e Mon Sep 17 00:00:00 2001 From: Sam MacPherson Date: Wed, 3 Jul 2024 00:42:05 +0900 Subject: [PATCH] refactor integration tests to make it easier to add many domains (#22) --- test/ArbitrumOneCrosschainTest.t.sol | 42 +++------- test/BaseChainCrosschainTest.t.sol | 40 ++------- test/CrosschainTestBase.sol | 92 +++++++++------------ test/GnosisCrosschainTest.t.sol | 38 ++------- test/OptimismCrosschainTest.t.sol | 42 +++------- test/interfaces/IL1Executor.sol | 9 -- test/mocks/PayloadWithEmit.sol | 2 +- test/mocks/ReconfigurationPayload.sol | 2 +- test/payloads/ArbitrumCrosschainPayload.sol | 27 ++++++ test/payloads/CrosschainPayload.sol | 42 ++++++++++ test/payloads/GnosisCrosschainPayload.sol | 21 +++++ test/{interfaces => payloads}/IPayload.sol | 2 - test/payloads/OptimismCrosschainPayload.sol | 25 ++++++ 13 files changed, 195 insertions(+), 189 deletions(-) delete mode 100644 test/interfaces/IL1Executor.sol create mode 100644 test/payloads/ArbitrumCrosschainPayload.sol create mode 100644 test/payloads/CrosschainPayload.sol create mode 100644 test/payloads/GnosisCrosschainPayload.sol rename test/{interfaces => payloads}/IPayload.sol (98%) create mode 100644 test/payloads/OptimismCrosschainPayload.sol diff --git a/test/ArbitrumOneCrosschainTest.t.sol b/test/ArbitrumOneCrosschainTest.t.sol index c8afd68..2a88bf8 100644 --- a/test/ArbitrumOneCrosschainTest.t.sol +++ b/test/ArbitrumOneCrosschainTest.t.sol @@ -7,23 +7,7 @@ import { ArbitrumBridgeTesting } from 'lib/xchain-helpers/src/testing/bridges/Ar import { ArbitrumForwarder } from 'lib/xchain-helpers/src/forwarders/ArbitrumForwarder.sol'; import { ArbitrumReceiver } from 'lib/xchain-helpers/src/receivers/ArbitrumReceiver.sol'; -contract ArbitrumOneCrosschainPayload is CrosschainPayload { - - constructor(IPayload _targetPayload, address _bridgeReceiver) - CrosschainPayload(_targetPayload, _bridgeReceiver) {} - - function execute() external override { - ArbitrumForwarder.sendMessageL1toL2( - ArbitrumForwarder.L1_CROSS_DOMAIN_ARBITRUM_ONE, - bridgeReceiver, - encodeCrosschainExecutionMessage(), - 1_000_000, - 1 gwei, - block.basefee + 10 gwei - ); - } - -} +import { ArbitrumCrosschainPayload } from './payloads/ArbitrumCrosschainPayload.sol'; contract ArbitrumOneCrosschainTest is CrosschainTestBase { @@ -31,31 +15,25 @@ contract ArbitrumOneCrosschainTest is CrosschainTestBase { using ArbitrumBridgeTesting for *; function deployCrosschainPayload(IPayload targetPayload, address bridgeReceiver) - public override returns (IPayload) + internal override returns (IPayload) { - return IPayload(new ArbitrumOneCrosschainPayload(targetPayload, bridgeReceiver)); + return IPayload(new ArbitrumCrosschainPayload(ArbitrumForwarder.L1_CROSS_DOMAIN_ARBITRUM_ONE, targetPayload, bridgeReceiver)); } - function setUp() public { + function setupDomain() internal override { + remote = getChain('arbitrum_one').createFork(); bridge = ArbitrumBridgeTesting.createNativeBridge( - getChain('mainnet').createFork(), - getChain('arbitrum_one').createFork() + mainnet, + remote ); - bridge.destination.selectFork(); - bridgeExecutor = new Executor( - defaultL2BridgeExecutorArgs.delay, - defaultL2BridgeExecutorArgs.gracePeriod - ); + remote.selectFork(); bridgeReceiver = address(new ArbitrumReceiver( defaultL2BridgeExecutorArgs.ethereumGovernanceExecutor, - address(bridgeExecutor) + vm.computeCreateAddress(address(this), 3) )); - bridgeExecutor.grantRole(bridgeExecutor.SUBMISSION_ROLE(), bridgeReceiver); - bridgeExecutor.grantRole(bridgeExecutor.GUARDIAN_ROLE(), defaultL2BridgeExecutorArgs.guardian); - bridgeExecutor.revokeRole(bridgeExecutor.DEFAULT_ADMIN_ROLE(), address(this)); - bridge.source.selectFork(); + mainnet.selectFork(); vm.deal(L1_EXECUTOR, 0.01 ether); } diff --git a/test/BaseChainCrosschainTest.t.sol b/test/BaseChainCrosschainTest.t.sol index ec4169c..1a8a9de 100644 --- a/test/BaseChainCrosschainTest.t.sol +++ b/test/BaseChainCrosschainTest.t.sol @@ -7,21 +7,7 @@ import { OptimismBridgeTesting } from 'lib/xchain-helpers/src/testing/bridges/Op import { OptimismForwarder } from 'lib/xchain-helpers/src/forwarders/OptimismForwarder.sol'; import { OptimismReceiver } from 'lib/xchain-helpers/src/receivers/OptimismReceiver.sol'; -contract BaseChainCrosschainPayload is CrosschainPayload { - - constructor(IPayload _targetPayload, address _bridgeReceiver) - CrosschainPayload(_targetPayload, _bridgeReceiver) {} - - function execute() external override { - OptimismForwarder.sendMessageL1toL2( - OptimismForwarder.L1_CROSS_DOMAIN_BASE, - bridgeReceiver, - encodeCrosschainExecutionMessage(), - 1_000_000 - ); - } - -} +import { OptimismCrosschainPayload } from './payloads/OptimismCrosschainPayload.sol'; contract BaseChainCrosschainTest is CrosschainTestBase { @@ -29,31 +15,23 @@ contract BaseChainCrosschainTest is CrosschainTestBase { using OptimismBridgeTesting for *; function deployCrosschainPayload(IPayload targetPayload, address bridgeReceiver) - public override returns (IPayload) + internal override returns (IPayload) { - return IPayload(new BaseChainCrosschainPayload(targetPayload, bridgeReceiver)); + return IPayload(new OptimismCrosschainPayload(OptimismForwarder.L1_CROSS_DOMAIN_BASE, targetPayload, bridgeReceiver)); } - function setUp() public { + function setupDomain() internal override { + remote = getChain('base').createFork(); bridge = OptimismBridgeTesting.createNativeBridge( - getChain('mainnet').createFork(), - getChain('base').createFork() + mainnet, + remote ); - bridge.destination.selectFork(); - bridgeExecutor = new Executor( - defaultL2BridgeExecutorArgs.delay, - defaultL2BridgeExecutorArgs.gracePeriod - ); + remote.selectFork(); bridgeReceiver = address(new OptimismReceiver( defaultL2BridgeExecutorArgs.ethereumGovernanceExecutor, - address(bridgeExecutor) + vm.computeCreateAddress(address(this), 2) )); - bridgeExecutor.grantRole(bridgeExecutor.SUBMISSION_ROLE(), bridgeReceiver); - bridgeExecutor.grantRole(bridgeExecutor.GUARDIAN_ROLE(), defaultL2BridgeExecutorArgs.guardian); - bridgeExecutor.revokeRole(bridgeExecutor.DEFAULT_ADMIN_ROLE(), address(this)); - - bridge.source.selectFork(); } function relayMessagesAcrossBridge() internal override { diff --git a/test/CrosschainTestBase.sol b/test/CrosschainTestBase.sol index ebb211c..22f778a 100644 --- a/test/CrosschainTestBase.sol +++ b/test/CrosschainTestBase.sol @@ -3,18 +3,25 @@ pragma solidity ^0.8.0; import 'forge-std/Test.sol'; +import { Domain } from 'lib/xchain-helpers/src/testing/Domain.sol'; import { Bridge } from 'lib/xchain-helpers/src/testing/Bridge.sol'; import { DomainHelpers } from 'lib/xchain-helpers/src/testing/Domain.sol'; import { IExecutor } from 'src/interfaces/IExecutor.sol'; import { Executor } from 'src/Executor.sol'; -import { IL1Executor } from './interfaces/IL1Executor.sol'; -import { IPayload } from './interfaces/IPayload.sol'; +import { IPayload } from './payloads/IPayload.sol'; import { PayloadWithEmit } from './mocks/PayloadWithEmit.sol'; import { ReconfigurationPayload } from './mocks/ReconfigurationPayload.sol'; +interface IL1Executor { + function exec(address target, bytes calldata args) + external + payable + returns (bytes memory out); +} + struct L2BridgeExecutorArguments { address ethereumGovernanceExecutor; uint256 delay; @@ -22,73 +29,56 @@ struct L2BridgeExecutorArguments { address guardian; } -abstract contract CrosschainPayload is IPayload { - - IPayload immutable targetPayload; - address immutable bridgeReceiver; - - constructor(IPayload _targetPayload, address _bridgeReceiver) { - targetPayload = _targetPayload; - bridgeReceiver = _bridgeReceiver; - } - - function execute() external virtual; - - function encodeCrosschainExecutionMessage() internal view returns (bytes memory) { - address[] memory targets = new address[](1); - targets[0] = address(targetPayload); - uint256[] memory values = new uint256[](1); - values[0] = 0; - string[] memory signatures = new string[](1); - signatures[0] = 'execute()'; - bytes[] memory calldatas = new bytes[](1); - calldatas[0] = ''; - bool[] memory withDelegatecalls = new bool[](1); - withDelegatecalls[0] = true; - - return abi.encodeWithSelector( - IExecutor.queue.selector, - targets, - values, - signatures, - calldatas, - withDelegatecalls - ); - } - -} - abstract contract CrosschainTestBase is Test { using DomainHelpers for *; event TestEvent(); - address public constant L1_EXECUTOR = 0x3300f198988e4C9C63F75dF86De36421f06af8c4; - address public constant L1_PAUSE_PROXY = 0xBE8E3e3618f7474F8cB1d074A26afFef007E98FB; - address public GUARDIAN = makeAddr('guardian'); + address constant L1_EXECUTOR = 0x3300f198988e4C9C63F75dF86De36421f06af8c4; + address constant L1_PAUSE_PROXY = 0xBE8E3e3618f7474F8cB1d074A26afFef007E98FB; + address GUARDIAN = makeAddr('guardian'); - L2BridgeExecutorArguments public defaultL2BridgeExecutorArgs = L2BridgeExecutorArguments({ + L2BridgeExecutorArguments defaultL2BridgeExecutorArgs = L2BridgeExecutorArguments({ ethereumGovernanceExecutor: L1_EXECUTOR, delay: 600, gracePeriod: 1200, guardian: GUARDIAN }); - Bridge public bridge; + Domain mainnet; + Domain remote; + Bridge bridge; + + Executor bridgeExecutor; + address bridgeReceiver; + + function setUp() public { + mainnet = getChain("mainnet").createFork(); + + setupDomain(); + + remote.selectFork(); - Executor public bridgeExecutor; - address public bridgeReceiver; + bridgeExecutor = new Executor( + defaultL2BridgeExecutorArgs.delay, + defaultL2BridgeExecutorArgs.gracePeriod + ); + bridgeExecutor.grantRole(bridgeExecutor.SUBMISSION_ROLE(), bridgeReceiver); + bridgeExecutor.grantRole(bridgeExecutor.GUARDIAN_ROLE(), defaultL2BridgeExecutorArgs.guardian); + bridgeExecutor.revokeRole(bridgeExecutor.DEFAULT_ADMIN_ROLE(), address(this)); + } - function deployCrosschainPayload(IPayload targetPayload, address bridgeReceiver) public virtual returns (IPayload); + function deployCrosschainPayload(IPayload targetPayload, address bridgeReceiver) internal virtual returns (IPayload); function relayMessagesAcrossBridge() internal virtual; + function setupDomain() internal virtual; - function preparePayloadExecution() public { - bridge.destination.selectFork(); + function preparePayloadExecution() internal { + remote.selectFork(); IPayload targetPayload = IPayload(new PayloadWithEmit()); - bridge.source.selectFork(); + mainnet.selectFork(); IPayload crosschainPayload = deployCrosschainPayload( targetPayload, @@ -304,7 +294,7 @@ abstract contract CrosschainTestBase is Test { } function test_selfReconfiguration() public { - bridge.destination.selectFork(); + remote.selectFork(); L2BridgeExecutorArguments memory newL2BridgeExecutorParams = L2BridgeExecutorArguments({ ethereumGovernanceExecutor: defaultL2BridgeExecutorArgs.ethereumGovernanceExecutor, @@ -337,7 +327,7 @@ abstract contract CrosschainTestBase is Test { newL2BridgeExecutorParams.guardian )); - bridge.source.selectFork(); + mainnet.selectFork(); IPayload crosschainPayload = deployCrosschainPayload( reconfigurationPayload, diff --git a/test/GnosisCrosschainTest.t.sol b/test/GnosisCrosschainTest.t.sol index e194e66..5d637be 100644 --- a/test/GnosisCrosschainTest.t.sol +++ b/test/GnosisCrosschainTest.t.sol @@ -4,23 +4,9 @@ pragma solidity ^0.8.0; import './CrosschainTestBase.sol'; import { AMBBridgeTesting } from 'lib/xchain-helpers/src/testing/bridges/AMBBridgeTesting.sol'; -import { AMBForwarder } from 'lib/xchain-helpers/src/forwarders/AMBForwarder.sol'; import { AMBReceiver } from 'lib/xchain-helpers/src/receivers/AMBReceiver.sol'; -contract GnosisCrosschainPayload is CrosschainPayload { - - constructor(IPayload _targetPayload, address _bridgeReceiver) - CrosschainPayload(_targetPayload, _bridgeReceiver) {} - - function execute() external override { - AMBForwarder.sendMessageEthereumToGnosisChain( - bridgeReceiver, - encodeCrosschainExecutionMessage(), - 1_000_000 - ); - } - -} +import { GnosisCrosschainPayload } from './payloads/GnosisCrosschainPayload.sol'; contract GnosisCrosschainTest is CrosschainTestBase { @@ -28,33 +14,25 @@ contract GnosisCrosschainTest is CrosschainTestBase { using AMBBridgeTesting for *; function deployCrosschainPayload(IPayload targetPayload, address bridgeReceiver) - public override returns (IPayload) + internal override returns (IPayload) { return IPayload(new GnosisCrosschainPayload(targetPayload, bridgeReceiver)); } - function setUp() public { + function setupDomain() internal override { + remote = getChain('gnosis_chain').createFork(); bridge = AMBBridgeTesting.createGnosisBridge( - getChain('mainnet').createFork(), - getChain('gnosis_chain').createFork() + mainnet, + remote ); - bridge.destination.selectFork(); - bridgeExecutor = new Executor( - defaultL2BridgeExecutorArgs.delay, - defaultL2BridgeExecutorArgs.gracePeriod - ); + remote.selectFork(); bridgeReceiver = address(new AMBReceiver( AMBBridgeTesting.getGnosisMessengerFromChainAlias(bridge.destination.chain.chainAlias), bytes32(uint256(1)), // Ethereum chainid defaultL2BridgeExecutorArgs.ethereumGovernanceExecutor, - address(bridgeExecutor) + vm.computeCreateAddress(address(this), 2) )); - bridgeExecutor.grantRole(bridgeExecutor.SUBMISSION_ROLE(), bridgeReceiver); - bridgeExecutor.grantRole(bridgeExecutor.GUARDIAN_ROLE(), defaultL2BridgeExecutorArgs.guardian); - bridgeExecutor.revokeRole(bridgeExecutor.DEFAULT_ADMIN_ROLE(), address(this)); - - bridge.source.selectFork(); } function relayMessagesAcrossBridge() internal override { diff --git a/test/OptimismCrosschainTest.t.sol b/test/OptimismCrosschainTest.t.sol index a3a6bec..78532fa 100644 --- a/test/OptimismCrosschainTest.t.sol +++ b/test/OptimismCrosschainTest.t.sol @@ -7,53 +7,31 @@ import { OptimismBridgeTesting } from 'lib/xchain-helpers/src/testing/bridges/Op import { OptimismForwarder } from 'lib/xchain-helpers/src/forwarders/OptimismForwarder.sol'; import { OptimismReceiver } from 'lib/xchain-helpers/src/receivers/OptimismReceiver.sol'; -contract OptimismCrosschainPayload is CrosschainPayload { +import { OptimismCrosschainPayload } from './payloads/OptimismCrosschainPayload.sol'; - constructor(IPayload _targetPayload, address _bridgeReceiver) - CrosschainPayload(_targetPayload, _bridgeReceiver) {} - - function execute() external override { - OptimismForwarder.sendMessageL1toL2( - OptimismForwarder.L1_CROSS_DOMAIN_OPTIMISM, - bridgeReceiver, - encodeCrosschainExecutionMessage(), - 1_000_000 - ); - } - -} - -contract OptimismCrosschainTest is CrosschainTestBase { +contract BaseChainCrosschainTest is CrosschainTestBase { using DomainHelpers for *; using OptimismBridgeTesting for *; function deployCrosschainPayload(IPayload targetPayload, address bridgeReceiver) - public override returns (IPayload) + internal override returns (IPayload) { - return IPayload(new OptimismCrosschainPayload(targetPayload, bridgeReceiver)); + return IPayload(new OptimismCrosschainPayload(OptimismForwarder.L1_CROSS_DOMAIN_OPTIMISM, targetPayload, bridgeReceiver)); } - function setUp() public { + function setupDomain() internal override { + remote = getChain('optimism').createFork(); bridge = OptimismBridgeTesting.createNativeBridge( - getChain('mainnet').createFork(), - getChain('optimism').createFork() + mainnet, + remote ); - bridge.destination.selectFork(); - bridgeExecutor = new Executor( - defaultL2BridgeExecutorArgs.delay, - defaultL2BridgeExecutorArgs.gracePeriod - ); + remote.selectFork(); bridgeReceiver = address(new OptimismReceiver( defaultL2BridgeExecutorArgs.ethereumGovernanceExecutor, - address(bridgeExecutor) + vm.computeCreateAddress(address(this), 2) )); - bridgeExecutor.grantRole(bridgeExecutor.SUBMISSION_ROLE(), bridgeReceiver); - bridgeExecutor.grantRole(bridgeExecutor.GUARDIAN_ROLE(), defaultL2BridgeExecutorArgs.guardian); - bridgeExecutor.revokeRole(bridgeExecutor.DEFAULT_ADMIN_ROLE(), address(this)); - - bridge.source.selectFork(); } function relayMessagesAcrossBridge() internal override { diff --git a/test/interfaces/IL1Executor.sol b/test/interfaces/IL1Executor.sol deleted file mode 100644 index 4362916..0000000 --- a/test/interfaces/IL1Executor.sol +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0 -pragma solidity ^0.8.0; - -interface IL1Executor { - function exec(address target, bytes calldata args) - external - payable - returns (bytes memory out); -} diff --git a/test/mocks/PayloadWithEmit.sol b/test/mocks/PayloadWithEmit.sol index 5ed6771..654ef98 100644 --- a/test/mocks/PayloadWithEmit.sol +++ b/test/mocks/PayloadWithEmit.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.10; -import { IPayload } from '../interfaces/IPayload.sol'; +import { IPayload } from '../payloads/IPayload.sol'; /** * @dev This payload simply emits an event on execution diff --git a/test/mocks/ReconfigurationPayload.sol b/test/mocks/ReconfigurationPayload.sol index 7fcd178..94a6d40 100644 --- a/test/mocks/ReconfigurationPayload.sol +++ b/test/mocks/ReconfigurationPayload.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.10; import { IExecutor } from 'src/interfaces/IExecutor.sol'; -import { IPayload } from '../interfaces/IPayload.sol'; +import { IPayload } from '../payloads/IPayload.sol'; /** * @dev This payload reconfigures bridge executor to a given state diff --git a/test/payloads/ArbitrumCrosschainPayload.sol b/test/payloads/ArbitrumCrosschainPayload.sol new file mode 100644 index 0000000..ba12eca --- /dev/null +++ b/test/payloads/ArbitrumCrosschainPayload.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.0; + +import { ArbitrumForwarder } from 'lib/xchain-helpers/src/forwarders/ArbitrumForwarder.sol'; + +import { CrosschainPayload, IPayload } from './CrosschainPayload.sol'; + +contract ArbitrumCrosschainPayload is CrosschainPayload { + + address public immutable l1CrossDomain; + + constructor(address _l1CrossDomain, IPayload _targetPayload, address _bridgeReceiver) CrosschainPayload(_targetPayload, _bridgeReceiver) { + l1CrossDomain = _l1CrossDomain; + } + + function execute() external override { + ArbitrumForwarder.sendMessageL1toL2( + l1CrossDomain, + bridgeReceiver, + encodeCrosschainExecutionMessage(), + 1_000_000, + 1 gwei, + block.basefee + 10 gwei + ); + } + +} diff --git a/test/payloads/CrosschainPayload.sol b/test/payloads/CrosschainPayload.sol new file mode 100644 index 0000000..9118388 --- /dev/null +++ b/test/payloads/CrosschainPayload.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.0; + +import { IExecutor } from 'src/interfaces/IExecutor.sol'; + +import { IPayload } from './IPayload.sol'; + +abstract contract CrosschainPayload is IPayload { + + IPayload immutable targetPayload; + address immutable bridgeReceiver; + + constructor(IPayload _targetPayload, address _bridgeReceiver) { + targetPayload = _targetPayload; + bridgeReceiver = _bridgeReceiver; + } + + function execute() external virtual; + + function encodeCrosschainExecutionMessage() internal view returns (bytes memory) { + address[] memory targets = new address[](1); + targets[0] = address(targetPayload); + uint256[] memory values = new uint256[](1); + values[0] = 0; + string[] memory signatures = new string[](1); + signatures[0] = 'execute()'; + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = ''; + bool[] memory withDelegatecalls = new bool[](1); + withDelegatecalls[0] = true; + + return abi.encodeWithSelector( + IExecutor.queue.selector, + targets, + values, + signatures, + calldatas, + withDelegatecalls + ); + } + +} diff --git a/test/payloads/GnosisCrosschainPayload.sol b/test/payloads/GnosisCrosschainPayload.sol new file mode 100644 index 0000000..03798fc --- /dev/null +++ b/test/payloads/GnosisCrosschainPayload.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.0; + +import { AMBForwarder } from 'lib/xchain-helpers/src/forwarders/AMBForwarder.sol'; + +import { CrosschainPayload, IPayload } from './CrosschainPayload.sol'; + +contract GnosisCrosschainPayload is CrosschainPayload { + + constructor(IPayload _targetPayload, address _bridgeReceiver) CrosschainPayload(_targetPayload, _bridgeReceiver) { + } + + function execute() external override { + AMBForwarder.sendMessageEthereumToGnosisChain( + bridgeReceiver, + encodeCrosschainExecutionMessage(), + 1_000_000 + ); + } + +} diff --git a/test/interfaces/IPayload.sol b/test/payloads/IPayload.sol similarity index 98% rename from test/interfaces/IPayload.sol rename to test/payloads/IPayload.sol index 55b76db..05e22b8 100644 --- a/test/interfaces/IPayload.sol +++ b/test/payloads/IPayload.sol @@ -2,7 +2,5 @@ pragma solidity ^0.8.10; interface IPayload { - function execute() external; - } diff --git a/test/payloads/OptimismCrosschainPayload.sol b/test/payloads/OptimismCrosschainPayload.sol new file mode 100644 index 0000000..8183756 --- /dev/null +++ b/test/payloads/OptimismCrosschainPayload.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.0; + +import { OptimismForwarder } from 'lib/xchain-helpers/src/forwarders/OptimismForwarder.sol'; + +import { CrosschainPayload, IPayload } from './CrosschainPayload.sol'; + +contract OptimismCrosschainPayload is CrosschainPayload { + + address public immutable l1CrossDomain; + + constructor(address _l1CrossDomain, IPayload _targetPayload, address _bridgeReceiver) CrosschainPayload(_targetPayload, _bridgeReceiver) { + l1CrossDomain = _l1CrossDomain; + } + + function execute() external override { + OptimismForwarder.sendMessageL1toL2( + l1CrossDomain, + bridgeReceiver, + encodeCrosschainExecutionMessage(), + 1_000_000 + ); + } + +}