diff --git a/contracts/dev-contracts/DummyArbitrator.sol b/contracts/dev-contracts/DummyArbitrator.sol index 161943f..e842c93 100644 --- a/contracts/dev-contracts/DummyArbitrator.sol +++ b/contracts/dev-contracts/DummyArbitrator.sol @@ -11,6 +11,8 @@ import {IL1Gateway} from "../interfaces/IL1Gateway.sol"; contract DummyArbitrator is IArbitrator, OwnableUpgradeable, UUPSUpgradeable, ReentrancyGuardUpgradeable { event ReceiveMessage(uint256 value, bytes callData); + IL1Gateway public primaryChainGateway; + function initialize() external initializer { __Ownable_init(); __UUPSUpgradeable_init(); diff --git a/contracts/dev-contracts/SyncL2TxHashRelayer.sol b/contracts/dev-contracts/SyncL2TxHashRelayer.sol new file mode 100644 index 0000000..a747adb --- /dev/null +++ b/contracts/dev-contracts/SyncL2TxHashRelayer.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +pragma solidity ^0.8.0; + +import {IArbitrator} from "../interfaces/IArbitrator.sol"; +import {IZkLink} from "../interfaces/IZkLink.sol"; + +contract SyncL2TxHashRelayer { + /// @dev The address of the arbitrator contract + IArbitrator public immutable ARBITRATOR; + + constructor(IArbitrator _arbitrator) { + ARBITRATOR = _arbitrator; + } + + function claimPrimaryChainSyncL2TxHashMessage( + address _sourceChainCanonicalMessageService, + bytes calldata _sourceChainClaimCallData, + address _secondaryChainL1Gateway, + bytes32 _secondaryChainL2TxHash, + bytes32 _primaryChainL2TxHash, + bytes calldata _forwardParams + ) external payable { + // Send l2 tx hash to secondary chain by gateway + bytes[] memory gatewayDataList = new bytes[](1); + bytes memory callData = abi.encodeCall(IZkLink.syncL2TxHash, (_secondaryChainL2TxHash, _primaryChainL2TxHash)); + gatewayDataList[0] = abi.encode(_secondaryChainL1Gateway, 0, callData); + + ARBITRATOR.claimMessage{value: msg.value}( + _sourceChainCanonicalMessageService, + _sourceChainClaimCallData, + ARBITRATOR.primaryChainGateway(), + 0, + abi.encode(gatewayDataList), + _forwardParams + ); + } +} diff --git a/contracts/interfaces/IArbitrator.sol b/contracts/interfaces/IArbitrator.sol index fbe7634..7e3289b 100644 --- a/contracts/interfaces/IArbitrator.sol +++ b/contracts/interfaces/IArbitrator.sol @@ -7,6 +7,9 @@ interface IArbitrator { /// @notice Return true if relayer is active function isRelayerActive(address _relayer) external view returns (bool); + /// @notice Return the primary chain gateway + function primaryChainGateway() external view returns (IL1Gateway); + /// @notice Enqueue message from EthereumGateway /// @dev Used by EthereumGateway to temporarily store message /// @param _value The msg value diff --git a/hardhat.config.js b/hardhat.config.js index d03d352..e8428d6 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -9,6 +9,7 @@ require('./script/deploy_erc20_bridge'); require('./script/deploy_governance'); require('./script/deploy_linea_l2_governance'); require('./script/deploy_zklink_token'); +require('./script/deploy_sync_l2_txHash_relayer'); const BaseConfig = require('./hardhat.base.config'); diff --git a/script/deploy_log_name.js b/script/deploy_log_name.js index cef26ec..d1113e6 100644 --- a/script/deploy_log_name.js +++ b/script/deploy_log_name.js @@ -33,6 +33,11 @@ const DEPLOY_LOG_ARBITRATOR_VERIFIED = 'arbitratorVerified'; const DEPLOY_LOG_ARBITRATOR_TARGET = 'arbitratorTarget'; const DEPLOY_LOG_ARBITRATOR_TARGET_VERIFIED = 'arbitratorTargetVerified'; +// consumed in deploy_sync_l2_txHash_relayer.js +const DEPLOY_SYNCL2TXHASHRELAYER_LOG_PREFIX = 'deploy_syncL2TxHashRelayer'; +const DEPLOY_LOG_SYNCL2TXHASHRELAYER = 'syncL2TxHashRelayer'; +const DEPLOY_LOG_SYNCL2TXHASHRELAYER_VERIFIED = 'syncL2TxHashRelayerVerified'; + // consumed in deploy_governance.js const DEPLOY_GOVERNANCE_LOG_PREFIX = 'deploy_governance'; const DEPLOY_LOG_GOVERNANCE = 'governance'; @@ -85,4 +90,7 @@ module.exports = { DEPLOY_LOG_ZKLINK_TOKEN_TARGET, DEPLOY_LOG_ZKLINK_TOKEN_PROXY_VERIFIED, DEPLOY_LOG_ZKLINK_TOKEN_TARGET_VERIFIED, + DEPLOY_SYNCL2TXHASHRELAYER_LOG_PREFIX, + DEPLOY_LOG_SYNCL2TXHASHRELAYER, + DEPLOY_LOG_SYNCL2TXHASHRELAYER_VERIFIED, }; diff --git a/script/deploy_sync_l2_txHash_relayer.js b/script/deploy_sync_l2_txHash_relayer.js new file mode 100644 index 0000000..c0fa33b --- /dev/null +++ b/script/deploy_sync_l2_txHash_relayer.js @@ -0,0 +1,65 @@ +const fs = require('fs'); +const { + verifyContractCode, + createOrGetDeployLog, + ChainContractDeployer, + getDeployTx, + readDeployLogField, +} = require('./utils'); +const logName = require('./deploy_log_name'); +const { task, types } = require('hardhat/config'); + +function getRelayerContractName() { + return 'SyncL2TxHashRelayer'; +} + +task('deploySyncL2TxHashRelayer', 'Deploy SyncL2TxHashRelayer') + .addParam( + 'arbitrator', + 'The arbitrator address (default get from arbitrator deploy log)', + undefined, + types.string, + true, + ) + .addParam('skipVerify', 'Skip verify', false, types.boolean, true) + .setAction(async (taskArgs, hardhat) => { + let arbitrator = taskArgs.arbitrator; + if (arbitrator === undefined) { + arbitrator = readDeployLogField(logName.DEPLOY_ARBITRATOR_LOG_PREFIX, logName.DEPLOY_LOG_ARBITRATOR); + } + let skipVerify = taskArgs.skipVerify; + console.log('arbitrator', arbitrator); + console.log('skip verify contracts?', skipVerify); + + const contractDeployer = new ChainContractDeployer(hardhat); + await contractDeployer.init(); + const deployerWallet = contractDeployer.deployerWallet; + + const { deployLogPath, deployLog } = createOrGetDeployLog(logName.DEPLOY_SYNCL2TXHASHRELAYER_LOG_PREFIX); + deployLog[logName.DEPLOY_LOG_DEPLOYER] = deployerWallet.address; + fs.writeFileSync(deployLogPath, JSON.stringify(deployLog, null, 2)); + + // deploy syncL2TxHashRelayer + let syncL2TxHashRelayerAddr; + if (!(logName.DEPLOY_LOG_SYNCL2TXHASHRELAYER in deployLog)) { + console.log('deploy syncL2TxHashRelayer...'); + const contractName = getRelayerContractName(); + const contract = await contractDeployer.deployContract(contractName, [arbitrator]); + const transaction = await getDeployTx(contract); + syncL2TxHashRelayerAddr = await contract.getAddress(); + deployLog[logName.DEPLOY_LOG_SYNCL2TXHASHRELAYER] = syncL2TxHashRelayerAddr; + deployLog[logName.DEPLOY_LOG_DEPLOY_TX_HASH] = transaction.hash; + deployLog[logName.DEPLOY_LOG_DEPLOY_BLOCK_NUMBER] = transaction.blockNumber; + fs.writeFileSync(deployLogPath, JSON.stringify(deployLog, null, 2)); + } else { + syncL2TxHashRelayerAddr = deployLog[logName.DEPLOY_LOG_SYNCL2TXHASHRELAYER]; + } + console.log('syncL2TxHashRelayer', syncL2TxHashRelayerAddr); + + // verify target contract + if (!(logName.DEPLOY_LOG_SYNCL2TXHASHRELAYER_VERIFIED in deployLog) && !skipVerify) { + await verifyContractCode(hardhat, syncL2TxHashRelayerAddr, [arbitrator]); + deployLog[logName.DEPLOY_LOG_SYNCL2TXHASHRELAYER_VERIFIED] = true; + fs.writeFileSync(deployLogPath, JSON.stringify(deployLog, null, 2)); + } + });