diff --git a/src/executors/ZkSyncBridgeExecutor.sol b/src/executors/ZkSyncBridgeExecutor.sol new file mode 100644 index 0000000..80cf63d --- /dev/null +++ b/src/executors/ZkSyncBridgeExecutor.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.10; + +import {L2BridgeExecutor} from "./L2BridgeExecutor.sol"; + +/** + * @title ZkSyncBridgeExecutor + * @author Aave + * @notice Implementation of the ZkSync Bridge Executor, able to receive cross-chain transactions from Ethereum + * @dev Queuing an ActionsSet into this Executor can only be done by the L2 Address Alias of the L1 EthereumGovernanceExecutor + */ +contract ZkSyncBridgeExecutor is L2BridgeExecutor { + uint160 internal constant OFFSET = + uint160(0x1111000000000000000000000000000000001111); //need to use zksync offset + + /// @notice Utility function that converts the msg.sender viewed in the L2 to the + /// address in the L1 that submitted a tx to the inbox + /// @param l2Address L2 address as viewed in msg.sender + /// @return l1Address the address in the L1 that triggered the tx to L2 + function undoL1ToL2Alias( + address l2Address + ) internal pure returns (address l1Address) { + unchecked { + l1Address = address(uint160(l2Address) - OFFSET); + } + } + + /// @inheritdoc L2BridgeExecutor + modifier onlyEthereumGovernanceExecutor() override { + if (undoL1ToL2Alias(msg.sender) != _ethereumGovernanceExecutor) + revert UnauthorizedEthereumExecutor(); + _; + } + + /** + * @dev Constructor + * + * @param ethereumGovernanceExecutor The address of the EthereumGovernanceExecutor + * @param delay The delay before which an actions set can be executed + * @param gracePeriod The time period after a delay during which an actions set can be executed + * @param minimumDelay The minimum bound a delay can be set to + * @param maximumDelay The maximum bound a delay can be set to + * @param guardian The address of the guardian, which can cancel queued proposals (can be zero) + */ + constructor( + address ethereumGovernanceExecutor, + uint256 delay, + uint256 gracePeriod, + uint256 minimumDelay, + uint256 maximumDelay, + address guardian + ) + L2BridgeExecutor( + ethereumGovernanceExecutor, + delay, + gracePeriod, + minimumDelay, + maximumDelay, + guardian + ) + { + // Intentionally left blank + } +} diff --git a/test/ArbitrumCrosschainTest.t.sol b/test/ArbitrumCrosschainTest.t.sol index f1fb3ad..ef7263c 100644 --- a/test/ArbitrumCrosschainTest.t.sol +++ b/test/ArbitrumCrosschainTest.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import 'forge-std/Test.sol'; -import { Domain, ArbitrumDomain } from 'xchain-helpers/ArbitrumDomain.sol'; +import { Domain, ArbitrumDomain } from 'xchain-helpers/testing/ArbitrumDomain.sol'; import { XChainForwarders } from 'xchain-helpers/XChainForwarders.sol'; import { ArbitrumBridgeExecutor } from '../src/executors/ArbitrumBridgeExecutor.sol'; diff --git a/test/CrosschainTestBase.sol b/test/CrosschainTestBase.sol index d443ca2..5b81644 100644 --- a/test/CrosschainTestBase.sol +++ b/test/CrosschainTestBase.sol @@ -4,8 +4,8 @@ pragma solidity ^0.8.0; import 'forge-std/Test.sol'; import 'forge-std/console.sol'; -import { BridgedDomain } from 'xchain-helpers/BridgedDomain.sol'; -import { Domain } from 'xchain-helpers/Domain.sol'; +import { BridgedDomain } from 'xchain-helpers/testing/BridgedDomain.sol'; +import { Domain } from 'xchain-helpers/testing/Domain.sol'; import { IL2BridgeExecutor, IExecutorBase } from '../src/interfaces/IL2BridgeExecutor.sol'; diff --git a/test/GnosisCrosschainTest.t.sol b/test/GnosisCrosschainTest.t.sol index 18b4aaf..8752f61 100644 --- a/test/GnosisCrosschainTest.t.sol +++ b/test/GnosisCrosschainTest.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import 'forge-std/Test.sol'; -import { Domain, GnosisDomain } from 'xchain-helpers/GnosisDomain.sol'; +import { Domain, GnosisDomain } from 'xchain-helpers/testing/GnosisDomain.sol'; import { XChainForwarders } from 'xchain-helpers/XChainForwarders.sol'; import { IAMB, GnosisBridgeExecutor } from '../src/executors/GnosisBridgeExecutor.sol'; diff --git a/test/OptimismCrosschainTest.t.sol b/test/OptimismCrosschainTest.t.sol index 8736471..81368f3 100644 --- a/test/OptimismCrosschainTest.t.sol +++ b/test/OptimismCrosschainTest.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import 'forge-std/Test.sol'; -import { Domain, OptimismDomain } from 'xchain-helpers/OptimismDomain.sol'; +import { Domain, OptimismDomain } from 'xchain-helpers/testing/OptimismDomain.sol'; import { XChainForwarders } from 'xchain-helpers/XChainForwarders.sol'; import { OptimismBridgeExecutor } from '../src/executors/OptimismBridgeExecutor.sol'; diff --git a/test/ZkSyncCrosschainTest.t.sol b/test/ZkSyncCrosschainTest.t.sol new file mode 100644 index 0000000..adf8a89 --- /dev/null +++ b/test/ZkSyncCrosschainTest.t.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "forge-std/Test.sol"; + +import {Domain, ZkSyncDomain} from "xchain-helpers/testing/ZkSyncDomain.sol"; +import {XChainForwarders} from "xchain-helpers/XChainForwarders.sol"; + +import {ZkSyncBridgeExecutor} from "../src/executors/ZkSyncBridgeExecutor.sol"; + +import {IPayload} from "./interfaces/IPayload.sol"; + +import {CrosschainPayload, CrosschainTestBase} from "./CrosschainTestBase.sol"; + +contract ZkSyncCrosschainPayload is CrosschainPayload { + constructor( + IPayload _targetPayload, + address _bridgeExecutor + ) CrosschainPayload(_targetPayload, _bridgeExecutor) {} + + function execute() external override { + XChainForwarders.sendMessageZkSyncEraMainnet( + bridgeExecutor, + encodeCrosschainExecutionMessage(), + 10_000_000, + 800 + ); + } +} + +contract ZkSyncCrosschainTest is CrosschainTestBase { + function deployCrosschainPayload( + IPayload targetPayload, + address bridgeExecutor + ) public override returns (IPayload) { + return + IPayload( + new ZkSyncCrosschainPayload(targetPayload, bridgeExecutor) + ); + } + + function setUp() public { + hostDomain = new Domain(getChain("mainnet")); + setChain( + "zksync_era", + ChainData("zkSync Era", 324, "https://mainnet.era.zksync.io") + ); + bridgedDomain = new ZkSyncDomain(getChain("zksync_era"), hostDomain); + + bridgedDomain.selectFork(); + bridgeExecutor = address( + new ZkSyncBridgeExecutor( + defaultL2BridgeExecutorArgs.ethereumGovernanceExecutor, + defaultL2BridgeExecutorArgs.delay, + defaultL2BridgeExecutorArgs.gracePeriod, + defaultL2BridgeExecutorArgs.minimumDelay, + defaultL2BridgeExecutorArgs.maximumDelay, + defaultL2BridgeExecutorArgs.guardian + ) + ); + + hostDomain.selectFork(); + vm.deal(L1_EXECUTOR, 10 ether); + } +}