├── .eslintrc ├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── .prettierrc ├── .solhint.json ├── .solhintignore ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── contracts ├── Arbitrator.sol ├── ZkLink.sol ├── dev-contracts │ ├── DummyArbitrator.sol │ ├── DummyZkLink.sol │ ├── SyncL2TxHashRelayer.sol │ └── ZkLinkToken.sol ├── fast-settlement │ ├── FastSettlementMiddleware.sol │ ├── FastSettlementNetwork.sol │ ├── FastSettlementOperator.sol │ └── libraries │ │ └── MapWithTimeData.sol ├── gateway │ ├── BaseGateway.sol │ ├── L1BaseGateway.sol │ ├── L2BaseGateway.sol │ ├── arbitrum │ │ ├── ArbitrumL1Gateway.sol │ │ └── ArbitrumL2Gateway.sol │ ├── ethereum │ │ └── EthereumGateway.sol │ ├── linea │ │ ├── LineaGateway.sol │ │ ├── LineaL1Gateway.sol │ │ ├── LineaL2Gateway.sol │ │ └── LineaL2Governance.sol │ ├── optimism │ │ ├── MantleL2Gateway.sol │ │ ├── OptimismGateway.sol │ │ ├── OptimismL1Gateway.sol │ │ └── OptimismL2Gateway.sol │ ├── scroll │ │ ├── ScrollGateway.sol │ │ ├── ScrollL1Gateway.sol │ │ └── ScrollL2Gateway.sol │ ├── zkpolygon │ │ ├── ZkPolygonL1Gateway.sol │ │ └── ZkPolygonL2Gateway.sol │ └── zksync │ │ ├── ZkSyncL1Gateway.sol │ │ └── ZkSyncL2Gateway.sol ├── interfaces │ ├── IArbitrator.sol │ ├── IFastSettlementMiddleware.sol │ ├── IGateway.sol │ ├── IL1Gateway.sol │ ├── IL2Gateway.sol │ ├── IMessageClaimer.sol │ ├── IZkLink.sol │ ├── linea │ │ ├── IL2MessageService.sol │ │ └── IMessageService.sol │ ├── optimism │ │ └── IOptimismMessenger.sol │ ├── scroll │ │ └── IScrollMessenger.sol │ ├── zkpolygon │ │ ├── IBridgeMessageReceiver.sol │ │ └── IZkPolygon.sol │ └── zksync │ │ ├── IL2ETHToken.sol │ │ └── IZkSyncL1Gateway.sol └── zksync │ └── l1-contracts │ ├── bridge │ ├── L1ERC20Bridge.sol │ ├── interfaces │ │ ├── IL1Bridge.sol │ │ ├── IL1BridgeLegacy.sol │ │ ├── IL2Bridge.sol │ │ └── IL2ERC20Bridge.sol │ └── libraries │ │ └── BridgeInitializationHelper.sol │ ├── common │ ├── L2ContractAddresses.sol │ ├── ReentrancyGuard.sol │ ├── interfaces │ │ └── IL2ContractDeployer.sol │ └── libraries │ │ ├── L2ContractHelper.sol │ │ ├── UncheckedMath.sol │ │ └── UnsafeBytes.sol │ ├── governance │ ├── Governance.sol │ └── IGovernance.sol │ ├── vendor │ └── AddressAliasHelper.sol │ └── zksync │ ├── Config.sol │ ├── Storage.sol │ ├── interfaces │ ├── IAdmin.sol │ ├── IGetters.sol │ ├── IMailbox.sol │ └── IZkSync.sol │ └── libraries │ ├── Merkle.sol │ └── TransactionValidator.sol ├── docs └── SecurityCheck.md ├── etc └── EXAMPLE.json ├── examples ├── arbitrum │ ├── .env-sample │ ├── .gitignore │ ├── hardhat.config.js │ ├── package-lock.json │ ├── package.json │ └── scripts │ │ ├── changeFeeParams.js │ │ ├── governance.js │ │ ├── setSecondaryGateway.js │ │ ├── setValidator.js │ │ ├── syncBatchRoot.js │ │ └── syncL2Requests.js ├── base │ ├── .env-sample │ ├── .gitignore │ ├── hardhat.config.js │ ├── package-lock.json │ ├── package.json │ └── scripts │ │ └── baseTasks.js ├── blast │ ├── .env-sample │ ├── .gitignore │ ├── hardhat.config.js │ ├── package-lock.json │ ├── package.json │ └── scripts │ │ ├── blastTasks.js │ │ ├── constants.js │ │ └── contractABIs.json ├── deposit │ ├── .env-sample │ ├── .gitignore │ ├── hardhat.config.js │ ├── package-lock.json │ ├── package.json │ └── scripts │ │ ├── depositERC20.js │ │ ├── depositETH.js │ │ └── requestL2Tx.js ├── ethereum │ ├── .env-sample │ ├── .gitignore │ ├── hardhat.config.js │ ├── package-lock.json │ ├── package.json │ └── scripts │ │ ├── changeFeeParams.js │ │ └── setValidator.js ├── failedDeposit │ ├── .env-sample │ ├── .gitignore │ ├── hardhat.config.js │ ├── package-lock.json │ ├── package.json │ └── scripts │ │ └── claimFailedDeposit.js ├── linea │ ├── .env-sample │ ├── .gitignore │ ├── hardhat.config.js │ ├── package-lock.json │ ├── package.json │ └── scripts │ │ ├── changeFeeParams.js │ │ ├── claimL1Tx.js │ │ ├── common.js │ │ ├── governance.js │ │ ├── setSecondaryGateway.js │ │ ├── setValidator.js │ │ ├── syncBatchRoot.js │ │ └── syncL2Requests.js ├── manta │ ├── .env-sample │ ├── .gitignore │ ├── hardhat.config.js │ ├── package-lock.json │ ├── package.json │ └── scripts │ │ ├── constants.js │ │ └── mantaTasks.js ├── mantle │ ├── .env-sample │ ├── .gitignore │ ├── hardhat.config.js │ ├── package-lock.json │ ├── package.json │ └── scripts │ │ └── mantleTasks.js ├── optimism │ ├── .env-sample │ ├── .gitignore │ ├── hardhat.config.js │ ├── package-lock.json │ ├── package.json │ └── scripts │ │ ├── opstack-utils.js │ │ └── optimismTasks.js ├── scroll │ ├── .env-sample │ ├── .gitignore │ ├── hardhat.config.js │ ├── package-lock.json │ ├── package.json │ └── scripts │ │ ├── changeFeeParams.js │ │ ├── checkTxStatus.js │ │ ├── scrollSDK.js │ │ ├── setValidator.js │ │ ├── syncBatchRoot.js │ │ └── syncL2Requests.js ├── zklinkNova │ ├── .env-sample │ ├── .gitignore │ ├── hardhat.config.js │ ├── package-lock.json │ ├── package.json │ └── scripts │ │ ├── decodeRawTx.js │ │ ├── getTxStatus.js │ │ ├── printGovernanceCall.js │ │ └── sendRawTx.js ├── zkpolygon │ ├── .env-sample │ ├── .gitignore │ ├── hardhat.config.js │ ├── package-lock.json │ ├── package.json │ └── scripts │ │ ├── changeFeeParams.js │ │ ├── constants.js │ │ ├── setValidator.js │ │ ├── syncBatchRoot.js │ │ └── syncL2Requests.js └── zksync │ ├── .env-sample │ ├── .gitignore │ ├── hardhat.config.js │ ├── package-lock.json │ ├── package.json │ └── scripts │ ├── changeFeeParams.js │ ├── governance.js │ ├── setValidator.js │ ├── syncBatchRoot.js │ └── syncL2Requests.js ├── hardhat.base.config.js ├── hardhat.config.js ├── package-lock.json ├── package.json ├── script ├── ChainConfig.json ├── deploy_arbitrator.js ├── deploy_erc20_bridge.js ├── deploy_eth_gateway.js ├── deploy_fastSettlement_middleware.js ├── deploy_fastSettlement_network.js ├── deploy_fastSettlement_operator.js ├── deploy_governance.js ├── deploy_l1_gateway.js ├── deploy_l2_gateway.js ├── deploy_linea_l2_governance.js ├── deploy_log_name.js ├── deploy_sync_l2_txHash_relayer.js ├── deploy_zklink.js ├── deploy_zklink_token.js ├── utils.js ├── zklink_config.js └── zksync_era.js └── zksync ├── .gitignore ├── hardhat.config.js ├── package-lock.json └── package.json /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends" : [ 4 | "eslint:recommended", 5 | "prettier" 6 | ], 7 | "env": { 8 | "es2022": true, 9 | "browser": true, 10 | "node": true, 11 | "mocha": true 12 | }, 13 | "globals" : { 14 | } 15 | } -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: github action CI 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | - testnet 8 | push: 9 | branches: 10 | - main 11 | - testnet 12 | 13 | jobs: 14 | ci: 15 | runs-on: ubuntu-latest 16 | name: Test solidity contracts 17 | steps: 18 | - name: Clone repo 19 | uses: actions/checkout@v3 20 | - name: Setup node environment 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: 20.x 24 | - uses: actions/cache@v4 25 | id: cache 26 | with: 27 | path: '**/node_modules' 28 | key: npm-v3-${{ hashFiles('**/package-lock.json') }} 29 | - name: Install dependencies 30 | run: npm ci 31 | shell: bash 32 | if: steps.cache.outputs.cache-hit != 'true' 33 | - name: Lint check 34 | run: npm run lint 35 | - name: Compile contracts 36 | run: npm run compile 37 | - name: Test contracts 38 | run: npm run test -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | node_modules 4 | 5 | log 6 | tmp 7 | 8 | etc/* 9 | !etc/EXAMPLE.json 10 | 11 | # Hardhat files 12 | cache 13 | artifacts 14 | 15 | # Openzeppelin upgrade 16 | .openzeppelin 17 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "singleQuote": true, 4 | "trailingComma": "all", 5 | "arrowParens": "avoid", 6 | "overrides": [ 7 | { 8 | "files": "*.sol", 9 | "options": { 10 | "singleQuote": false 11 | } 12 | } 13 | ], 14 | "plugins": ["prettier-plugin-solidity"] 15 | } -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "plugins": [], 4 | "rules": { 5 | "compiler-version": "off", 6 | "func-name-mixedcase": "off", 7 | "func-visibility": ["warn",{"ignoreConstructors": true}], 8 | "mark-callable-contracts": "off", 9 | "max-states-count": "off", 10 | "no-inline-assembly": "off", 11 | "not-rely-on-time": "off", 12 | "no-empty-blocks": "off", 13 | "avoid-tx-origin": "off", 14 | "gas-custom-errors": "off" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.solhintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | artifacts/ 3 | artifacts-zk/ 4 | cache/ 5 | cache-zk/ 6 | contracts/zksync/ -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 ZkLink Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [ZkLink EVM Contracts](https://zk.link/) 2 | 3 | ## Install Dependencies 4 | 5 | `npm install` 6 | 7 | ## Compile contracts 8 | 9 | `npx hardhat compile` 10 | 11 | ## Run tests 12 | 13 | Run all unit tests: 14 | 15 | `npx hardhat test` 16 | 17 | ## Deploy 18 | 19 | 20 | ## Development 21 | 22 | For developers, static analysis need to be done before committing code. Read more of [SecurityCheck](docs/SecurityCheck.md). 23 | -------------------------------------------------------------------------------- /contracts/dev-contracts/DummyArbitrator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; 6 | import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; 7 | import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; 8 | import {IArbitrator} from "../interfaces/IArbitrator.sol"; 9 | import {IL1Gateway} from "../interfaces/IL1Gateway.sol"; 10 | 11 | contract DummyArbitrator is IArbitrator, OwnableUpgradeable, UUPSUpgradeable, ReentrancyGuardUpgradeable { 12 | event ReceiveMessage(uint256 value, bytes callData); 13 | 14 | IL1Gateway public primaryChainGateway; 15 | bool public paused; 16 | 17 | function initialize() external initializer { 18 | __Ownable_init(); 19 | __UUPSUpgradeable_init(); 20 | __ReentrancyGuard_init(); 21 | } 22 | 23 | function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} 24 | 25 | function isRelayerActive(address) external pure returns (bool) { 26 | return false; 27 | } 28 | 29 | function enqueueMessage(uint256 _value, bytes calldata _callData) external payable { 30 | require(msg.value == _value, "Invalid msg value"); 31 | emit ReceiveMessage(_value, _callData); 32 | } 33 | 34 | function receiveMessage(uint256 _value, bytes calldata _callData) external payable { 35 | require(msg.value == _value, "Invalid msg value"); 36 | emit ReceiveMessage(_value, _callData); 37 | } 38 | 39 | function forwardMessage( 40 | IL1Gateway _gateway, 41 | uint256 _value, 42 | bytes calldata _callData, 43 | bytes calldata _adapterParams 44 | ) external payable { 45 | // Forward fee to send message 46 | _gateway.sendMessage{value: msg.value}(_value, _callData, _adapterParams); 47 | } 48 | 49 | function claimMessage( 50 | address, 51 | bytes calldata, 52 | IL1Gateway, 53 | uint256, 54 | bytes calldata, 55 | bytes calldata 56 | ) external payable { 57 | // do nothing 58 | } 59 | 60 | function setPause(bool _paused) external { 61 | paused = _paused; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /contracts/dev-contracts/DummyZkLink.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; 6 | import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; 7 | import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; 8 | import {IL2Gateway} from "../interfaces/IL2Gateway.sol"; 9 | import {IZkLink} from "../interfaces/IZkLink.sol"; 10 | 11 | contract DummyZkLink is IZkLink, OwnableUpgradeable, UUPSUpgradeable, ReentrancyGuardUpgradeable { 12 | IL2Gateway public gateway; 13 | 14 | event ReceiveBatchRoot(uint256 batchNumber, bytes32 l2LogsRootHash, uint256 forwardEthAmount); 15 | event ReceiveRangeBatchRoot( 16 | uint256 fromBatchNumber, 17 | uint256 toBatchNumber, 18 | bytes32 rangeBatchRootHash, 19 | uint256 forwardEthAmount 20 | ); 21 | event ReceiveL2TxHash(bytes32 l2TxHash, bytes32 primaryChainL2TxHash); 22 | 23 | modifier onlyGateway() { 24 | require(msg.sender == address(gateway), "Not gateway"); 25 | _; 26 | } 27 | 28 | constructor() { 29 | _disableInitializers(); 30 | } 31 | 32 | function initialize() external initializer { 33 | __Ownable_init(); 34 | __UUPSUpgradeable_init(); 35 | __ReentrancyGuard_init(); 36 | } 37 | 38 | function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} 39 | 40 | function getGateway() external view returns (IL2Gateway) { 41 | return gateway; 42 | } 43 | 44 | function setGateway(IL2Gateway _gateway) external onlyOwner { 45 | gateway = _gateway; 46 | } 47 | 48 | function syncL2Requests(uint256 _newTotalSyncedPriorityTxs) external payable { 49 | bytes memory callData = abi.encode(0, _newTotalSyncedPriorityTxs); 50 | gateway.sendMessage{value: msg.value}(0, callData); 51 | } 52 | 53 | function syncBatchRoot( 54 | uint256 _batchNumber, 55 | bytes32 _l2LogsRootHash, 56 | uint256 _forwardEthAmount 57 | ) external payable onlyGateway { 58 | emit ReceiveBatchRoot(_batchNumber, _l2LogsRootHash, _forwardEthAmount); 59 | } 60 | 61 | function syncRangeBatchRoot( 62 | uint256 _fromBatchNumber, 63 | uint256 _toBatchNumber, 64 | bytes32 _rangeBatchRootHash, 65 | uint256 _forwardEthAmount 66 | ) external payable onlyGateway { 67 | emit ReceiveRangeBatchRoot(_fromBatchNumber, _toBatchNumber, _rangeBatchRootHash, _forwardEthAmount); 68 | } 69 | 70 | function syncL2TxHash(bytes32 _l2TxHash, bytes32 _primaryChainL2TxHash) external onlyGateway { 71 | emit ReceiveL2TxHash(_l2TxHash, _primaryChainL2TxHash); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /contracts/dev-contracts/SyncL2TxHashRelayer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import {IArbitrator} from "../interfaces/IArbitrator.sol"; 6 | import {IZkLink} from "../interfaces/IZkLink.sol"; 7 | 8 | contract SyncL2TxHashRelayer { 9 | /// @dev The address of the arbitrator contract 10 | IArbitrator public immutable ARBITRATOR; 11 | 12 | constructor(IArbitrator _arbitrator) { 13 | ARBITRATOR = _arbitrator; 14 | } 15 | 16 | function claimPrimaryChainSyncL2TxHashMessage( 17 | address _sourceChainCanonicalMessageService, 18 | bytes calldata _sourceChainClaimCallData, 19 | address _secondaryChainL1Gateway, 20 | bytes32 _secondaryChainL2TxHash, 21 | bytes32 _primaryChainL2TxHash, 22 | bytes calldata _forwardParams 23 | ) external payable { 24 | // Send l2 tx hash to secondary chain by gateway 25 | bytes[] memory gatewayDataList = new bytes[](1); 26 | bytes memory callData = abi.encodeCall(IZkLink.syncL2TxHash, (_secondaryChainL2TxHash, _primaryChainL2TxHash)); 27 | gatewayDataList[0] = abi.encode(_secondaryChainL1Gateway, 0, callData); 28 | 29 | ARBITRATOR.claimMessage{value: msg.value}( 30 | _sourceChainCanonicalMessageService, 31 | _sourceChainClaimCallData, 32 | ARBITRATOR.primaryChainGateway(), 33 | 0, 34 | abi.encode(gatewayDataList), 35 | _forwardParams 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/dev-contracts/ZkLinkToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; 6 | import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; 7 | import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; 8 | import {ERC20PermitUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol"; 9 | import {ERC20CappedUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20CappedUpgradeable.sol"; 10 | 11 | contract ZkLinkToken is 12 | OwnableUpgradeable, 13 | UUPSUpgradeable, 14 | ERC20Upgradeable, 15 | ERC20PermitUpgradeable, 16 | ERC20CappedUpgradeable 17 | { 18 | function initialize() external initializer { 19 | __Ownable_init(); 20 | __UUPSUpgradeable_init(); 21 | __ERC20_init("zkLink Token", "ZKL"); 22 | __ERC20Permit_init("ZKLink"); 23 | __ERC20Capped_init(1000000000000000000000000000); 24 | } 25 | 26 | function _authorizeUpgrade(address newImplementation) internal override onlyOwner { 27 | // can only called by owner 28 | } 29 | 30 | function mint(address account, uint256 amount) external onlyOwner { 31 | _mint(account, amount); 32 | } 33 | 34 | function _mint(address account, uint256 amount) internal override(ERC20CappedUpgradeable, ERC20Upgradeable) { 35 | ERC20CappedUpgradeable._mint(account, amount); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /contracts/fast-settlement/FastSettlementNetwork.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; 6 | import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 7 | import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; 8 | import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; 9 | import {IBaseDelegator} from "@symbioticfi/core/src/interfaces/delegator/IBaseDelegator.sol"; 10 | import {IVault} from "@symbioticfi/core/src/interfaces/vault/IVault.sol"; 11 | import {IVetoSlasher} from "@symbioticfi/core/src/interfaces/slasher/IVetoSlasher.sol"; 12 | import {INetworkMiddlewareService} from "@symbioticfi/core/src/interfaces/service/INetworkMiddlewareService.sol"; 13 | import {INetworkRegistry} from "@symbioticfi/core/src/interfaces/INetworkRegistry.sol"; 14 | import {IRegistry} from "@symbioticfi/core/src/interfaces/common/IRegistry.sol"; 15 | 16 | contract FastSettlementNetwork is Initializable, UUPSUpgradeable, OwnableUpgradeable, ReentrancyGuardUpgradeable { 17 | address public immutable NETWORK_REGISTRY; 18 | address public immutable NETWORK_MIDDLEWARE_SERVICE; 19 | 20 | address public immutable VAULT_FACTORY; 21 | 22 | constructor( 23 | address _networkRegistry, 24 | address _networkMiddlewareService, 25 | address _vaultFactory 26 | ) { 27 | _disableInitializers(); 28 | 29 | NETWORK_REGISTRY = _networkRegistry; 30 | NETWORK_MIDDLEWARE_SERVICE = _networkMiddlewareService; 31 | VAULT_FACTORY = _vaultFactory; 32 | } 33 | 34 | function initialize() public initializer { 35 | __UUPSUpgradeable_init_unchained(); 36 | __Ownable_init_unchained(); 37 | __ReentrancyGuard_init_unchained(); 38 | } 39 | 40 | function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} 41 | 42 | modifier checkVault(address vault) { 43 | require(IRegistry(VAULT_FACTORY).isEntity(vault), "vault not found"); 44 | _; 45 | } 46 | 47 | function registerNetwork() external onlyOwner { 48 | INetworkRegistry(NETWORK_REGISTRY).registerNetwork(); 49 | } 50 | 51 | function setMiddleware(address middleware) external onlyOwner { 52 | INetworkMiddlewareService(NETWORK_MIDDLEWARE_SERVICE).setMiddleware(middleware); 53 | } 54 | 55 | function setMaxNetworkLimit(address vault, uint96 identifier, uint256 amount) external onlyOwner checkVault(vault) { 56 | address delegator = IVault(vault).delegator(); 57 | IBaseDelegator(delegator).setMaxNetworkLimit(identifier, amount); 58 | } 59 | 60 | function setResolver( 61 | address vault, 62 | uint96 identifier, 63 | address resolver_, 64 | bytes calldata hints 65 | ) external onlyOwner checkVault(vault) { 66 | address slasher = IVault(vault).slasher(); 67 | IVetoSlasher(slasher).setResolver(identifier, resolver_, hints); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /contracts/fast-settlement/libraries/MapWithTimeData.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import {EnumerableMapUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableMapUpgradeable.sol"; 5 | 6 | library MapWithTimeData { 7 | using EnumerableMapUpgradeable for EnumerableMapUpgradeable.AddressToUintMap; 8 | 9 | error AlreadyAdded(); 10 | error NotEnabled(); 11 | error AlreadyEnabled(); 12 | 13 | uint256 private constant ENABLED_TIME_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFF; 14 | uint256 private constant DISABLED_TIME_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFF << 48; 15 | 16 | function add(EnumerableMapUpgradeable.AddressToUintMap storage self, address addr) internal { 17 | if (!self.set(addr, uint256(0))) { 18 | revert AlreadyAdded(); 19 | } 20 | } 21 | 22 | function disable(EnumerableMapUpgradeable.AddressToUintMap storage self, address addr) internal { 23 | uint256 value = self.get(addr); 24 | 25 | if (uint48(value) == 0 || uint48(value >> 48) != 0) { 26 | revert NotEnabled(); 27 | } 28 | 29 | value |= uint256(block.timestamp) << 48; 30 | self.set(addr, value); 31 | } 32 | 33 | function enable(EnumerableMapUpgradeable.AddressToUintMap storage self, address addr) internal { 34 | uint256 value = self.get(addr); 35 | 36 | if (uint48(value) != 0 && uint48(value >> 48) == 0) { 37 | revert AlreadyEnabled(); 38 | } 39 | 40 | value = uint256(block.timestamp); 41 | self.set(addr, value); 42 | } 43 | 44 | function atWithTimes( 45 | EnumerableMapUpgradeable.AddressToUintMap storage self, 46 | uint256 idx 47 | ) internal view returns (address key, uint48 enabledTime, uint48 disabledTime) { 48 | uint256 value; 49 | (key, value) = self.at(idx); 50 | enabledTime = uint48(value); 51 | disabledTime = uint48(value >> 48); 52 | } 53 | 54 | function getTimes( 55 | EnumerableMapUpgradeable.AddressToUintMap storage self, 56 | address addr 57 | ) internal view returns (uint48 enabledTime, uint48 disabledTime) { 58 | uint256 value = self.get(addr); 59 | enabledTime = uint48(value); 60 | disabledTime = uint48(value >> 48); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /contracts/gateway/BaseGateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; 5 | import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; 6 | import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; 7 | import {IGateway} from "../interfaces/IGateway.sol"; 8 | 9 | abstract contract BaseGateway is IGateway, OwnableUpgradeable, UUPSUpgradeable, ReentrancyGuardUpgradeable { 10 | /// @notice Gateway address on remote chain 11 | address internal remoteGateway; 12 | 13 | /** 14 | * @dev This empty reserved space is put in place to allow future versions to add new 15 | * variables without shifting down storage in the inheritance chain. 16 | * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps 17 | */ 18 | uint256[49] private __gap; 19 | 20 | event NewRemoteGateway(address remoteGateWay); 21 | 22 | function __BaseGateway_init() internal onlyInitializing { 23 | __BaseGateway_init_unchained(); 24 | } 25 | 26 | function __BaseGateway_init_unchained() internal onlyInitializing { 27 | __Ownable_init(); 28 | __UUPSUpgradeable_init(); 29 | __ReentrancyGuard_init(); 30 | } 31 | 32 | function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} 33 | 34 | function getRemoteGateway() external view returns (address) { 35 | return remoteGateway; 36 | } 37 | 38 | /// @notice Set remote Gateway address 39 | /// @param _remoteGateway remote gateway address 40 | function setRemoteGateway(address _remoteGateway) external onlyOwner { 41 | require(remoteGateway == address(0), "Duplicate init remote gateway"); 42 | require(_remoteGateway != address(0), "Invalid gateway"); 43 | remoteGateway = _remoteGateway; 44 | emit NewRemoteGateway(_remoteGateway); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /contracts/gateway/L1BaseGateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {IArbitrator} from "../interfaces/IArbitrator.sol"; 5 | import {IL1Gateway} from "../interfaces/IL1Gateway.sol"; 6 | 7 | abstract contract L1BaseGateway is IL1Gateway { 8 | /// @notice The arbitrator to confirm synchronization 9 | IArbitrator public immutable ARBITRATOR; 10 | 11 | /** 12 | * @dev This empty reserved space is put in place to allow future versions to add new 13 | * variables without shifting down storage in the inheritance chain. 14 | * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps 15 | */ 16 | uint256[50] private __gap; 17 | 18 | /// @dev Modifier to make sure the caller is the known arbitrator. 19 | modifier onlyArbitrator() { 20 | require(msg.sender == address(ARBITRATOR), "Not arbitrator"); 21 | _; 22 | } 23 | 24 | constructor(IArbitrator _arbitrator) { 25 | ARBITRATOR = _arbitrator; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /contracts/gateway/L2BaseGateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {IL2Gateway} from "../interfaces/IL2Gateway.sol"; 5 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | 7 | abstract contract L2BaseGateway is IL2Gateway { 8 | /// @notice The zkLink contract 9 | address public immutable ZKLINK; 10 | 11 | /** 12 | * @dev This empty reserved space is put in place to allow future versions to add new 13 | * variables without shifting down storage in the inheritance chain. 14 | * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps 15 | */ 16 | uint256[50] private __gap; 17 | 18 | /// @dev Ensure withdraw come from zkLink 19 | modifier onlyZkLink() { 20 | require(msg.sender == ZKLINK, "Not zkLink contract"); 21 | _; 22 | } 23 | 24 | constructor(address _zkLink) { 25 | ZKLINK = _zkLink; 26 | } 27 | 28 | function isEthGasToken() external pure virtual returns (bool) { 29 | return true; 30 | } 31 | 32 | function ethToken() external pure virtual returns (IERC20) { 33 | return IERC20(address(0)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /contracts/gateway/arbitrum/ArbitrumL1Gateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {Inbox, IBridge} from "@arbitrum/nitro-contracts/src/bridge/Inbox.sol"; 5 | import {IOutbox} from "@arbitrum/nitro-contracts/src/bridge/Outbox.sol"; 6 | import {IArbitrator} from "../../interfaces/IArbitrator.sol"; 7 | import {L1BaseGateway} from "../L1BaseGateway.sol"; 8 | import {BaseGateway} from "../BaseGateway.sol"; 9 | import {IMessageClaimer} from "../../interfaces/IMessageClaimer.sol"; 10 | 11 | contract ArbitrumL1Gateway is IMessageClaimer, L1BaseGateway, BaseGateway { 12 | /// @notice Arbitrum inbox on local chain 13 | Inbox public immutable INBOX; 14 | 15 | /// @dev Modifier to make sure the original sender is gateway on remote chain. 16 | modifier onlyRemoteGateway() { 17 | IBridge bridge = INBOX.bridge(); 18 | require(msg.sender == address(bridge), "Not bridge"); 19 | IOutbox outbox = IOutbox(bridge.activeOutbox()); 20 | address l2Sender = outbox.l2ToL1Sender(); 21 | require(l2Sender == remoteGateway, "Not remote gateway"); 22 | _; 23 | } 24 | 25 | constructor(IArbitrator _arbitrator, Inbox _inbox) L1BaseGateway(_arbitrator) { 26 | _disableInitializers(); 27 | INBOX = _inbox; 28 | } 29 | 30 | function initialize() external initializer { 31 | __BaseGateway_init(); 32 | } 33 | 34 | function sendMessage( 35 | uint256 _value, 36 | bytes calldata _callData, 37 | bytes calldata _adapterParams 38 | ) external payable onlyArbitrator { 39 | (uint256 maxSubmissionCost, uint256 gasLimit, uint256 maxFeePerGas) = abi.decode( 40 | _adapterParams, 41 | (uint256, uint256, uint256) 42 | ); 43 | bytes memory data = abi.encodeCall(IMessageClaimer.claimMessageCallback, (_value, _callData)); 44 | // no use of the return value 45 | INBOX.createRetryableTicket{value: msg.value}( 46 | remoteGateway, 47 | _value, 48 | maxSubmissionCost, 49 | // solhint-disable-next-line avoid-tx-origin 50 | tx.origin, 51 | remoteGateway, 52 | gasLimit, 53 | maxFeePerGas, 54 | data 55 | ); 56 | } 57 | 58 | function claimMessageCallback(uint256 _value, bytes calldata _callData) external payable onlyRemoteGateway { 59 | require(msg.value == _value, "Invalid value"); 60 | // Forward message to arbitrator 61 | ARBITRATOR.receiveMessage{value: _value}(_value, _callData); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /contracts/gateway/arbitrum/ArbitrumL2Gateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {ArbSys} from "@arbitrum/nitro-contracts/src/precompiles/ArbSys.sol"; 5 | import {L2BaseGateway} from "../L2BaseGateway.sol"; 6 | import {AddressAliasHelper} from "../../zksync/l1-contracts/vendor/AddressAliasHelper.sol"; 7 | import {IMessageClaimer} from "../../interfaces/IMessageClaimer.sol"; 8 | import {BaseGateway} from "../BaseGateway.sol"; 9 | 10 | contract ArbitrumL2Gateway is IMessageClaimer, L2BaseGateway, BaseGateway { 11 | /// @notice Arbitrum system contract 12 | ArbSys public constant ARB_SYS = ArbSys(address(100)); 13 | 14 | /// @dev Modifier to make sure the original sender is gateway on remote chain. 15 | modifier onlyRemoteGateway() { 16 | require(AddressAliasHelper.undoL1ToL2Alias(msg.sender) == remoteGateway, "Not remote gateway"); 17 | _; 18 | } 19 | 20 | constructor(address _zkLink) L2BaseGateway(_zkLink) { 21 | _disableInitializers(); 22 | } 23 | 24 | function initialize() external initializer { 25 | __BaseGateway_init(); 26 | } 27 | 28 | function sendMessage(uint256 _value, bytes calldata _callData) external payable override onlyZkLink { 29 | // no fee 30 | require(msg.value == _value, "Invalid value"); 31 | 32 | // send message to ArbitrumL1Gateway 33 | bytes memory message = abi.encodeCall(IMessageClaimer.claimMessageCallback, (_value, _callData)); 34 | // no use of the return value 35 | ARB_SYS.sendTxToL1{value: _value}(remoteGateway, message); 36 | emit L2GatewayMessageSent(_value, _callData); 37 | } 38 | 39 | function claimMessageCallback(uint256 _value, bytes calldata _callData) external payable onlyRemoteGateway { 40 | require(msg.value == _value, "Invalid value"); 41 | 42 | // solhint-disable-next-line avoid-low-level-calls 43 | (bool success, ) = ZKLINK.call{value: _value}(_callData); 44 | require(success, "Call zkLink failed"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /contracts/gateway/ethereum/EthereumGateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; 5 | import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; 6 | import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; 7 | import {IArbitrator} from "../../interfaces/IArbitrator.sol"; 8 | import {L2BaseGateway} from "../L2BaseGateway.sol"; 9 | import {L1BaseGateway} from "../L1BaseGateway.sol"; 10 | 11 | contract EthereumGateway is 12 | L1BaseGateway, 13 | L2BaseGateway, 14 | OwnableUpgradeable, 15 | UUPSUpgradeable, 16 | ReentrancyGuardUpgradeable 17 | { 18 | constructor(IArbitrator _arbitrator, address _zkLink) L1BaseGateway(_arbitrator) L2BaseGateway(_zkLink) { 19 | _disableInitializers(); 20 | } 21 | 22 | function initialize() external initializer { 23 | __Ownable_init(); 24 | __UUPSUpgradeable_init(); 25 | __ReentrancyGuard_init(); 26 | } 27 | 28 | function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} 29 | 30 | function getRemoteGateway() external view returns (address) { 31 | return address(this); 32 | } 33 | 34 | function sendMessage(uint256 _value, bytes calldata _callData, bytes calldata) external payable onlyArbitrator { 35 | require(msg.value == _value, "Invalid value"); 36 | 37 | // solhint-disable-next-line avoid-low-level-calls 38 | (bool success, ) = ZKLINK.call{value: _value}(_callData); 39 | require(success, "Call zkLink failed"); 40 | } 41 | 42 | function sendMessage(uint256 _value, bytes calldata _callData) external payable override onlyZkLink { 43 | require(msg.value == _value, "Invalid value"); 44 | // Forward message to arbitrator 45 | ARBITRATOR.enqueueMessage{value: _value}(_value, _callData); 46 | emit L2GatewayMessageSent(_value, _callData); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /contracts/gateway/linea/LineaGateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {IMessageService} from "../../interfaces/linea/IMessageService.sol"; 5 | import {BaseGateway} from "../BaseGateway.sol"; 6 | import {IMessageClaimer} from "../../interfaces/IMessageClaimer.sol"; 7 | 8 | abstract contract LineaGateway is BaseGateway, IMessageClaimer { 9 | /// @notice Linea message service on local chain 10 | IMessageService public immutable MESSAGE_SERVICE; 11 | 12 | /** 13 | * @dev This empty reserved space is put in place to allow future versions to add new 14 | * variables without shifting down storage in the inheritance chain. 15 | * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps 16 | */ 17 | uint256[50] private __gap; 18 | 19 | /// @dev Modifier to make sure the caller is the known message service. 20 | modifier onlyMessageService() { 21 | require(msg.sender == address(MESSAGE_SERVICE), "Not message service"); 22 | _; 23 | } 24 | 25 | /// @dev Modifier to make sure the original sender is gateway on remote chain. 26 | modifier onlyRemoteGateway() { 27 | require(MESSAGE_SERVICE.sender() == remoteGateway, "Not remote gateway"); 28 | _; 29 | } 30 | 31 | constructor(IMessageService _messageService) { 32 | MESSAGE_SERVICE = _messageService; 33 | } 34 | 35 | function __LineaGateway_init() internal onlyInitializing { 36 | __BaseGateway_init(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/gateway/linea/LineaL1Gateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {IArbitrator} from "../../interfaces/IArbitrator.sol"; 5 | import {IMessageService} from "../../interfaces/linea/IMessageService.sol"; 6 | import {IMessageClaimer} from "../../interfaces/IMessageClaimer.sol"; 7 | import {LineaGateway} from "./LineaGateway.sol"; 8 | import {L1BaseGateway} from "../L1BaseGateway.sol"; 9 | 10 | contract LineaL1Gateway is L1BaseGateway, LineaGateway { 11 | constructor( 12 | IArbitrator _arbitrator, 13 | IMessageService _messageService 14 | ) L1BaseGateway(_arbitrator) LineaGateway(_messageService) { 15 | _disableInitializers(); 16 | } 17 | 18 | function initialize() external initializer { 19 | __LineaGateway_init(); 20 | } 21 | 22 | function sendMessage(uint256 _value, bytes calldata _callData, bytes calldata) external payable onlyArbitrator { 23 | // transfer no fee to destination chain 24 | require(msg.value == _value, "Invalid value"); 25 | bytes memory message = abi.encodeCall(IMessageClaimer.claimMessageCallback, (_value, _callData)); 26 | MESSAGE_SERVICE.sendMessage{value: _value}(remoteGateway, 0, message); 27 | } 28 | 29 | function claimMessageCallback( 30 | uint256 _value, 31 | bytes calldata _callData 32 | ) external payable onlyMessageService onlyRemoteGateway { 33 | require(msg.value == _value, "Invalid value"); 34 | // Forward message to arbitrator 35 | ARBITRATOR.receiveMessage{value: _value}(_value, _callData); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /contracts/gateway/linea/LineaL2Gateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {IL2MessageService} from "../../interfaces/linea/IL2MessageService.sol"; 5 | import {IMessageClaimer} from "../../interfaces/IMessageClaimer.sol"; 6 | import {LineaGateway} from "./LineaGateway.sol"; 7 | import {L2BaseGateway} from "../L2BaseGateway.sol"; 8 | 9 | contract LineaL2Gateway is L2BaseGateway, LineaGateway { 10 | constructor( 11 | address _zkLink, 12 | IL2MessageService _messageService 13 | ) L2BaseGateway(_zkLink) LineaGateway(_messageService) { 14 | _disableInitializers(); 15 | } 16 | 17 | function initialize() external initializer { 18 | __LineaGateway_init(); 19 | } 20 | 21 | function sendMessage(uint256 _value, bytes calldata _callData) external payable override onlyZkLink { 22 | // msg value should include fee 23 | uint256 coinbaseFee = IL2MessageService(address(MESSAGE_SERVICE)).minimumFeeInWei(); 24 | require(msg.value == _value + coinbaseFee, "Invalid value"); 25 | 26 | bytes memory message = abi.encodeCall(IMessageClaimer.claimMessageCallback, (_value, _callData)); 27 | MESSAGE_SERVICE.sendMessage{value: msg.value}(remoteGateway, coinbaseFee, message); 28 | emit L2GatewayMessageSent(_value, _callData); 29 | } 30 | 31 | function claimMessageCallback( 32 | uint256 _value, 33 | bytes calldata _callData 34 | ) external payable onlyMessageService onlyRemoteGateway { 35 | require(msg.value == _value, "Invalid value"); 36 | 37 | // solhint-disable-next-line avoid-low-level-calls 38 | (bool success, ) = ZKLINK.call{value: _value}(_callData); 39 | require(success, "Call zkLink failed"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /contracts/gateway/linea/LineaL2Governance.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; 6 | import {Address} from "@openzeppelin/contracts/utils/Address.sol"; 7 | import {IMessageService} from "../../interfaces/linea/IMessageService.sol"; 8 | 9 | contract LineaL2Governance is Ownable { 10 | /// @notice Linea message service on local chain 11 | IMessageService public immutable MESSAGE_SERVICE; 12 | 13 | /// @dev Represents a call to be made during an operation. 14 | /// @param target The address to which the call will be made. 15 | /// @param value The amount of Ether (in wei) to be sent along with the call. 16 | /// @param data The calldata to be executed on the `target` address. 17 | struct Call { 18 | address target; 19 | uint256 value; 20 | bytes data; 21 | } 22 | 23 | constructor(IMessageService _messageService, address _owner) { 24 | MESSAGE_SERVICE = _messageService; 25 | _transferOwnership(_owner); 26 | } 27 | 28 | /** 29 | * @dev Throws if the sender is not the Governance on Ethereum. 30 | */ 31 | function _checkOwner() internal view override { 32 | require( 33 | _msgSender() == address(MESSAGE_SERVICE) && owner() == MESSAGE_SERVICE.sender(), 34 | "Ownable: caller is not the owner" 35 | ); 36 | } 37 | 38 | /// @notice Executes the operation's calls from the Governance contract on Ethereum. 39 | /// @param _calls The array of calls to be executed. 40 | function execute(Call[] calldata _calls) external payable onlyOwner { 41 | for (uint256 i = 0; i < _calls.length; ++i) { 42 | Call memory _call = _calls[i]; 43 | // No use of return value 44 | Address.functionCallWithValue(_call.target, _call.data, _call.value); 45 | } 46 | } 47 | 48 | /// @dev Contract might receive/hold ETH as part of the maintenance process. 49 | receive() external payable { 50 | // nothing to do here 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /contracts/gateway/optimism/MantleL2Gateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {OptimismL2Gateway} from "./OptimismL2Gateway.sol"; 5 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 7 | 8 | contract MantleL2Gateway is OptimismL2Gateway { 9 | using SafeERC20 for IERC20; 10 | 11 | /// @dev The ETH token deployed on Mantle 12 | IERC20 private constant BVM_ETH = IERC20(0xdEAddEaDdeadDEadDEADDEAddEADDEAddead1111); 13 | 14 | constructor(address _zkLink) OptimismL2Gateway(_zkLink) { 15 | _disableInitializers(); 16 | } 17 | 18 | function isEthGasToken() external pure override returns (bool) { 19 | return false; 20 | } 21 | 22 | function ethToken() external pure override returns (IERC20) { 23 | return BVM_ETH; 24 | } 25 | 26 | function claimMessageCallback( 27 | uint256 _ethValue, 28 | bytes calldata _callData 29 | ) external payable override onlyMessageService onlyRemoteGateway { 30 | if (_ethValue > 0) { 31 | // Mantle L2CrossDomainMessenger will approve l2 gateway before the callback in `relayMessage` 32 | SafeERC20.safeTransferFrom(BVM_ETH, address(MESSAGE_SERVICE), address(ZKLINK), _ethValue); 33 | } 34 | // solhint-disable-next-line avoid-low-level-calls 35 | (bool success, ) = ZKLINK.call{value: 0}(_callData); 36 | require(success, "Call zkLink failed"); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/gateway/optimism/OptimismGateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {IOptimismMessenger} from "../../interfaces/optimism/IOptimismMessenger.sol"; 5 | import {IMessageClaimer} from "../../interfaces/IMessageClaimer.sol"; 6 | import {BaseGateway} from "../BaseGateway.sol"; 7 | 8 | abstract contract OptimismGateway is BaseGateway, IMessageClaimer { 9 | /// @notice Optimism message service on local chain 10 | IOptimismMessenger public immutable MESSAGE_SERVICE; 11 | 12 | /** 13 | * @dev This empty reserved space is put in place to allow future versions to add new 14 | * variables without shifting down storage in the inheritance chain. 15 | * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps 16 | */ 17 | uint256[50] private __gap; 18 | 19 | /// @dev Modifier to make sure the caller is the known message service. 20 | modifier onlyMessageService() { 21 | require(msg.sender == address(MESSAGE_SERVICE), "Not message service"); 22 | _; 23 | } 24 | 25 | /// @dev Modifier to make sure the original sender is gateway on remote chain. 26 | modifier onlyRemoteGateway() { 27 | require(MESSAGE_SERVICE.xDomainMessageSender() == remoteGateway, "Not remote gateway"); 28 | _; 29 | } 30 | 31 | constructor(IOptimismMessenger _messageService) { 32 | MESSAGE_SERVICE = _messageService; 33 | } 34 | 35 | function __OptimismGateway_init() internal onlyInitializing { 36 | __BaseGateway_init(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/gateway/optimism/OptimismL1Gateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {IArbitrator} from "../../interfaces/IArbitrator.sol"; 5 | import {IOptimismMessenger} from "../../interfaces/optimism/IOptimismMessenger.sol"; 6 | import {IMessageClaimer} from "../../interfaces/IMessageClaimer.sol"; 7 | import {OptimismGateway} from "./OptimismGateway.sol"; 8 | import {L1BaseGateway} from "../L1BaseGateway.sol"; 9 | 10 | contract OptimismL1Gateway is L1BaseGateway, OptimismGateway { 11 | constructor( 12 | IArbitrator _arbitrator, 13 | IOptimismMessenger _messageService 14 | ) L1BaseGateway(_arbitrator) OptimismGateway(_messageService) { 15 | _disableInitializers(); 16 | } 17 | 18 | function initialize() external initializer { 19 | __OptimismGateway_init(); 20 | } 21 | 22 | function sendMessage( 23 | uint256 _value, 24 | bytes calldata _callData, 25 | bytes calldata _adapterParams 26 | ) external payable onlyArbitrator { 27 | require(msg.value == _value, "Invalid value"); 28 | uint32 _minGasLimit = abi.decode(_adapterParams, (uint32)); 29 | bytes memory message = abi.encodeCall(IMessageClaimer.claimMessageCallback, (_value, _callData)); 30 | MESSAGE_SERVICE.sendMessage{value: _value}(remoteGateway, message, _minGasLimit); 31 | } 32 | 33 | function claimMessageCallback( 34 | uint256 _value, 35 | bytes calldata _callData 36 | ) external payable onlyMessageService onlyRemoteGateway { 37 | // Blast will return more value(the stake profit) than burned on L2 38 | require(msg.value >= _value, "Invalid value"); 39 | // Forward message to arbitrator 40 | ARBITRATOR.receiveMessage{value: _value}(_value, _callData); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /contracts/gateway/optimism/OptimismL2Gateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {IOptimismMessenger} from "../../interfaces/optimism/IOptimismMessenger.sol"; 5 | import {IMessageClaimer} from "../../interfaces/IMessageClaimer.sol"; 6 | import {OptimismGateway} from "./OptimismGateway.sol"; 7 | import {L2BaseGateway} from "../L2BaseGateway.sol"; 8 | 9 | contract OptimismL2Gateway is L2BaseGateway, OptimismGateway { 10 | /// @dev The L2CrossDomainMessenger deployed on OP 11 | /// see https://docs.optimism.io/chain/addresses 12 | IOptimismMessenger private constant L2_CROSS_DOMAIN_MESSENGER = 13 | IOptimismMessenger(0x4200000000000000000000000000000000000007); 14 | 15 | constructor(address _zkLink) L2BaseGateway(_zkLink) OptimismGateway(L2_CROSS_DOMAIN_MESSENGER) { 16 | _disableInitializers(); 17 | } 18 | 19 | function initialize() external initializer { 20 | __OptimismGateway_init(); 21 | } 22 | 23 | function sendMessage(uint256 _value, bytes calldata _callData) external payable override onlyZkLink { 24 | require(msg.value == _value, "Invalid fee"); 25 | 26 | bytes memory message = abi.encodeCall(IMessageClaimer.claimMessageCallback, (_value, _callData)); 27 | // `_minGasLimit` can be zero here as long as sufficient gas is provided 28 | // when `finalizeWithdrawalTransaction` is executed on layer one 29 | MESSAGE_SERVICE.sendMessage{value: _value}(remoteGateway, message, uint32(0)); 30 | emit L2GatewayMessageSent(_value, _callData); 31 | } 32 | 33 | function claimMessageCallback( 34 | uint256 _value, 35 | bytes calldata _callData 36 | ) external payable virtual onlyMessageService onlyRemoteGateway { 37 | require(msg.value == _value, "Invalid value"); 38 | 39 | // solhint-disable-next-line avoid-low-level-calls 40 | (bool success, ) = ZKLINK.call{value: _value}(_callData); 41 | require(success, "Call zkLink failed"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /contracts/gateway/scroll/ScrollGateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {IScrollMessenger} from "../../interfaces/scroll/IScrollMessenger.sol"; 5 | import {IMessageClaimer} from "../../interfaces/IMessageClaimer.sol"; 6 | import {BaseGateway} from "../BaseGateway.sol"; 7 | 8 | abstract contract ScrollGateway is BaseGateway, IMessageClaimer { 9 | /// @notice Scroll message service on local chain 10 | IScrollMessenger public immutable MESSAGE_SERVICE; 11 | 12 | /** 13 | * @dev This empty reserved space is put in place to allow future versions to add new 14 | * variables without shifting down storage in the inheritance chain. 15 | * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps 16 | */ 17 | uint256[50] private __gap; 18 | 19 | /// @dev Modifier to make sure the caller is the known message service. 20 | modifier onlyMessageService() { 21 | require(msg.sender == address(MESSAGE_SERVICE), "Not message service"); 22 | _; 23 | } 24 | 25 | /// @dev Modifier to make sure the original sender is gateway on remote chain. 26 | modifier onlyRemoteGateway() { 27 | require(MESSAGE_SERVICE.xDomainMessageSender() == remoteGateway, "Not remote gateway"); 28 | _; 29 | } 30 | 31 | constructor(IScrollMessenger _messageService) { 32 | MESSAGE_SERVICE = _messageService; 33 | } 34 | 35 | function __ScrollGateway_init() internal onlyInitializing { 36 | __BaseGateway_init(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/gateway/scroll/ScrollL1Gateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {IMessageClaimer} from "../../interfaces/IMessageClaimer.sol"; 5 | import {IScrollMessenger} from "../../interfaces/scroll/IScrollMessenger.sol"; 6 | import {IArbitrator} from "../../interfaces/IArbitrator.sol"; 7 | import {L1BaseGateway} from "../L1BaseGateway.sol"; 8 | import {ScrollGateway} from "./ScrollGateway.sol"; 9 | 10 | contract ScrollL1Gateway is ScrollGateway, L1BaseGateway { 11 | constructor( 12 | IArbitrator _arbitrator, 13 | IScrollMessenger _messageService 14 | ) L1BaseGateway(_arbitrator) ScrollGateway(_messageService) { 15 | _disableInitializers(); 16 | } 17 | 18 | function initialize() external initializer { 19 | __ScrollGateway_init(); 20 | } 21 | 22 | function sendMessage( 23 | uint256 _value, 24 | bytes calldata _callData, 25 | bytes calldata _adapterParams 26 | ) external payable override onlyArbitrator { 27 | uint256 _finalizeMessageGasLimit = abi.decode(_adapterParams, (uint256)); 28 | 29 | bytes memory executeData = abi.encodeCall(IMessageClaimer.claimMessageCallback, (_value, _callData)); 30 | MESSAGE_SERVICE.sendMessage{value: msg.value}( 31 | remoteGateway, 32 | _value, 33 | executeData, 34 | _finalizeMessageGasLimit, 35 | // solhint-disable-next-line avoid-tx-origin 36 | // The origin address paid the network fees of L2 on L1 37 | // So the origin address is set as the refund address for the excess network fees on L2. 38 | tx.origin 39 | ); 40 | } 41 | 42 | function claimMessageCallback( 43 | uint256 _value, 44 | bytes calldata _callData 45 | ) external payable override onlyMessageService onlyRemoteGateway { 46 | // no fee 47 | require(msg.value == _value, "Invalid value"); 48 | 49 | // Forward message to arbitrator 50 | ARBITRATOR.receiveMessage{value: msg.value}(_value, _callData); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /contracts/gateway/scroll/ScrollL2Gateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {IMessageClaimer} from "../../interfaces/IMessageClaimer.sol"; 5 | import {IScrollMessenger} from "../../interfaces/scroll/IScrollMessenger.sol"; 6 | import {L2BaseGateway} from "../L2BaseGateway.sol"; 7 | import {ScrollGateway} from "./ScrollGateway.sol"; 8 | 9 | contract ScrollL2Gateway is L2BaseGateway, ScrollGateway { 10 | constructor( 11 | address _zkLink, 12 | IScrollMessenger _messageService 13 | ) L2BaseGateway(_zkLink) ScrollGateway(_messageService) { 14 | _disableInitializers(); 15 | } 16 | 17 | function initialize() external initializer { 18 | __ScrollGateway_init(); 19 | } 20 | 21 | function sendMessage(uint256 _value, bytes calldata _callData) external payable override onlyZkLink { 22 | // no fee 23 | require(msg.value == _value, "Invalid value"); 24 | 25 | bytes memory callData = abi.encodeCall(IMessageClaimer.claimMessageCallback, (_value, _callData)); 26 | // transfer no fee to L1 27 | MESSAGE_SERVICE.sendMessage{value: _value}( 28 | remoteGateway, 29 | _value, 30 | callData, 31 | 0 // Gas limit required to complete the deposit on L1. This is optional, send 0 if you don’t want to set it. 32 | ); 33 | emit L2GatewayMessageSent(_value, _callData); 34 | } 35 | 36 | function claimMessageCallback( 37 | uint256 _value, 38 | bytes calldata _callData 39 | ) external payable override onlyMessageService onlyRemoteGateway { 40 | require(msg.value == _value, "Invalid value"); 41 | 42 | // solhint-disable-next-line avoid-low-level-calls 43 | (bool success, ) = ZKLINK.call{value: _value}(_callData); 44 | require(success, "Call zkLink failed"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /contracts/gateway/zkpolygon/ZkPolygonL1Gateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {IZkPolygon} from "../../interfaces/zkpolygon/IZkPolygon.sol"; 5 | import {IArbitrator} from "../../interfaces/IArbitrator.sol"; 6 | import {L1BaseGateway} from "../L1BaseGateway.sol"; 7 | import {IBridgeMessageReceiver} from "../../interfaces/zkpolygon/IBridgeMessageReceiver.sol"; 8 | import {BaseGateway} from "../BaseGateway.sol"; 9 | 10 | contract ZkPolygonL1Gateway is IBridgeMessageReceiver, L1BaseGateway, BaseGateway { 11 | /// @notice ZkPolygon message service on local chain 12 | IZkPolygon public immutable MESSAGE_SERVICE; 13 | 14 | /// @dev The destination network of Polygon zkEVM 15 | uint32 public constant ETH_NETWORK_ID = 1; 16 | // @dev Set to true for claiming asset on the destination network 17 | bool public constant FORCE_UPDATE_GLOBAL_EXIT_ROOT = true; 18 | 19 | modifier onlyMessageService() { 20 | require(msg.sender == address(MESSAGE_SERVICE), "Not remote gateway"); 21 | _; 22 | } 23 | 24 | constructor(IArbitrator _arbitrator, IZkPolygon _messageService) L1BaseGateway(_arbitrator) { 25 | _disableInitializers(); 26 | MESSAGE_SERVICE = _messageService; 27 | } 28 | 29 | function initialize() external initializer { 30 | __BaseGateway_init(); 31 | } 32 | 33 | function sendMessage(uint256 _value, bytes calldata _callData, bytes calldata) external payable onlyArbitrator { 34 | require(msg.value == _value, "Invalid value"); 35 | 36 | bytes memory executeData = abi.encode(_value, _callData); 37 | MESSAGE_SERVICE.bridgeMessage{value: msg.value}( 38 | ETH_NETWORK_ID, 39 | remoteGateway, 40 | FORCE_UPDATE_GLOBAL_EXIT_ROOT, 41 | executeData 42 | ); 43 | } 44 | 45 | function onMessageReceived( 46 | address originAddress, 47 | uint32, 48 | bytes calldata data 49 | ) external payable override onlyMessageService { 50 | require(originAddress == remoteGateway, "Invalid origin address"); 51 | 52 | (uint256 _value, bytes memory _callData) = abi.decode(data, (uint256, bytes)); 53 | require(msg.value == _value, "Invalid value"); 54 | 55 | ARBITRATOR.receiveMessage{value: _value}(_value, _callData); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /contracts/gateway/zkpolygon/ZkPolygonL2Gateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {IZkPolygon} from "../../interfaces/zkpolygon/IZkPolygon.sol"; 5 | import {IBridgeMessageReceiver} from "../../interfaces/zkpolygon/IBridgeMessageReceiver.sol"; 6 | import {L2BaseGateway} from "../L2BaseGateway.sol"; 7 | import {BaseGateway} from "../BaseGateway.sol"; 8 | 9 | contract ZkPolygonL2Gateway is IBridgeMessageReceiver, L2BaseGateway, BaseGateway { 10 | /// @notice ZkPolygon message service on local chain 11 | IZkPolygon public immutable MESSAGE_SERVICE; 12 | 13 | /// @dev The destination network of Ethereum 14 | uint32 public constant ETH_NETWORK_ID = 0; 15 | // @dev Set to true for claiming asset on the destination network 16 | bool public constant FORCE_UPDATE_GLOBAL_EXIT_ROOT = true; 17 | 18 | /// @dev Modifier to make sure the original sender is messageService on remote chain. 19 | modifier onlyMessageService() { 20 | require(msg.sender == address(MESSAGE_SERVICE), "Not remote gateway"); 21 | _; 22 | } 23 | 24 | constructor(address _zkLink, IZkPolygon _messageService) L2BaseGateway(_zkLink) { 25 | _disableInitializers(); 26 | MESSAGE_SERVICE = _messageService; 27 | } 28 | 29 | function initialize() external initializer { 30 | __BaseGateway_init(); 31 | } 32 | 33 | function sendMessage(uint256 _value, bytes calldata _callData) external payable onlyZkLink { 34 | // no fee 35 | require(msg.value == _value, "Invalid value"); 36 | 37 | bytes memory executeData = abi.encode(_value, _callData); 38 | MESSAGE_SERVICE.bridgeMessage{value: msg.value}( 39 | ETH_NETWORK_ID, 40 | remoteGateway, 41 | FORCE_UPDATE_GLOBAL_EXIT_ROOT, 42 | executeData 43 | ); 44 | emit L2GatewayMessageSent(_value, _callData); 45 | } 46 | 47 | function onMessageReceived( 48 | address originAddress, 49 | uint32, 50 | bytes calldata data 51 | ) external payable override onlyMessageService { 52 | require(originAddress == remoteGateway, "Invalid origin address"); 53 | (uint256 _value, bytes memory _callData) = abi.decode(data, (uint256, bytes)); 54 | require(msg.value == _value, "Invalid value"); 55 | 56 | (bool success, ) = ZKLINK.call{value: _value}(_callData); 57 | require(success, "Call zkLink failed"); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /contracts/gateway/zksync/ZkSyncL2Gateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {IL2ETHToken} from "../../interfaces/zksync/IL2ETHToken.sol"; 5 | import {L2BaseGateway} from "../L2BaseGateway.sol"; 6 | import {AddressAliasHelper} from "../../zksync/l1-contracts/vendor/AddressAliasHelper.sol"; 7 | import {IMessageClaimer} from "../../interfaces/IMessageClaimer.sol"; 8 | import {BaseGateway} from "../BaseGateway.sol"; 9 | 10 | contract ZkSyncL2Gateway is IMessageClaimer, L2BaseGateway, BaseGateway { 11 | uint160 internal constant SYSTEM_CONTRACTS_OFFSET = 0x8000; // 2^15 12 | 13 | /// @notice ZkSync eth bridge service on local chain 14 | IL2ETHToken public constant L2_ETH_ADDRESS = IL2ETHToken(address(SYSTEM_CONTRACTS_OFFSET + 0x0a)); 15 | 16 | /// @dev Modifier to make sure the original sender is gateway on remote chain. 17 | modifier onlyRemoteGateway() { 18 | require(AddressAliasHelper.undoL1ToL2Alias(msg.sender) == remoteGateway, "Not remote gateway"); 19 | _; 20 | } 21 | 22 | constructor(address _zkLink) L2BaseGateway(_zkLink) { 23 | _disableInitializers(); 24 | } 25 | 26 | function initialize() external initializer { 27 | __BaseGateway_init(); 28 | } 29 | 30 | function sendMessage(uint256 _value, bytes calldata _callData) external payable override onlyZkLink { 31 | // no fee 32 | require(msg.value == _value, "Invalid value"); 33 | 34 | bytes memory message = abi.encodePacked(_value, _callData); 35 | L2_ETH_ADDRESS.withdrawWithMessage{value: _value}(remoteGateway, message); 36 | emit L2GatewayMessageSent(_value, _callData); 37 | } 38 | 39 | function claimMessageCallback(uint256 _value, bytes calldata _callData) external payable onlyRemoteGateway { 40 | // when l1 to l2 executed failed then the l2 value will be refunded to L2 gateway 41 | // we can retry the failed tx on l1(without l2 value) so we should not check msg.value here 42 | // solhint-disable-next-line avoid-low-level-calls 43 | (bool success, ) = ZKLINK.call{value: _value}(_callData); 44 | require(success, "Call zkLink failed"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /contracts/interfaces/IArbitrator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {IL1Gateway} from "./IL1Gateway.sol"; 5 | 6 | interface IArbitrator { 7 | /// @notice Return true if relayer is active 8 | function isRelayerActive(address _relayer) external view returns (bool); 9 | 10 | /// @notice Return the primary chain gateway 11 | function primaryChainGateway() external view returns (IL1Gateway); 12 | 13 | /// @notice Enqueue message from EthereumGateway 14 | /// @dev Used by EthereumGateway to temporarily store message 15 | /// @param _value The msg value 16 | /// @param _callData The call data 17 | function enqueueMessage(uint256 _value, bytes calldata _callData) external payable; 18 | 19 | /// @notice Deliver message from one L1 gateway to another L1 gateway 20 | /// @dev Used by L1Gateways to deliver message 21 | /// @param _value The msg value 22 | /// @param _callData The call data 23 | function receiveMessage(uint256 _value, bytes calldata _callData) external payable; 24 | 25 | /// @notice Forward message from one L1 gateway to another L1 gateway 26 | /// @param _gateway The message source gateway 27 | /// @param _value The msg value 28 | /// @param _callData The call data 29 | /// @param _adapterParams Some params need to call canonical message service 30 | function forwardMessage( 31 | IL1Gateway _gateway, 32 | uint256 _value, 33 | bytes calldata _callData, 34 | bytes calldata _adapterParams 35 | ) external payable; 36 | 37 | /// @notice Claim a message of source chain and deliver it to the target chain 38 | /// @param _sourceChainCanonicalMessageService The message service to claim message 39 | /// @param _sourceChainClaimCallData The call data that need to claim message from source chain 40 | /// @param _sourceChainL1Gateway The msg.sender passed in the `receiveMessage` interface 41 | /// @param _receiveValue The value passed in the `receiveMessage` interface 42 | /// @param _receiveCallData The call data passed in the `receiveMessage` interface 43 | /// @param _forwardParams Some params need to call canonical message service of target chain 44 | function claimMessage( 45 | address _sourceChainCanonicalMessageService, 46 | bytes calldata _sourceChainClaimCallData, 47 | IL1Gateway _sourceChainL1Gateway, 48 | uint256 _receiveValue, 49 | bytes calldata _receiveCallData, 50 | bytes calldata _forwardParams 51 | ) external payable; 52 | 53 | /// @notice Set the pause state to stop forward message across chains 54 | function setPause(bool _paused) external; 55 | } 56 | -------------------------------------------------------------------------------- /contracts/interfaces/IFastSettlementMiddleware.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {IL1Gateway} from "./IL1Gateway.sol"; 5 | interface IFastSettlementMiddleware { 6 | /** 7 | * @notice Get the available stake value of an operator at a specific epoch 8 | * @param operator Operator address 9 | * @param epoch Epoch 10 | * @return Stake value 11 | */ 12 | function getOperatorStakeValue(address operator, uint48 epoch) external view returns (uint256); 13 | 14 | /** 15 | * @notice Get the available stake value for the operator at the current epoch 16 | * @param operator Operator address 17 | * @return Stake value 18 | */ 19 | function getOperatorStakeCurrentValue(address operator) external view returns (uint256); 20 | 21 | /** 22 | * @notice Set the pause status of arbitrator 23 | */ 24 | function pauseArbitrator(bool _paused) external; 25 | } 26 | -------------------------------------------------------------------------------- /contracts/interfaces/IGateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | interface IGateway { 5 | /// @return Remote gateway 6 | function getRemoteGateway() external view returns (address); 7 | } 8 | -------------------------------------------------------------------------------- /contracts/interfaces/IL1Gateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {IGateway} from "./IGateway.sol"; 5 | 6 | interface IL1Gateway is IGateway { 7 | /// @notice Send message to remote gateway 8 | /// @param _value The msg value 9 | /// @param _callData The call data 10 | /// @param _adapterParams Some params need to call canonical message service 11 | function sendMessage(uint256 _value, bytes calldata _callData, bytes calldata _adapterParams) external payable; 12 | } 13 | -------------------------------------------------------------------------------- /contracts/interfaces/IL2Gateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {IGateway} from "./IGateway.sol"; 5 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | 7 | interface IL2Gateway is IGateway { 8 | /// @notice Emit when sending a message 9 | event L2GatewayMessageSent(uint256 value, bytes callData); 10 | 11 | /// @notice Send message to remote gateway 12 | /// @param _value The msg value 13 | /// @param _callData The call data 14 | function sendMessage(uint256 _value, bytes calldata _callData) external payable; 15 | 16 | function isEthGasToken() external view returns (bool); 17 | 18 | function ethToken() external view returns (IERC20); 19 | } 20 | -------------------------------------------------------------------------------- /contracts/interfaces/IMessageClaimer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | interface IMessageClaimer { 5 | /// @notice Receive callback called by message service 6 | /// @param _value The message value 7 | /// @param _callData The message data 8 | function claimMessageCallback(uint256 _value, bytes calldata _callData) external payable; 9 | } 10 | -------------------------------------------------------------------------------- /contracts/interfaces/IZkLink.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /// @title ZkLink interface contract 6 | /// @author zk.link 7 | interface IZkLink { 8 | /// @notice Send l2 requests sync status to primary chain 9 | /// @param _newTotalSyncedPriorityTxs New sync point 10 | function syncL2Requests(uint256 _newTotalSyncedPriorityTxs) external payable; 11 | 12 | /// @notice Receive batch root from primary chain 13 | /// @param _batchNumber The batch number 14 | /// @param _l2LogsRootHash The L2 to L1 log root hash 15 | /// @param _forwardEthAmount The forward eth amount 16 | function syncBatchRoot(uint256 _batchNumber, bytes32 _l2LogsRootHash, uint256 _forwardEthAmount) external payable; 17 | 18 | /// @notice Receive range batch root hash from primary chain 19 | /// @param _fromBatchNumber The batch number from 20 | /// @param _toBatchNumber The batch number to 21 | /// @param _rangeBatchRootHash The accumulation hash of l2LogsRootHash in the range [`_fromBatchNumber`, `_toBatchNumber`] 22 | /// @param _forwardEthAmount The forward eth amount 23 | function syncRangeBatchRoot( 24 | uint256 _fromBatchNumber, 25 | uint256 _toBatchNumber, 26 | bytes32 _rangeBatchRootHash, 27 | uint256 _forwardEthAmount 28 | ) external payable; 29 | 30 | /// @notice Receive l2 tx hash from primary chain 31 | /// @param _l2TxHash The l2 tx hash on local chain 32 | /// @param _primaryChainL2TxHash The l2 tx hash on primary chain 33 | function syncL2TxHash(bytes32 _l2TxHash, bytes32 _primaryChainL2TxHash) external; 34 | } 35 | -------------------------------------------------------------------------------- /contracts/interfaces/linea/IL2MessageService.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | import {IMessageService} from "./IMessageService.sol"; 5 | 6 | interface IL2MessageService is IMessageService { 7 | /// @notice Returns the fee charged by Linea canonical message service when sending a message 8 | function minimumFeeInWei() external view returns (uint256); 9 | } 10 | -------------------------------------------------------------------------------- /contracts/interfaces/linea/IMessageService.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | interface IMessageService { 5 | /** 6 | * @notice Sends a message for transporting from the given chain. 7 | * @dev This function should be called with a msg.value = _value + _fee. The fee will be paid on the destination chain. 8 | * @param _to The destination address on the destination chain. 9 | * @param _fee The message service fee on the origin chain. 10 | * @param _calldata The calldata used by the destination message service to call the destination contract. 11 | */ 12 | function sendMessage(address _to, uint256 _fee, bytes calldata _calldata) external payable; 13 | 14 | /** 15 | * @notice Deliver a message to the destination chain. 16 | * @notice Is called automatically by the Postman, dApp or end user. 17 | * @param _from The msg.sender calling the origin message service. 18 | * @param _to The destination address on the destination chain. 19 | * @param _value The value to be transferred to the destination address. 20 | * @param _fee The message service fee on the origin chain. 21 | * @param _feeRecipient Address that will receive the fees. 22 | * @param _calldata The calldata used by the destination message service to call/forward to the destination contract. 23 | * @param _nonce Unique message number. 24 | */ 25 | function claimMessage( 26 | address _from, 27 | address _to, 28 | uint256 _fee, 29 | uint256 _value, 30 | address payable _feeRecipient, 31 | bytes calldata _calldata, 32 | uint256 _nonce 33 | ) external; 34 | 35 | /** 36 | * @notice Returns the original sender of the message on the origin layer. 37 | * @return The original sender of the message on the origin layer. 38 | */ 39 | function sender() external view returns (address); 40 | } 41 | -------------------------------------------------------------------------------- /contracts/interfaces/optimism/IOptimismMessenger.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | interface IOptimismMessenger { 5 | /// @notice Sends a message to some target address on the other chain. Note that if the call 6 | /// always reverts, then the message will be unrelayable, and any ETH sent will be 7 | /// permanently locked. The same will occur if the target on the other chain is 8 | /// considered unsafe (see the _isUnsafeTarget() function). 9 | /// @param _target Target contract or wallet address. 10 | /// @param _message Message to trigger the target address with. 11 | /// @param _minGasLimit Minimum gas limit that the message can be executed with. 12 | function sendMessage(address _target, bytes calldata _message, uint32 _minGasLimit) external payable; 13 | 14 | /// @notice Retrieves the address of the contract or wallet that initiated the currently 15 | /// executing message on the other chain. Will throw an error if there is no message 16 | /// currently being executed. Allows the recipient of a call to see who triggered it. 17 | /// @return Address of the sender of the currently executing message on the other chain. 18 | function xDomainMessageSender() external view returns (address); 19 | } 20 | -------------------------------------------------------------------------------- /contracts/interfaces/scroll/IScrollMessenger.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | interface IScrollMessenger { 5 | function messageQueue() external returns (address); 6 | 7 | /// @notice Send cross chain message from L1 to L2 or L2 to L1. 8 | /// @param target The address of account who receive the message. 9 | /// @param value The amount of ether passed when call target contract. 10 | /// @param message The content of the message. 11 | /// @param gasLimit Gas limit required to complete the message relay on corresponding chain. 12 | function sendMessage(address target, uint256 value, bytes calldata message, uint256 gasLimit) external payable; 13 | 14 | /// @notice Send cross chain message from L1 to L2 or L2 to L1. 15 | /// @param target The address of account who receive the message. 16 | /// @param value The amount of ether passed when call target contract. 17 | /// @param message The content of the message. 18 | /// @param gasLimit Gas limit required to complete the message relay on corresponding chain. 19 | /// @param refundAddress The address of account who will receive the refunded fee. 20 | function sendMessage( 21 | address target, 22 | uint256 value, 23 | bytes calldata message, 24 | uint256 gasLimit, 25 | address refundAddress 26 | ) external payable; 27 | 28 | /// @notice Return the sender of a cross domain message. 29 | function xDomainMessageSender() external view returns (address); 30 | } 31 | -------------------------------------------------------------------------------- /contracts/interfaces/zkpolygon/IBridgeMessageReceiver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | /** 5 | * @dev Define interface for PolygonZkEVM Bridge message receiver 6 | */ 7 | interface IBridgeMessageReceiver { 8 | function onMessageReceived(address originAddress, uint32 originNetwork, bytes memory data) external payable; 9 | } 10 | -------------------------------------------------------------------------------- /contracts/interfaces/zkpolygon/IZkPolygon.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | interface IZkPolygon { 5 | /** 6 | * @notice Bridge message and send ETH value 7 | * note User/UI must be aware of the existing/available networks when choosing the destination network 8 | * @param destinationNetwork Network destination 9 | * @param destinationAddress Address destination 10 | * @param forceUpdateGlobalExitRoot Indicates if the new global exit root is updated or not 11 | * @param metadata Message metadata 12 | */ 13 | function bridgeMessage( 14 | uint32 destinationNetwork, 15 | address destinationAddress, 16 | bool forceUpdateGlobalExitRoot, 17 | bytes calldata metadata 18 | ) external payable; 19 | } 20 | -------------------------------------------------------------------------------- /contracts/interfaces/zksync/IL2ETHToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /// @author Matter Labs 6 | interface IL2ETHToken { 7 | /// @notice Initiate the ETH withdrawal, funds will be available to claim on L1 `finalizeEthWithdrawal` method. 8 | /// @param _l1Receiver The address on L1 to receive the funds. 9 | function withdraw(address _l1Receiver) external payable; 10 | 11 | /// @notice Initiate the ETH withdrawal, with the sent message. The funds will be available to claim on L1 `finalizeEthWithdrawal` method. 12 | /// @param _l1Receiver The address on L1 to receive the funds. 13 | /// @param _additionalData Additional data to be sent to L1 with the withdrawal. 14 | function withdrawWithMessage(address _l1Receiver, bytes calldata _additionalData) external payable; 15 | } 16 | -------------------------------------------------------------------------------- /contracts/interfaces/zksync/IZkSyncL1Gateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity ^0.8.0; 3 | 4 | interface IZkSyncL1Gateway { 5 | /// @notice Finalize the message sent from ZkSyncL2Gateway 6 | /// @param _l2BatchNumber The L2 batch number where the message was processed 7 | /// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message 8 | /// @param _l2TxNumberInBatch The L2 transaction number in the batch, in which the log was sent 9 | /// @param _message The L2 withdraw data, stored in an L2 -> L1 message 10 | /// @param _merkleProof The Merkle proof of the inclusion L2 -> L1 message about withdrawal initialization 11 | function finalizeMessage( 12 | uint256 _l2BatchNumber, 13 | uint256 _l2MessageIndex, 14 | uint16 _l2TxNumberInBatch, 15 | bytes calldata _message, 16 | bytes32[] calldata _merkleProof 17 | ) external; 18 | } 19 | -------------------------------------------------------------------------------- /contracts/zksync/l1-contracts/bridge/interfaces/IL1Bridge.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /// @title L1 Bridge contract interface 6 | /// @author Matter Labs 7 | /// @custom:security-contact security@matterlabs.dev 8 | interface IL1Bridge { 9 | event DepositInitiated( 10 | bytes32 indexed l2DepositTxHash, 11 | address indexed from, 12 | address indexed to, 13 | address l1Token, 14 | uint256 amount 15 | ); 16 | 17 | event DepositToMergeInitiated( 18 | bytes32 indexed l2DepositTxHash, 19 | address indexed from, 20 | address indexed to, 21 | address l1Token, 22 | uint256 amount, 23 | bool toMerge 24 | ); 25 | 26 | event WithdrawalFinalized(address indexed to, address indexed l1Token, uint256 amount); 27 | 28 | event ClaimedFailedDeposit(address indexed to, address indexed l1Token, uint256 amount); 29 | 30 | function isWithdrawalFinalized(uint256 _l2BatchNumber, uint256 _l2MessageIndex) external view returns (bool); 31 | 32 | function deposit( 33 | address _l2Receiver, 34 | address _l1Token, 35 | uint256 _amount, 36 | uint256 _l2TxGasLimit, 37 | uint256 _l2TxGasPerPubdataByte, 38 | address _refundRecipient 39 | ) external payable returns (bytes32 txHash); 40 | 41 | function depositToMerge( 42 | address _l2Receiver, 43 | address _l1Token, 44 | uint256 _amount, 45 | uint256 _l2TxGasLimit, 46 | uint256 _l2TxGasPerPubdataByte, 47 | address _refundRecipient 48 | ) external payable returns (bytes32 txHash); 49 | 50 | function claimFailedDeposit( 51 | address _depositSender, 52 | address _l1Token, 53 | bytes32 _l2TxHash, 54 | uint256 _l2BatchNumber, 55 | uint256 _l2MessageIndex, 56 | uint16 _l2TxNumberInBatch, 57 | bytes32[] calldata _merkleProof 58 | ) external; 59 | 60 | function finalizeWithdrawal( 61 | uint256 _l2BatchNumber, 62 | uint256 _l2MessageIndex, 63 | uint16 _l2TxNumberInBatch, 64 | bytes calldata _message, 65 | bytes32[] calldata _merkleProof 66 | ) external; 67 | 68 | function l2TokenAddress(address _l1Token) external view returns (address); 69 | 70 | function l2Bridge() external view returns (address); 71 | } 72 | -------------------------------------------------------------------------------- /contracts/zksync/l1-contracts/bridge/interfaces/IL1BridgeLegacy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /// @title L1 Bridge contract legacy interface 6 | /// @author Matter Labs 7 | /// @custom:security-contact security@matterlabs.dev 8 | interface IL1BridgeLegacy { 9 | function deposit( 10 | address _l2Receiver, 11 | address _l1Token, 12 | uint256 _amount, 13 | uint256 _l2TxGasLimit, 14 | uint256 _l2TxGasPerPubdataByte 15 | ) external payable returns (bytes32 txHash); 16 | } 17 | -------------------------------------------------------------------------------- /contracts/zksync/l1-contracts/bridge/interfaces/IL2Bridge.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /// @author Matter Labs 6 | interface IL2Bridge { 7 | function finalizeDeposit( 8 | address _l1Sender, 9 | address _l2Receiver, 10 | address _l1Token, 11 | uint256 _amount, 12 | bytes calldata _data 13 | ) external payable; 14 | 15 | function finalizeDepositToMerge( 16 | address _l1Sender, 17 | address _l2Receiver, 18 | address _l1Token, 19 | uint256 _amount, 20 | bytes calldata _data 21 | ) external payable; 22 | 23 | function withdraw(address _l1Receiver, address _l2Token, uint256 _amount) external; 24 | 25 | function l1TokenAddress(address _l2Token) external view returns (address); 26 | 27 | function l2TokenAddress(address _l1Token) external view returns (address); 28 | 29 | function l1Bridge() external view returns (address); 30 | } 31 | -------------------------------------------------------------------------------- /contracts/zksync/l1-contracts/bridge/interfaces/IL2ERC20Bridge.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /// @author Matter Labs 6 | interface IL2ERC20Bridge { 7 | function initialize(address _l1Bridge, bytes32 _l2TokenProxyBytecodeHash, address _governor) external; 8 | } 9 | -------------------------------------------------------------------------------- /contracts/zksync/l1-contracts/bridge/libraries/BridgeInitializationHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "../../zksync/interfaces/IMailbox.sol"; 6 | import "../../vendor/AddressAliasHelper.sol"; 7 | import "../../common/libraries/L2ContractHelper.sol"; 8 | import {L2_DEPLOYER_SYSTEM_CONTRACT_ADDR} from "../../common/L2ContractAddresses.sol"; 9 | import "../../common/interfaces/IL2ContractDeployer.sol"; 10 | 11 | /// @author Matter Labs 12 | /// @custom:security-contact security@matterlabs.dev 13 | /// @dev A helper library for initializing L2 bridges in zkSync L2 network. 14 | library BridgeInitializationHelper { 15 | /// @dev The L2 gas limit for requesting L1 -> L2 transaction of deploying L2 bridge instance. 16 | /// @dev It is big enough to deploy any contract, so we can use the same value for all bridges. 17 | /// NOTE: this constant will be accurately calculated in the future. 18 | uint256 constant DEPLOY_L2_BRIDGE_COUNTERPART_GAS_LIMIT = 10000000; 19 | 20 | /// @dev The default l2GasPricePerPubdata to be used in bridges. 21 | uint256 constant REQUIRED_L2_GAS_PRICE_PER_PUBDATA = 800; 22 | 23 | /// @notice Requests L2 transaction that will deploy a contract with a given bytecode hash and constructor data. 24 | /// NOTE: it is always used to deploy via create2 with ZERO salt 25 | /// @param _zkSync The address of the zkSync contract 26 | /// @param _deployTransactionFee The fee that will be paid for the L1 -> L2 transaction 27 | /// @param _bytecodeHash The hash of the bytecode of the contract to be deployed 28 | /// @param _constructorData The data to be passed to the contract constructor 29 | /// @param _factoryDeps A list of raw bytecodes that are needed for deployment 30 | function requestDeployTransaction( 31 | IMailbox _zkSync, 32 | uint256 _deployTransactionFee, 33 | bytes32 _bytecodeHash, 34 | bytes memory _constructorData, 35 | bytes[] memory _factoryDeps 36 | ) internal returns (address deployedAddress) { 37 | bytes memory deployCalldata = abi.encodeCall( 38 | IL2ContractDeployer.create2, 39 | (bytes32(0), _bytecodeHash, _constructorData) 40 | ); 41 | _zkSync.requestL2Transaction{value: _deployTransactionFee}( 42 | L2_DEPLOYER_SYSTEM_CONTRACT_ADDR, 43 | 0, 44 | deployCalldata, 45 | DEPLOY_L2_BRIDGE_COUNTERPART_GAS_LIMIT, 46 | REQUIRED_L2_GAS_PRICE_PER_PUBDATA, 47 | _factoryDeps, 48 | msg.sender 49 | ); 50 | 51 | deployedAddress = L2ContractHelper.computeCreate2Address( 52 | // Apply the alias to the address of the bridge contract, to get the `msg.sender` in L2. 53 | AddressAliasHelper.applyL1ToL2Alias(address(this)), 54 | bytes32(0), // Zero salt 55 | _bytecodeHash, 56 | keccak256(_constructorData) 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /contracts/zksync/l1-contracts/common/L2ContractAddresses.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /// @dev The address of the L2 deployer system contract. 6 | address constant L2_DEPLOYER_SYSTEM_CONTRACT_ADDR = address(0x8006); 7 | 8 | /// @dev The special reserved L2 address. It is located in the system contracts space but doesn't have deployed 9 | /// bytecode. 10 | /// @dev The L2 deployer system contract allows changing bytecodes on any address if the `msg.sender` is this address. 11 | /// @dev So, whenever the governor wants to redeploy system contracts, it just initiates the L1 upgrade call deployer 12 | /// system contract 13 | /// via the L1 -> L2 transaction with `sender == L2_FORCE_DEPLOYER_ADDR`. For more details see the 14 | /// `diamond-initializers` contracts. 15 | address constant L2_FORCE_DEPLOYER_ADDR = address(0x8007); 16 | 17 | /// @dev The address of the special smart contract that can send arbitrary length message as an L2 log 18 | address constant L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR = address(0x8008); 19 | 20 | /// @dev The formal address of the initial program of the system: the bootloader 21 | address constant L2_BOOTLOADER_ADDRESS = address(0x8001); 22 | 23 | /// @dev The address of the eth token system contract 24 | address constant L2_ETH_TOKEN_SYSTEM_CONTRACT_ADDR = address(0x800a); 25 | 26 | /// @dev The address of the known code storage system contract 27 | address constant L2_KNOWN_CODE_STORAGE_SYSTEM_CONTRACT_ADDR = address(0x8004); 28 | 29 | /// @dev The address of the context system contract 30 | address constant L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR = address(0x800b); 31 | -------------------------------------------------------------------------------- /contracts/zksync/l1-contracts/common/ReentrancyGuard.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @custom:security-contact security@matterlabs.dev 7 | * @dev Contract module that helps prevent reentrant calls to a function. 8 | * 9 | * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier 10 | * available, which can be applied to functions to make sure there are no nested 11 | * (reentrant) calls to them. 12 | * 13 | * Note that because there is a single `nonReentrant` guard, functions marked as 14 | * `nonReentrant` may not call one another. This can be worked around by making 15 | * those functions `private`, and then adding `external` `nonReentrant` entry 16 | * points to them. 17 | * 18 | * TIP: If you would like to learn more about reentrancy and alternative ways 19 | * to protect against it, check out our blog post 20 | * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. 21 | * 22 | * _Since v2.5.0:_ this module is now much more gas efficient, given net gas 23 | * metering changes introduced in the Istanbul hardfork. 24 | */ 25 | abstract contract ReentrancyGuard { 26 | /// @dev Address of lock flag variable. 27 | /// @dev Flag is placed at random memory location to not interfere with Storage contract. 28 | // keccak256("ReentrancyGuard") - 1; 29 | uint256 private constant LOCK_FLAG_ADDRESS = 0x8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4; 30 | 31 | // solhint-disable-next-line max-line-length 32 | // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/566a774222707e424896c0c390a84dc3c13bdcb2/contracts/security/ReentrancyGuard.sol 33 | // The values being non-zero value makes deployment a bit more expensive, 34 | // but in exchange the refund on every call to nonReentrant will be lower in 35 | // amount. Since refunds are capped to a percentage of the total 36 | // transaction's gas, it is best to keep them low in cases like this one, to 37 | // increase the likelihood of the full refund coming into effect. 38 | uint256 private constant _NOT_ENTERED = 1; 39 | uint256 private constant _ENTERED = 2; 40 | 41 | modifier reentrancyGuardInitializer() { 42 | _initializeReentrancyGuard(); 43 | _; 44 | } 45 | 46 | function _initializeReentrancyGuard() private { 47 | uint256 lockSlotOldValue; 48 | 49 | // Storing an initial non-zero value makes deployment a bit more 50 | // expensive but in exchange every call to nonReentrant 51 | // will be cheaper. 52 | assembly { 53 | lockSlotOldValue := sload(LOCK_FLAG_ADDRESS) 54 | sstore(LOCK_FLAG_ADDRESS, _NOT_ENTERED) 55 | } 56 | 57 | // Check that storage slot for reentrancy guard is empty to rule out possibility of slot conflict 58 | require(lockSlotOldValue == 0, "1B"); 59 | } 60 | 61 | /** 62 | * @dev Prevents a contract from calling itself, directly or indirectly. 63 | * Calling a `nonReentrant` function from another `nonReentrant` 64 | * function is not supported. It is possible to prevent this from happening 65 | * by making the `nonReentrant` function external, and make it call a 66 | * `private` function that does the actual work. 67 | */ 68 | modifier nonReentrant() { 69 | uint256 _status; 70 | assembly { 71 | _status := sload(LOCK_FLAG_ADDRESS) 72 | } 73 | 74 | // On the first call to nonReentrant, _notEntered will be true 75 | require(_status == _NOT_ENTERED, "r1"); 76 | 77 | // Any calls to nonReentrant after this point will fail 78 | assembly { 79 | sstore(LOCK_FLAG_ADDRESS, _ENTERED) 80 | } 81 | 82 | _; 83 | 84 | // By storing the original value once again, a refund is triggered (see 85 | // https://eips.ethereum.org/EIPS/eip-2200) 86 | assembly { 87 | sstore(LOCK_FLAG_ADDRESS, _NOT_ENTERED) 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /contracts/zksync/l1-contracts/common/interfaces/IL2ContractDeployer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @author Matter Labs 7 | * @notice System smart contract that is responsible for deploying other smart contracts on zkSync. 8 | */ 9 | interface IL2ContractDeployer { 10 | /// @notice A struct that describes a forced deployment on an address. 11 | /// @param bytecodeHash The bytecode hash to put on an address. 12 | /// @param newAddress The address on which to deploy the bytecodehash to. 13 | /// @param callConstructor Whether to run the constructor on the force deployment. 14 | /// @param value The `msg.value` with which to initialize a contract. 15 | /// @param input The constructor calldata. 16 | struct ForceDeployment { 17 | bytes32 bytecodeHash; 18 | address newAddress; 19 | bool callConstructor; 20 | uint256 value; 21 | bytes input; 22 | } 23 | 24 | /// @notice This method is to be used only during an upgrade to set bytecodes on specific addresses. 25 | function forceDeployOnAddresses(ForceDeployment[] calldata _deployParams) external; 26 | 27 | /// @notice Deploys a contract with similar address derivation rules to the EVM's `CREATE2` opcode. 28 | /// @param _salt The create2 salt. 29 | /// @param _bytecodeHash The correctly formatted hash of the bytecode. 30 | /// @param _input The constructor calldata. 31 | function create2(bytes32 _salt, bytes32 _bytecodeHash, bytes calldata _input) external; 32 | } 33 | -------------------------------------------------------------------------------- /contracts/zksync/l1-contracts/common/libraries/L2ContractHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @author Matter Labs 7 | * @custom:security-contact security@matterlabs.dev 8 | * @notice Helper library for working with L2 contracts on L1. 9 | */ 10 | library L2ContractHelper { 11 | /// @dev The prefix used to create CREATE2 addresses. 12 | bytes32 private constant CREATE2_PREFIX = keccak256("zksyncCreate2"); 13 | 14 | /// @notice Validate the bytecode format and calculate its hash. 15 | /// @param _bytecodeLength The bytecode length. 16 | /// @param _bytecodeHash The bytecode hash. 17 | /// @return hashedBytecode The 32-byte hash of the bytecode. 18 | /// Note: The function reverts the execution if the bytecode has non expected format: 19 | /// - Bytecode bytes length is not a multiple of 32 20 | /// - Bytecode bytes length is not less than 2^21 bytes (2^16 words) 21 | /// - Bytecode words length is not odd 22 | function hashL2Bytecode( 23 | uint256 _bytecodeLength, 24 | bytes32 _bytecodeHash 25 | ) internal pure returns (bytes32 hashedBytecode) { 26 | // Note that the length of the bytecode must be provided in 32-byte words. 27 | require(_bytecodeLength % 32 == 0, "pq"); 28 | 29 | uint256 bytecodeLenInWords = _bytecodeLength / 32; 30 | require(bytecodeLenInWords < 2 ** 16, "pp"); // bytecode length must be less than 2^16 words 31 | require(bytecodeLenInWords % 2 == 1, "ps"); // bytecode length in words must be odd 32 | hashedBytecode = _bytecodeHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; 33 | // Setting the version of the hash 34 | hashedBytecode = (hashedBytecode | bytes32(uint256(1 << 248))); 35 | // Setting the length 36 | hashedBytecode = hashedBytecode | bytes32(bytecodeLenInWords << 224); 37 | } 38 | 39 | /// @notice Validates the format of the given bytecode hash. 40 | /// @dev Due to the specification of the L2 bytecode hash, not every 32 bytes could be a legit bytecode hash. 41 | /// @dev The function reverts on invalid bytecode hash formam. 42 | /// @param _bytecodeHash The hash of the bytecode to validate. 43 | function validateBytecodeHash(bytes32 _bytecodeHash) internal pure { 44 | uint8 version = uint8(_bytecodeHash[0]); 45 | require(version == 1 && _bytecodeHash[1] == bytes1(0), "zf"); // Incorrectly formatted bytecodeHash 46 | 47 | require(_bytecodeLen(_bytecodeHash) % 2 == 1, "uy"); // Code length in words must be odd 48 | } 49 | 50 | /// @notice Returns the length of the bytecode associated with the given hash. 51 | /// @param _bytecodeHash The hash of the bytecode. 52 | /// @return codeLengthInWords The length of the bytecode in words. 53 | function _bytecodeLen(bytes32 _bytecodeHash) private pure returns (uint256 codeLengthInWords) { 54 | codeLengthInWords = uint256(uint8(_bytecodeHash[2])) * 256 + uint256(uint8(_bytecodeHash[3])); 55 | } 56 | 57 | /// @notice Computes the create2 address for a Layer 2 contract. 58 | /// @param _sender The address of the sender. 59 | /// @param _salt The salt value to use in the create2 address computation. 60 | /// @param _bytecodeHash The contract bytecode hash. 61 | /// @param _constructorInputHash The hash of the constructor input data. 62 | /// @return The create2 address of the contract. 63 | /// NOTE: L2 create2 derivation is different from L1 derivation! 64 | function computeCreate2Address( 65 | address _sender, 66 | bytes32 _salt, 67 | bytes32 _bytecodeHash, 68 | bytes32 _constructorInputHash 69 | ) internal pure returns (address) { 70 | bytes32 senderBytes = bytes32(uint256(uint160(_sender))); 71 | bytes32 data = keccak256( 72 | bytes.concat(CREATE2_PREFIX, senderBytes, _salt, _bytecodeHash, _constructorInputHash) 73 | ); 74 | 75 | return address(uint160(uint256(data))); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /contracts/zksync/l1-contracts/common/libraries/UncheckedMath.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @author Matter Labs 7 | * @custom:security-contact security@matterlabs.dev 8 | * @notice The library for unchecked math. 9 | */ 10 | library UncheckedMath { 11 | function uncheckedInc(uint256 _number) internal pure returns (uint256) { 12 | unchecked { 13 | return _number + 1; 14 | } 15 | } 16 | 17 | function uncheckedAdd(uint256 _lhs, uint256 _rhs) internal pure returns (uint256) { 18 | unchecked { 19 | return _lhs + _rhs; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /contracts/zksync/l1-contracts/common/libraries/UnsafeBytes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @author Matter Labs 7 | * @custom:security-contact security@matterlabs.dev 8 | * @dev The library provides a set of functions that help read data from an "abi.encodePacked" byte array. 9 | * @dev Each of the functions accepts the `bytes memory` and the offset where data should be read and returns a value of a certain type. 10 | * 11 | * @dev WARNING! 12 | * 1) Functions don't check the length of the bytes array, so it can go out of bounds. 13 | * The user of the library must check for bytes length before using any functions from the library! 14 | * 15 | * 2) Read variables are not cleaned up - https://docs.soliditylang.org/en/v0.8.16/internals/variable_cleanup.html. 16 | * Using data in inline assembly can lead to unexpected behavior! 17 | */ 18 | library UnsafeBytes { 19 | function readUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32 result, uint256 offset) { 20 | assembly { 21 | offset := add(_start, 4) 22 | result := mload(add(_bytes, offset)) 23 | } 24 | } 25 | 26 | function readAddress(bytes memory _bytes, uint256 _start) internal pure returns (address result, uint256 offset) { 27 | assembly { 28 | offset := add(_start, 20) 29 | result := mload(add(_bytes, offset)) 30 | } 31 | } 32 | 33 | function readUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256 result, uint256 offset) { 34 | assembly { 35 | offset := add(_start, 32) 36 | result := mload(add(_bytes, offset)) 37 | } 38 | } 39 | 40 | function readBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32 result, uint256 offset) { 41 | assembly { 42 | offset := add(_start, 32) 43 | result := mload(add(_bytes, offset)) 44 | } 45 | } 46 | 47 | // Original source code: https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol#L228 48 | // Get slice from bytes arrays 49 | // Returns the newly created 'bytes memory' 50 | // NOTE: theoretically possible overflow of (_start + _length) 51 | function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) { 52 | require(_bytes.length >= (_start + _length), "Z"); // bytes length is less then start byte + length bytes 53 | 54 | bytes memory tempBytes = new bytes(_length); 55 | 56 | if (_length != 0) { 57 | assembly { 58 | let slice_curr := add(tempBytes, 0x20) 59 | let slice_end := add(slice_curr, _length) 60 | 61 | for { 62 | let array_current := add(_bytes, add(_start, 0x20)) 63 | } lt(slice_curr, slice_end) { 64 | slice_curr := add(slice_curr, 0x20) 65 | array_current := add(array_current, 0x20) 66 | } { 67 | mstore(slice_curr, mload(array_current)) 68 | } 69 | } 70 | } 71 | 72 | return tempBytes; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /contracts/zksync/l1-contracts/governance/IGovernance.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /// @title Governance contract interface 6 | /// @author Matter Labs 7 | /// @custom:security-contact security@matterlabs.dev 8 | interface IGovernance { 9 | /// @dev This enumeration includes the following states: 10 | /// @param Unset Default state, indicating the operation has not been set. 11 | /// @param Waiting The operation is scheduled but not yet ready to be executed. 12 | /// @param Ready The operation is ready to be executed. 13 | /// @param Done The operation has been successfully executed. 14 | enum OperationState { 15 | Unset, 16 | Waiting, 17 | Ready, 18 | Done 19 | } 20 | 21 | /// @dev Represents a call to be made during an operation. 22 | /// @param target The address to which the call will be made. 23 | /// @param value The amount of Ether (in wei) to be sent along with the call. 24 | /// @param data The calldata to be executed on the `target` address. 25 | struct Call { 26 | address target; 27 | uint256 value; 28 | bytes data; 29 | } 30 | 31 | /// @dev Defines the structure of an operation that Governance executes. 32 | /// @param calls An array of `Call` structs, each representing a call to be made during the operation. 33 | /// @param predecessor The hash of the predecessor operation, that should be executed before this operation. 34 | /// @param salt A bytes32 value used for creating unique operation hashes. 35 | struct Operation { 36 | Call[] calls; 37 | bytes32 predecessor; 38 | bytes32 salt; 39 | } 40 | 41 | function isOperation(bytes32 _id) external view returns (bool); 42 | 43 | function isOperationPending(bytes32 _id) external view returns (bool); 44 | 45 | function isOperationReady(bytes32 _id) external view returns (bool); 46 | 47 | function isOperationDone(bytes32 _id) external view returns (bool); 48 | 49 | function getOperationState(bytes32 _id) external view returns (OperationState); 50 | 51 | function scheduleTransparent(Operation calldata _operation, uint256 _delay) external; 52 | 53 | function scheduleShadow(bytes32 _id, uint256 _delay) external; 54 | 55 | function cancel(bytes32 _id) external; 56 | 57 | function execute(Operation calldata _operation) external payable; 58 | 59 | function executeInstant(Operation calldata _operation) external payable; 60 | 61 | function hashOperation(Operation calldata _operation) external pure returns (bytes32); 62 | 63 | function updateDelay(uint256 _newDelay) external; 64 | 65 | function updateSecurityCouncil(address _newSecurityCouncil) external; 66 | 67 | /// @notice Emitted when transparent operation is scheduled. 68 | event TransparentOperationScheduled(bytes32 indexed _id, uint256 delay, Operation _operation); 69 | 70 | /// @notice Emitted when shadow operation is scheduled. 71 | event ShadowOperationScheduled(bytes32 indexed _id, uint256 delay); 72 | 73 | /// @notice Emitted when the operation is executed with delay or instantly. 74 | event OperationExecuted(bytes32 indexed _id); 75 | 76 | /// @notice Emitted when the security council address is changed. 77 | event ChangeSecurityCouncil(address _securityCouncilBefore, address _securityCouncilAfter); 78 | 79 | /// @notice Emitted when the minimum delay for future operations is modified. 80 | event ChangeMinDelay(uint256 _delayBefore, uint256 _delayAfter); 81 | 82 | /// @notice Emitted when the operation with specified id is cancelled. 83 | event OperationCancelled(bytes32 indexed _id); 84 | } 85 | -------------------------------------------------------------------------------- /contracts/zksync/l1-contracts/vendor/AddressAliasHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | /* 4 | * Copyright 2019-2021, Offchain Labs, Inc. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | pragma solidity ^0.8.0; 20 | 21 | library AddressAliasHelper { 22 | uint160 internal constant OFFSET = uint160(0x1111000000000000000000000000000000001111); 23 | 24 | /// @notice Utility function converts the address that submitted a tx 25 | /// to the inbox on L1 to the msg.sender viewed on L2 26 | /// @param l1Address the address in the L1 that triggered the tx to L2 27 | /// @return l2Address L2 address as viewed in msg.sender 28 | function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) { 29 | unchecked { 30 | l2Address = address(uint160(l1Address) + OFFSET); 31 | } 32 | } 33 | 34 | /// @notice Utility function that converts the msg.sender viewed on L2 to the 35 | /// address that submitted a tx to the inbox on L1 36 | /// @param l2Address L2 address as viewed in msg.sender 37 | /// @return l1Address the address in the L1 that triggered the tx to L2 38 | function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) { 39 | unchecked { 40 | l1Address = address(uint160(l2Address) - OFFSET); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /contracts/zksync/l1-contracts/zksync/Storage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /// @dev The log passed from L2 6 | /// @param l2ShardId The shard identifier, 0 - rollup, 1 - porter. All other values are not used but are reserved for 7 | /// the future 8 | /// @param isService A boolean flag that is part of the log along with `key`, `value`, and `sender` address. 9 | /// This field is required formally but does not have any special meaning. 10 | /// @param txNumberInBatch The L2 transaction number in the batch, in which the log was sent 11 | /// @param sender The L2 address which sent the log 12 | /// @param key The 32 bytes of information that was sent in the log 13 | /// @param value The 32 bytes of information that was sent in the log 14 | // Both `key` and `value` are arbitrary 32-bytes selected by the log sender 15 | struct L2Log { 16 | uint8 l2ShardId; 17 | bool isService; 18 | uint16 txNumberInBatch; 19 | address sender; 20 | bytes32 key; 21 | bytes32 value; 22 | } 23 | 24 | /// @dev An arbitrary length message passed from L2 25 | /// @notice Under the hood it is `L2Log` sent from the special system L2 contract 26 | /// @param txNumberInBatch The L2 transaction number in the batch, in which the message was sent 27 | /// @param sender The address of the L2 account from which the message was passed 28 | /// @param data An arbitrary length message 29 | struct L2Message { 30 | uint16 txNumberInBatch; 31 | address sender; 32 | bytes data; 33 | } 34 | 35 | /// @notice The struct that describes whether users will be charged for pubdata for L1->L2 transactions. 36 | /// @param Rollup The users are charged for pubdata & it is priced based on the gas price on Ethereum. 37 | /// @param Validium The pubdata is considered free with regard to the L1 gas price. 38 | enum PubdataPricingMode { 39 | Rollup, 40 | Validium 41 | } 42 | 43 | /// @notice The fee params for L1->L2 transactions for the network. 44 | /// @param pubdataPricingMode How the users will charged for pubdata in L1->L2 transactions. 45 | /// @param batchOverheadL1Gas The amount of L1 gas required to process the batch (except for the calldata). 46 | /// @param maxPubdataPerBatch The maximal number of pubdata that can be emitted per batch. 47 | /// @param priorityTxMaxPubdata The maximal amount of pubdata a priority transaction is allowed to publish. 48 | /// It can be slightly less than maxPubdataPerBatch in order to have some margin for the bootloader execution. 49 | /// @param minimalL2GasPrice The minimal L2 gas price to be used by L1->L2 transactions. It should represent 50 | /// the price that a single unit of compute costs. 51 | struct FeeParams { 52 | PubdataPricingMode pubdataPricingMode; 53 | uint32 batchOverheadL1Gas; 54 | uint32 maxPubdataPerBatch; 55 | uint32 maxL2GasPerBatch; 56 | uint32 priorityTxMaxPubdata; 57 | uint64 minimalL2GasPrice; 58 | } 59 | 60 | /// @dev The sync status for priority op of secondary chain 61 | /// @param hash The cumulative canonicalTxHash 62 | /// @param amount The cumulative l2 value 63 | struct SecondaryChainSyncStatus { 64 | bytes32 hash; 65 | uint256 amount; 66 | } 67 | -------------------------------------------------------------------------------- /contracts/zksync/l1-contracts/zksync/interfaces/IAdmin.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import {FeeParams} from "../Storage.sol"; 6 | import {IL2Gateway} from "../../../../interfaces/IL2Gateway.sol"; 7 | 8 | /// @title The interface of the Admin Contract that controls access rights for contract management. 9 | /// @author Matter Labs 10 | /// @custom:security-contact security@matterlabs.dev 11 | interface IAdmin { 12 | /// @notice Init gateway 13 | /// @param _gateway The gateway on local chain 14 | function setGateway(IL2Gateway _gateway) external; 15 | 16 | /// @notice Change validator status (active or not active) 17 | /// @param _validator Validator address 18 | /// @param _active Active flag 19 | function setValidator(address _validator, bool _active) external; 20 | 21 | /// @notice Change the fee params for L1->L2 transactions 22 | /// @param _newFeeParams The new fee params 23 | function changeFeeParams(FeeParams calldata _newFeeParams) external; 24 | } 25 | -------------------------------------------------------------------------------- /contracts/zksync/l1-contracts/zksync/interfaces/IGetters.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import {IL2Gateway} from "../../../../interfaces/IL2Gateway.sol"; 6 | 7 | /// @title The interface of the Getters Contract that implements functions for getting contract state from outside the blockchain. 8 | /// @author Matter Labs 9 | /// @custom:security-contact security@matterlabs.dev 10 | interface IGetters { 11 | /*////////////////////////////////////////////////////////////// 12 | CUSTOM GETTERS 13 | //////////////////////////////////////////////////////////////*/ 14 | 15 | /// @return The gateway on local chain 16 | function getGateway() external view returns (IL2Gateway); 17 | 18 | /// @return The address of the current governor 19 | function getGovernor() external view returns (address); 20 | 21 | /// @return The total number of batches that were committed & verified & executed 22 | function getTotalBatchesExecuted() external view returns (uint256); 23 | 24 | /// @return The total number of priority operations that were added to the priority queue, including all processed ones 25 | function getTotalPriorityTxs() external view returns (uint256); 26 | 27 | /// @return Whether the address has a validator access 28 | function isValidator(address _address) external view returns (bool); 29 | 30 | /// @return merkleRoot Merkle root of the tree with L2 logs for the selected batch 31 | function l2LogsRootHash(uint256 _batchNumber) external view returns (bytes32 merkleRoot); 32 | 33 | /// @return The maximum number of L2 gas that a user can request for L1 -> L2 transactions 34 | function getPriorityTxMaxGasLimit() external view returns (uint256); 35 | 36 | /// @return Whether a withdrawal has been finalized. 37 | /// @param _l2BatchNumber The L2 batch number within which the withdrawal happened. 38 | /// @param _l2MessageIndex The index of the L2->L1 message denoting the withdrawal. 39 | function isEthWithdrawalFinalized(uint256 _l2BatchNumber, uint256 _l2MessageIndex) external view returns (bool); 40 | } 41 | -------------------------------------------------------------------------------- /contracts/zksync/l1-contracts/zksync/interfaces/IZkSync.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | interface IZkSync { 6 | /// @notice Update secondary chain status 7 | /// @param _gateway The secondary chain gateway 8 | /// @param _active Active flag 9 | function setSecondaryChainGateway(address _gateway, bool _active) external; 10 | 11 | /// @notice Receive sync status from secondary chain 12 | /// @param _secondaryChainGateway The secondary chain gateway address 13 | /// @param _newTotalSyncedPriorityTxs New sync point 14 | /// @param _syncHash New sync hash 15 | /// @param _forwardEthAmount The difference eth amount between two sync points 16 | function syncL2Requests( 17 | address _secondaryChainGateway, 18 | uint256 _newTotalSyncedPriorityTxs, 19 | bytes32 _syncHash, 20 | uint256 _forwardEthAmount 21 | ) external payable; 22 | } 23 | -------------------------------------------------------------------------------- /contracts/zksync/l1-contracts/zksync/libraries/Merkle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import {UncheckedMath} from "../../common/libraries/UncheckedMath.sol"; 6 | 7 | /// @author Matter Labs 8 | /// @custom:security-contact security@matterlabs.dev 9 | library Merkle { 10 | using UncheckedMath for uint256; 11 | 12 | /// @dev Calculate Merkle root by the provided Merkle proof. 13 | /// NOTE: When using this function, check that the _path length is equal to the tree height to prevent shorter/longer paths attack 14 | /// @param _path Merkle path from the leaf to the root 15 | /// @param _index Leaf index in the tree 16 | /// @param _itemHash Hash of leaf content 17 | /// @return The Merkle root 18 | function calculateRoot( 19 | bytes32[] calldata _path, 20 | uint256 _index, 21 | bytes32 _itemHash 22 | ) internal pure returns (bytes32) { 23 | uint256 pathLength = _path.length; 24 | require(pathLength > 0, "xc"); 25 | require(pathLength < 256, "bt"); 26 | require(_index < (1 << pathLength), "px"); 27 | 28 | bytes32 currentHash = _itemHash; 29 | for (uint256 i; i < pathLength; i = i.uncheckedInc()) { 30 | currentHash = (_index % 2 == 0) 31 | ? _efficientHash(currentHash, _path[i]) 32 | : _efficientHash(_path[i], currentHash); 33 | _index /= 2; 34 | } 35 | 36 | return currentHash; 37 | } 38 | 39 | /// @dev Keccak hash of the concatenation of two 32-byte words 40 | function _efficientHash(bytes32 _lhs, bytes32 _rhs) internal pure returns (bytes32 result) { 41 | assembly { 42 | mstore(0x00, _lhs) 43 | mstore(0x20, _rhs) 44 | result := keccak256(0x00, 0x40) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /docs/SecurityCheck.md: -------------------------------------------------------------------------------- 1 | ## File 2 | 3 | P1 - SPDX header 4 | 5 | P2 - Remove unused imported contract 6 | 7 | P3 - Remove unused code 8 | 9 | P4 - Remove solhints that aren't needed 10 | 11 | ## Contract 12 | 13 | T1 - Outdated compiler version not used (SWC-102) 14 | 15 | T2 - Use fixed compiler version to compile contract (SWC-103) 16 | 17 | T3 - Check for correct inheritance, keep it simple and linear (SWC-125) 18 | 19 | T4 - Constructor should not be existed in proxyed contract 20 | 21 | T5 - Right-To-Left-Override control character not used (SWC-130) 22 | 23 | ## Variables 24 | 25 | V1 - Is visibility set (SWC-108) 26 | 27 | V2 - Can they be private? 28 | 29 | V3 - Can it be constant? 30 | 31 | V4 - Can it be immutable? 32 | 33 | V5 - No unused variables (SWC-131) 34 | 35 | V6 - Can be a smaller type? 36 | 37 | V7 - Storage multiple variables in a slot 38 | 39 | V8 - Don't initialize variables of proxyed contract 40 | 41 | V9 - Don't delete or change variables order of proxyed contract 42 | 43 | ## Events 44 | 45 | E1 - Should any argument be indexed 46 | 47 | E2 - Don't abuse indexed attribute 48 | 49 | ## Modifiers 50 | 51 | M1 - No state changes (except for a reentrancy lock) 52 | 53 | M2 - No external calls 54 | 55 | M3 - Checks only 56 | 57 | M4 - Should use inline function instead of modifier to reduce contract bytecode size? 58 | 59 | ## Functions 60 | 61 | F1 - Set visibility: Change external to public to support batching. Should it be private? (SWC-100) 62 | 63 | F2 - Should it be payable? 64 | 65 | F3 - Can use calldata to replace memory of reference params? 66 | 67 | F4 - Are the correct modifiers applied, such as onlyOwner, nonReentrant 68 | 69 | F5 - Check behaviour for all function arguments when wrong or extreme 70 | 71 | F6 - Checks-Effects-Interactions pattern followed? (SWC-107) 72 | 73 | ## Code 74 | 75 | C1 - All math done through SafeMath (SWC-101) 76 | 77 | C2 - Are any unbounded loops/arrays used that can cause DoS? (SWC-128) 78 | 79 | C3 - Use block.timestamp only for long intervals (SWC-116) 80 | 81 | C4 - Don't use block.number for elapsed time (SWC-116) 82 | 83 | C5 - Don't use assert (SWC-110) 84 | 85 | C6 - Don't use tx.origin (SWC-115) 86 | 87 | C7 - Don't use blockhash, etc for randomness (SWC-120) 88 | 89 | C8 - Protect signatures against replay, use EIP-712 (SWC-117 SWC-121) 90 | 91 | C9 - Can abi.encodePacked lead to a hash collision? (SWC-133) 92 | 93 | C10 - Local variables should never shadow state variables (SWC-119) 94 | 95 | C11 - Careful with assembly, don't allow any arbitrary use data (SWC-127) 96 | 97 | C12 - Are any storage slots read multiple times? 98 | 99 | C13 - Is calculation on the fly cheaper than storing the value 100 | 101 | C14 - Is > or < or >= or <= correct 102 | 103 | C15 - Are logical operators correct ==, !=, &&, ||, ! 104 | 105 | C16 - Check for front-running possibilities (SWC-114) 106 | 107 | ### Call 108 | 109 | L1 - Is external function call, staticcall or delegatecall trusted? (SWC-112) 110 | 111 | L2 - Is external function call, staticcall or delegatecall can be run out of gas? (SWC-113) 112 | 113 | L3 - Is external function call, delegatecall can be caused a gas minting attack? 114 | 115 | L4 - Is the result checked and errors dealt with? (SWC-104) 116 | 117 | L5 - Is a lock used? If so are the external calls protected? 118 | 119 | L6 - Can a closed loop call such as f0 -> f1 -> f0 make attacker a profit? 120 | 121 | L7 - Can delegated contract be selfdestruct? 122 | 123 | L8 - Is an external contract call needed? 124 | 125 | L9 - Don't use msg.value repeatedly at delegatecall 126 | 127 | ### Token 128 | 129 | A1 - Recipent-Withdraw pattern followed? 130 | 131 | A2 - Use call to send eth to a contract address and check the result (SWC-134) 132 | 133 | A3 - Don't assume a specific ETH balance (and token) (SWC-132) 134 | 135 | A4 - Does msg.sender has the authority to move token of other addresses? 136 | 137 | A5 - Use the balance difference as the amount when non-standard token deposit in or withdraw out of contract 138 | 139 | A6 - Is there a possibility that tokens can not be retrieved? 140 | 141 | A7 - Is code is still correct if the token contract is upgradable? 142 | 143 | A8 - Is code is still correct if the token contract has hooks when transfer such as ERC677, ERC777, ERC1155? 144 | 145 | ### Price 146 | 147 | O1 - Does everyone has the authority to set oracle? 148 | 149 | O2 - Use TWP of onchain oracle 150 | 151 | O3 - The price of LP is correct? 152 | 153 | O4 - Is there a possibility that lend a large amount of low-value token and manipulate its price to borrow a high-value token? 154 | 155 | ## Reference 156 | 157 | https://docs.soliditylang.org/en/v0.8.15/security-considerations.html#security-considerations 158 | 159 | https://github.com/boringcrypto/BoringSolidity/blob/master/docs/checks.txt 160 | 161 | https://swcregistry.io/ 162 | -------------------------------------------------------------------------------- /etc/EXAMPLE.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": { 3 | "url": "https://matic-mumbai.chainstacklabs.com", 4 | "accounts": ["YOUR_DEPLOYER_KEY"], 5 | "gas": "auto", 6 | "gasPrice": "auto", 7 | "gasMultiplier": 1 8 | }, 9 | "etherscan": { 10 | "apiKey": "YOUR_ETHERSCAN_KEY" 11 | } 12 | } -------------------------------------------------------------------------------- /examples/arbitrum/.env-sample: -------------------------------------------------------------------------------- 1 | # This is a sample .env file for use in local development. 2 | 3 | # Duplicate this file as .env here 4 | 5 | # Your Private key 6 | 7 | DEVNET_PRIVKEY="0x your key here" 8 | 9 | # Hosted Aggregator Node (JSON-RPC Endpoint). This is Arbitrum Sepolia Testnet, can use any Arbitrum chain 10 | 11 | L2RPC="https://sepolia-rollup.arbitrum.io/rpc" 12 | 13 | # Ethereum RPC; i.e., for Sepolia https://sepolia.infura.io/v3/ 14 | 15 | L1RPC="" 16 | 17 | # Arbitrum chain name 18 | ARBITRUM="ARBITRUMTEST" 19 | 20 | # Ethereum chain name 21 | ETHEREUM="SEPOLIA" -------------------------------------------------------------------------------- /examples/arbitrum/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /examples/arbitrum/hardhat.config.js: -------------------------------------------------------------------------------- 1 | require('@nomiclabs/hardhat-ethers'); 2 | require('./scripts/syncL2Requests'); 3 | require('./scripts/syncBatchRoot'); 4 | require('./scripts/changeFeeParams'); 5 | require('./scripts/setValidator'); 6 | require('./scripts/setSecondaryGateway'); 7 | require('./scripts/governance'); 8 | 9 | const BaseConfig = require('../../hardhat.base.config'); 10 | 11 | module.exports = Object.assign({}, BaseConfig, { 12 | paths: { 13 | cache: '../../cache', 14 | artifacts: '../../artifacts', 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /examples/arbitrum/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "arbitrum-example", 3 | "license": "MIT", 4 | "version": "1.0.0", 5 | "scripts": {}, 6 | "devDependencies": { 7 | "@nomiclabs/hardhat-ethers": "^2.0.2", 8 | "hardhat": "^2.9.1", 9 | "ethers": "^5.1.2" 10 | }, 11 | "dependencies": { 12 | "@arbitrum/sdk": "^4.0.3", 13 | "dotenv": "^16.4.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/arbitrum/scripts/syncL2Requests.js: -------------------------------------------------------------------------------- 1 | const { providers, Wallet, utils } = require('ethers'); 2 | const { readDeployContract } = require('../../../script/utils'); 3 | const logName = require('../../../script/deploy_log_name'); 4 | const { ChildTransactionReceipt, ChildToParentMessageStatus } = require('@arbitrum/sdk'); 5 | const { task, types } = require('hardhat/config'); 6 | 7 | require('dotenv').config(); 8 | 9 | task('syncL2Requests', 'Send sync point from zkLink to arbitrator') 10 | .addParam('txs', 'New sync point', 100, types.int, true) 11 | .setAction(async (taskArgs, hre) => { 12 | const txs = taskArgs.txs; 13 | console.log(`The sync point: ${txs}`); 14 | 15 | const walletPrivateKey = process.env.DEVNET_PRIVKEY; 16 | const l1Provider = new providers.JsonRpcProvider(process.env.L1RPC); 17 | const l2Provider = new providers.JsonRpcProvider(process.env.L2RPC); 18 | const arbitrumName = process.env.ARBITRUM; 19 | const l1Wallet = new Wallet(walletPrivateKey, l1Provider); 20 | const l2Wallet = new Wallet(walletPrivateKey, l2Provider); 21 | 22 | const l2WalletAddress = await l2Wallet.getAddress(); 23 | const l2WalletBalance = utils.formatEther(await l2Wallet.getBalance()); 24 | console.log(`${l2WalletAddress} balance on l2: ${l2WalletBalance} ether`); 25 | 26 | const zkLinkAddr = readDeployContract( 27 | logName.DEPLOY_ZKLINK_LOG_PREFIX, 28 | logName.DEPLOY_LOG_ZKLINK_PROXY, 29 | arbitrumName, 30 | ); 31 | if (zkLinkAddr === undefined) { 32 | console.log('zkLink address not exist'); 33 | return; 34 | } 35 | console.log(`The zkLink address: ${zkLinkAddr}`); 36 | 37 | const zkLink = await hre.ethers.getContractAt('DummyZkLink', zkLinkAddr, l2Wallet); 38 | console.log(`Send a l2 message to l1...`); 39 | const l2Tx = await zkLink.syncL2Requests(txs); 40 | const txHash = l2Tx.hash; 41 | console.log(`The l2 tx hash: ${txHash}`); 42 | const syncL2RequestsReceipt = await l2Tx.wait(); 43 | 44 | /** 45 | * First, let's find the Arbitrum txn from the txn hash provided 46 | */ 47 | const l2Receipt = new ChildTransactionReceipt(syncL2RequestsReceipt); 48 | 49 | /** 50 | * Note that in principle, a single transaction could trigger any number of outgoing messages; the common case will be there's only one. 51 | * For the sake of this script, we assume there's only one / just grad the first one. 52 | */ 53 | const messages = await l2Receipt.getChildToParentMessages(l1Wallet); 54 | const l2ToL1Msg = messages[0]; 55 | 56 | /** 57 | * Check if already executed 58 | */ 59 | const msgStatus = await l2ToL1Msg.status(l2Provider); 60 | console.log(`Message status: ${msgStatus}`); 61 | if ((await l2ToL1Msg.status(l2Provider)) === ChildToParentMessageStatus.EXECUTED) { 62 | console.log(`Message already executed! Nothing else to do here`); 63 | return; 64 | } 65 | 66 | /** 67 | * before we try to execute out message, we need to make sure the l2 block it's included in is confirmed! (It can only be confirmed after the dispute period; Arbitrum is an optimistic rollup after-all) 68 | * waitUntilReadyToExecute() waits until the item outbox entry exists 69 | */ 70 | const timeToWaitMs = 1000 * 60; 71 | console.log( 72 | "Waiting for the outbox entry to be created. This only happens when the L2 block is confirmed on L1, ~1 week after it's creation.", 73 | ); 74 | await l2ToL1Msg.waitUntilReadyToExecute(l2Provider, timeToWaitMs); 75 | console.log('Outbox entry exists! Trying to execute now'); 76 | 77 | /** 78 | * Now that its confirmed and not executed, we can execute our message in its outbox entry. 79 | */ 80 | const res = await l2ToL1Msg.execute(l2Provider); 81 | const rec = await res.wait(); 82 | console.log('Done! Your transaction is executed', rec); 83 | }); 84 | -------------------------------------------------------------------------------- /examples/base/.env-sample: -------------------------------------------------------------------------------- 1 | # This is a sample .env file for use in local development. 2 | 3 | # Duplicate this file as .env here 4 | 5 | # Your Private key 6 | 7 | DEVNET_PRIVKEY="0x your key here" 8 | 9 | # Hosted Aggregator Node (JSON-RPC Endpoint). This is Base Testnet, can use any Base chain 10 | 11 | L2RPC="https://sepolia.base.org" 12 | 13 | # Ethereum RPC; i.e., for Sepolia https://sepolia.infura.io/v3/ 14 | 15 | L1RPC="" 16 | 17 | # Base chain name 18 | BASE="BASETEST" 19 | 20 | # Ethereum chain name 21 | ETHEREUM="SEPOLIA" -------------------------------------------------------------------------------- /examples/base/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /examples/base/hardhat.config.js: -------------------------------------------------------------------------------- 1 | require('@nomiclabs/hardhat-ethers'); 2 | require('./scripts/baseTasks'); 3 | 4 | const BaseConfig = require('../../hardhat.base.config'); 5 | 6 | module.exports = Object.assign({}, BaseConfig, { 7 | paths: { 8 | cache: '../../cache', 9 | artifacts: '../../artifacts', 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /examples/base/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "base-example", 3 | "license": "MIT", 4 | "version": "1.0.0", 5 | "scripts": {}, 6 | "devDependencies": { 7 | "@eth-optimism/sdk": "^3.3.2", 8 | "@nomiclabs/hardhat-ethers": "^2.0.2", 9 | "ethers": "^5.7.2", 10 | "hardhat": "^2.9.1" 11 | }, 12 | "dependencies": { 13 | "@eth-optimism/core-utils": "^0.13.1", 14 | "dotenv": "^16.4.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/blast/.env-sample: -------------------------------------------------------------------------------- 1 | # This is a sample .env file for use in local development. 2 | 3 | # Duplicate this file as .env here 4 | 5 | # Your Private key 6 | 7 | DEVNET_PRIVKEY="0x your key here" 8 | 9 | # Hosted Aggregator Node (JSON-RPC Endpoint). This is Blast Testnet, can use any Blast chain 10 | 11 | L2RPC="https://sepolia.blast.io" 12 | 13 | # Ethereum RPC; i.e., for Sepolia https://sepolia.infura.io/v3/ 14 | 15 | L1RPC="" 16 | 17 | # Blast chain name 18 | BLAST="BLASTTEST" 19 | 20 | # Ethereum chain name 21 | ETHEREUM="SEPOLIA" -------------------------------------------------------------------------------- /examples/blast/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /examples/blast/hardhat.config.js: -------------------------------------------------------------------------------- 1 | require('@nomiclabs/hardhat-ethers'); 2 | require('./scripts/blastTasks'); 3 | 4 | const BaseConfig = require('../../hardhat.base.config'); 5 | 6 | module.exports = Object.assign({}, BaseConfig, { 7 | paths: { 8 | cache: '../../cache', 9 | artifacts: '../../artifacts', 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /examples/blast/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blast-example", 3 | "license": "MIT", 4 | "version": "1.0.0", 5 | "scripts": {}, 6 | "devDependencies": { 7 | "@eth-optimism/sdk": "^3.2.3", 8 | "@nomiclabs/hardhat-ethers": "^2.0.2", 9 | "ethers": "^5.7.2", 10 | "hardhat": "^2.9.1" 11 | }, 12 | "dependencies": { 13 | "@eth-optimism/core-utils": "^0.13.1", 14 | "dotenv": "^16.4.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/blast/scripts/constants.js: -------------------------------------------------------------------------------- 1 | const contractABIs = require('./contractABIs.json'); 2 | 3 | const OPTIMISM_PORTAL_ABI = contractABIs.OptimismPortal; 4 | 5 | const YIELD_MANAGER_ABI = contractABIs.YieldManager; 6 | 7 | const MESSAGE_PASSER_ABI = contractABIs.MessagePasser; 8 | const MESSAGE_PASSER_ADDRESS = '0x4200000000000000000000000000000000000016'; 9 | 10 | // Testnet 11 | const YIELD_MANAGER_TESTNET_ADDRESS = '0xeD530bA33B4dC14572864Bb9a776C9a42CF89fA5'; 12 | const L1_TESTNET_CONTRACTS = { 13 | StateCommitmentChain: '0x0000000000000000000000000000000000000000', 14 | BondManager: '0x0000000000000000000000000000000000000000', 15 | CanonicalTransactionChain: '0x0000000000000000000000000000000000000000', 16 | AddressManager: '0x092dD3E2272a372cdfbcCb8F689423F09ED6242a', 17 | L1CrossDomainMessenger: '0x9338F298F29D3918D5D1Feb209aeB9915CC96333', 18 | L1StandardBridge: '0xDeDa8D3CCf044fE2A16217846B6e1f1cfD8e122f', 19 | OptimismPortal: '0x2757E4430e694F27b73EC9C02257cab3a498C8C5', 20 | L2OutputOracle: '0x311fF72DfE214ADF97618DD2E731637E8F41bD8c', 21 | }; 22 | 23 | // Mainnet 24 | const YIELD_MANAGER_MAINNET_ADDRESS = '0x98078db053902644191f93988341E31289E1C8FE'; 25 | const L1_MAINNET_CONTRACTS = { 26 | StateCommitmentChain: '0x0000000000000000000000000000000000000000', 27 | BondManager: '0x0000000000000000000000000000000000000000', 28 | CanonicalTransactionChain: '0x0000000000000000000000000000000000000000', 29 | AddressManager: '0xE064B565Cf2A312a3e66Fe4118890583727380C0', 30 | L1CrossDomainMessenger: '0x5D4472f31Bd9385709ec61305AFc749F0fA8e9d0', 31 | L1StandardBridge: '0x697402166Fbf2F22E970df8a6486Ef171dbfc524', 32 | OptimismPortal: '0x0Ec68c5B10F21EFFb74f2A5C61DFe6b08C0Db6Cb', 33 | L2OutputOracle: '0x826D1B0D4111Ad9146Eb8941D7Ca2B6a44215c76', 34 | }; 35 | 36 | module.exports = { 37 | OPTIMISM_PORTAL_ABI, 38 | YIELD_MANAGER_ABI, 39 | MESSAGE_PASSER_ABI, 40 | MESSAGE_PASSER_ADDRESS, 41 | YIELD_MANAGER_MAINNET_ADDRESS, 42 | YIELD_MANAGER_TESTNET_ADDRESS, 43 | L1_TESTNET_CONTRACTS, 44 | L1_MAINNET_CONTRACTS, 45 | }; 46 | -------------------------------------------------------------------------------- /examples/deposit/.env-sample: -------------------------------------------------------------------------------- 1 | # This is a sample .env file for use in local development. 2 | 3 | # Duplicate this file as .env here 4 | 5 | # Your Private key 6 | 7 | DEVNET_PRIVKEY="0x your key here" 8 | 9 | # Hosted Aggregator Node (JSON-RPC Endpoint). This is zkLink Sepolia Testnet 10 | 11 | L2RPC="https://sepolia.rpc.zklink.network" 12 | 13 | # Ethereum RPC; i.e., for Sepolia https://sepolia.infura.io/v3/ 14 | 15 | L1RPC="" 16 | 17 | # ZkLink address 18 | 19 | ZKLINK_ADDRESS="" 20 | 21 | # L1ERC20Bridge address 22 | 23 | L1_ERC20_BRIDGE_ADDRESS="" -------------------------------------------------------------------------------- /examples/deposit/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /examples/deposit/hardhat.config.js: -------------------------------------------------------------------------------- 1 | require('@nomicfoundation/hardhat-toolbox'); 2 | require('./scripts/depositETH'); 3 | require('./scripts/depositERC20'); 4 | require('./scripts/requestL2Tx'); 5 | 6 | const BaseConfig = require('../../hardhat.base.config'); 7 | 8 | module.exports = Object.assign({}, BaseConfig, { 9 | paths: { 10 | cache: '../../cache', 11 | artifacts: '../../artifacts', 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /examples/deposit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "deposit-example", 3 | "license": "MIT", 4 | "version": "1.0.0", 5 | "scripts": {}, 6 | "devDependencies": { 7 | "@nomicfoundation/hardhat-toolbox": "^4.0.0", 8 | "ethers": "^6.9.2", 9 | "hardhat": "^2.12.4", 10 | "zksync-ethers": "^6.0.0" 11 | }, 12 | "dependencies": { 13 | "dotenv": "^16.4.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/deposit/scripts/depositERC20.js: -------------------------------------------------------------------------------- 1 | const { Provider, Wallet, utils } = require('zksync-ethers'); 2 | const { task, types } = require('hardhat/config'); 3 | const { ethers } = require('ethers'); 4 | 5 | require('dotenv').config(); 6 | 7 | task('depositERC20', 'Deposit eth from secondary chain') 8 | .addParam('token', 'The token address', undefined, types.string, false) 9 | .addOptionalParam('decimals', 'The token decimals', 18, types.int) 10 | .addParam('amount', 'Deposit token amount(unit: ether)', undefined, types.string, false) 11 | .setAction(async (taskArgs, hre) => { 12 | const l1TokenAddress = taskArgs.token; 13 | const tokenDecimals = taskArgs.decimals; 14 | console.log(`Token address is ${l1TokenAddress}`); 15 | console.log(`Token decimals is ${tokenDecimals}`); 16 | console.log(`Deposit amount is ${taskArgs.amount}`); 17 | const tokenAmount = ethers.parseUnits(taskArgs.amount, tokenDecimals); 18 | const l1Provider = new Provider(process.env.L1RPC); 19 | const l2Provider = new Provider(process.env.L2RPC); 20 | const wallet = new Wallet(process.env.DEVNET_PRIVKEY, l2Provider, l1Provider); 21 | console.log(`Wallet address is ${wallet.address}`); 22 | const l1Token = await hre.ethers.getContractAt('IERC20', l1TokenAddress, wallet.ethWallet()); 23 | const l1TokenBalance = await l1Token.balanceOf(wallet.address); 24 | console.log(`Token L1 Balance is ${ethers.formatUnits(l1TokenBalance, tokenDecimals)}`); 25 | const l1ERC20BridgeAddress = process.env.L1_ERC20_BRIDGE_ADDRESS; 26 | const l1ERC20Bridge = await hre.ethers.getContractAt('L1ERC20Bridge', l1ERC20BridgeAddress, wallet.ethWallet()); 27 | const l2TokenAddress = await l1ERC20Bridge.l2TokenAddress(l1TokenAddress); 28 | console.log(`L2 token address is ${l2TokenAddress}`); 29 | const l2Token = await hre.ethers.getContractAt('IERC20', l2TokenAddress, wallet); 30 | const l2TokeCode = await l2Provider.getCode(l2TokenAddress); 31 | let l2TokenBalance = 0n; 32 | if (l2TokeCode !== '0x') { 33 | l2TokenBalance = await l2Token.balanceOf(wallet.address); 34 | } 35 | console.log(`Token L2 Balance is ${ethers.formatUnits(l2TokenBalance, tokenDecimals)}`); 36 | 37 | // Estimate the gas 38 | const l2ERC20BridgeAddress = await l1ERC20Bridge.l2Bridge(); 39 | const customBridgeData = await utils.getERC20DefaultBridgeData(l1TokenAddress, l1Provider); 40 | const l2GasLimit = await utils.estimateCustomBridgeDepositL2Gas( 41 | l2Provider, 42 | l1ERC20BridgeAddress, 43 | l2ERC20BridgeAddress, 44 | l1TokenAddress, 45 | tokenAmount, 46 | wallet.address, 47 | customBridgeData, 48 | wallet.address, 49 | ); 50 | // Log the estimated gas 51 | console.log(`Estimated gas for L1 to L2 operation: ${l2GasLimit.toString()}`); 52 | 53 | // Get primary tx gas price 54 | const zkLink = await hre.ethers.getContractAt('ZkLink', process.env.ZKLINK_ADDRESS, wallet.ethWallet()); 55 | const primaryTxGasPrice = await zkLink.txGasPrice(); 56 | console.log(`Primary tx gas price: ${ethers.formatUnits(primaryTxGasPrice, 'gwei')} gwei`); 57 | 58 | // Base cost 59 | const l2GasPerPubdataByteLimit = utils.REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT; 60 | const baseCost = await zkLink.l2TransactionBaseCost(primaryTxGasPrice, l2GasLimit, l2GasPerPubdataByteLimit); 61 | console.log(`Base cost is ${ethers.formatEther(baseCost)} ether`); 62 | const msgValue = baseCost; 63 | 64 | // Approve token 65 | const allowance = await l1Token.allowance(wallet.address, l1ERC20BridgeAddress); 66 | if (allowance < tokenAmount) { 67 | console.log(`Approve to l1 erc20 bridge...`); 68 | await l1Token.approve(l1ERC20BridgeAddress, ethers.MaxUint256); 69 | } 70 | 71 | // Deposit eth 72 | console.log(`Send a l1 message to l2...`); 73 | const tx = await l1ERC20Bridge.deposit( 74 | wallet.address, 75 | l1TokenAddress, 76 | tokenAmount, 77 | l2GasLimit, 78 | l2GasPerPubdataByteLimit, 79 | wallet.address, 80 | { value: msgValue }, 81 | ); 82 | console.log(`The tx hash: ${tx.hash} , waiting for confirm...`); 83 | await tx.wait(); 84 | console.log(`The tx confirmed`); 85 | }); 86 | -------------------------------------------------------------------------------- /examples/deposit/scripts/depositETH.js: -------------------------------------------------------------------------------- 1 | const { Provider, Wallet, utils } = require('zksync-ethers'); 2 | const { task, types } = require('hardhat/config'); 3 | const { ethers } = require('ethers'); 4 | 5 | require('dotenv').config(); 6 | 7 | task('depositETH', 'Deposit eth from secondary chain') 8 | .addParam('amount', 'Deposit eth amount(unit: ether)', undefined, types.string, false) 9 | .setAction(async (taskArgs, hre) => { 10 | console.log(`Deposit ${taskArgs.amount} ether`); 11 | const l2Value = ethers.parseEther(taskArgs.amount); 12 | const l1Provider = new Provider(process.env.L1RPC); 13 | const l2Provider = new Provider(process.env.L2RPC); 14 | const wallet = new Wallet(process.env.DEVNET_PRIVKEY, l2Provider, l1Provider); 15 | 16 | const l1Balance = ethers.formatEther(await wallet.getBalanceL1()); 17 | const l2Balance = ethers.formatEther(await wallet.getBalance()); 18 | console.log(`Wallet address is ${wallet.address}`); 19 | console.log(`L1 Balance is ${l1Balance}`); 20 | console.log(`L2 Balance is ${l2Balance}`); 21 | 22 | // Estimate the gas 23 | const l2GasLimit = await l2Provider.estimateL1ToL2Execute({ 24 | contractAddress: wallet.address, 25 | calldata: '0x', 26 | caller: wallet.address, 27 | l2Value: l2Value, 28 | factoryDeps: [], 29 | }); 30 | // Log the estimated gas 31 | console.log(`Estimated gas for L1 to L2 operation: ${l2GasLimit.toString()}`); 32 | 33 | // Get primary tx gas price 34 | const zkLink = await hre.ethers.getContractAt('ZkLink', process.env.ZKLINK_ADDRESS, wallet.ethWallet()); 35 | const primaryTxGasPrice = await zkLink.txGasPrice(); 36 | console.log(`Primary tx gas price: ${ethers.formatUnits(primaryTxGasPrice, 'gwei')} gwei`); 37 | 38 | // Base cost 39 | const l2GasPerPubdataByteLimit = utils.REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT; 40 | const baseCost = await zkLink.l2TransactionBaseCost(primaryTxGasPrice, l2GasLimit, l2GasPerPubdataByteLimit); 41 | console.log(`Base cost is ${ethers.formatEther(baseCost)} ether`); 42 | const msgValue = baseCost + l2Value; 43 | 44 | // Deposit eth 45 | console.log(`Send a l1 message to l2...`); 46 | const tx = await zkLink.requestL2Transaction( 47 | wallet.address, 48 | l2Value, 49 | '0x', 50 | l2GasLimit, 51 | l2GasPerPubdataByteLimit, 52 | [], 53 | wallet.address, 54 | { value: msgValue }, 55 | ); 56 | console.log(`The tx hash: ${tx.hash} , waiting for confirm...`); 57 | await tx.wait(); 58 | console.log(`The tx confirmed`); 59 | }); 60 | -------------------------------------------------------------------------------- /examples/ethereum/.env-sample: -------------------------------------------------------------------------------- 1 | # This is a sample .env file for use in local development. 2 | 3 | # Duplicate this file as .env here 4 | 5 | # Your Private key 6 | 7 | DEVNET_PRIVKEY="0x your key here" 8 | 9 | # Ethereum RPC; i.e., for Goerli https://goerli.infura.io/v3/ 10 | 11 | L1RPC="" 12 | 13 | # Ethereum chain name 14 | ETHEREUM="GOERLI" -------------------------------------------------------------------------------- /examples/ethereum/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /examples/ethereum/hardhat.config.js: -------------------------------------------------------------------------------- 1 | require('@nomicfoundation/hardhat-toolbox'); 2 | require('./scripts/changeFeeParams'); 3 | require('./scripts/setValidator'); 4 | 5 | const BaseConfig = require('../../hardhat.base.config'); 6 | 7 | module.exports = Object.assign({}, BaseConfig, { 8 | paths: { 9 | cache: '../../cache', 10 | artifacts: '../../artifacts', 11 | }, 12 | }); 13 | -------------------------------------------------------------------------------- /examples/ethereum/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ethereum-example", 3 | "license": "MIT", 4 | "version": "1.0.0", 5 | "scripts": {}, 6 | "devDependencies": { 7 | "@nomicfoundation/hardhat-toolbox": "^4.0.0", 8 | "ethers": "^6.11.1", 9 | "hardhat": "^2.19.5" 10 | }, 11 | "dependencies": { 12 | "dotenv": "^16.4.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/ethereum/scripts/changeFeeParams.js: -------------------------------------------------------------------------------- 1 | const { JsonRpcProvider, Wallet, formatEther } = require('ethers'); 2 | const { readDeployContract } = require('../../../script/utils'); 3 | const logName = require('../../../script/deploy_log_name'); 4 | const { task } = require('hardhat/config'); 5 | 6 | require('dotenv').config(); 7 | 8 | task('changeFeeParams', 'Change fee params for zkLink').setAction(async (taskArgs, hre) => { 9 | const walletPrivateKey = process.env.DEVNET_PRIVKEY; 10 | const l1Provider = new JsonRpcProvider(process.env.L1RPC); 11 | const ethereumName = process.env.ETHEREUM; 12 | const l1Wallet = new Wallet(walletPrivateKey, l1Provider); 13 | 14 | const l1WalletAddress = await l1Wallet.getAddress(); 15 | const l1WalletBalance = formatEther(await l1Provider.getBalance(l1WalletAddress)); 16 | console.log(`${l1WalletAddress} balance on l1: ${l1WalletBalance} ether`); 17 | 18 | const arbitratorAddr = readDeployContract( 19 | logName.DEPLOY_ARBITRATOR_LOG_PREFIX, 20 | logName.DEPLOY_LOG_ARBITRATOR, 21 | ethereumName, 22 | ); 23 | if (arbitratorAddr === undefined) { 24 | console.log('The arbitrator address not exist'); 25 | return; 26 | } 27 | console.log(`The arbitrator address: ${arbitratorAddr}`); 28 | 29 | const ethereumGatewayAddr = readDeployContract( 30 | logName.DEPLOY_ETH_GATEWAY_LOG_PREFIX, 31 | logName.DEPLOY_GATEWAY, 32 | ethereumName, 33 | ); 34 | if (ethereumGatewayAddr === undefined) { 35 | console.log('ethereum gateway address not exist'); 36 | return; 37 | } 38 | console.log(`The ethereum gateway address: ${ethereumGatewayAddr}`); 39 | 40 | // forward message to gateway 41 | const arbitrator = await hre.ethers.getContractAt('Arbitrator', arbitratorAddr, l1Wallet); 42 | const { INIT_FEE_PARAMS } = require('../../../script/zksync_era'); 43 | let tx = await arbitrator.changeFeeParams(ethereumGatewayAddr, INIT_FEE_PARAMS, '0x'); 44 | console.log(`The tx hash: ${tx.hash} , waiting confirm...`); 45 | await tx.wait(); 46 | console.log(`The tx confirmed`); 47 | }); 48 | -------------------------------------------------------------------------------- /examples/ethereum/scripts/setValidator.js: -------------------------------------------------------------------------------- 1 | const { JsonRpcProvider, Wallet, formatEther } = require('ethers'); 2 | const { readDeployContract } = require('../../../script/utils'); 3 | const logName = require('../../../script/deploy_log_name'); 4 | const { task, types } = require('hardhat/config'); 5 | 6 | require('dotenv').config(); 7 | 8 | task('setValidator', 'Set validator for zkLink') 9 | .addParam('validator', 'Validator Address', undefined, types.string) 10 | .addOptionalParam('active', 'Whether to activate the validator address', true, types.boolean) 11 | .setAction(async (taskArgs, hre) => { 12 | const validatorAddr = taskArgs.validator; 13 | const isActive = taskArgs.active; 14 | console.log(`The validator: address: ${validatorAddr}, active: ${isActive}`); 15 | 16 | const walletPrivateKey = process.env.DEVNET_PRIVKEY; 17 | const l1Provider = new JsonRpcProvider(process.env.L1RPC); 18 | const ethereumName = process.env.ETHEREUM; 19 | const l1Wallet = new Wallet(walletPrivateKey, l1Provider); 20 | 21 | const l1WalletAddress = await l1Wallet.getAddress(); 22 | const l1WalletBalance = formatEther(await l1Provider.getBalance(l1WalletAddress)); 23 | console.log(`${l1WalletAddress} balance on l1: ${l1WalletBalance} ether`); 24 | 25 | const arbitratorAddr = readDeployContract( 26 | logName.DEPLOY_ARBITRATOR_LOG_PREFIX, 27 | logName.DEPLOY_LOG_ARBITRATOR, 28 | ethereumName, 29 | ); 30 | if (arbitratorAddr === undefined) { 31 | console.log('The arbitrator address not exist'); 32 | return; 33 | } 34 | console.log(`The arbitrator address: ${arbitratorAddr}`); 35 | 36 | const ethereumGatewayAddr = readDeployContract( 37 | logName.DEPLOY_ETH_GATEWAY_LOG_PREFIX, 38 | logName.DEPLOY_GATEWAY, 39 | ethereumName, 40 | ); 41 | if (ethereumGatewayAddr === undefined) { 42 | console.log('ethereum gateway address not exist'); 43 | return; 44 | } 45 | console.log(`The ethereum gateway address: ${ethereumGatewayAddr}`); 46 | 47 | // forward message to gateway 48 | const arbitrator = await hre.ethers.getContractAt('Arbitrator', arbitratorAddr, l1Wallet); 49 | let tx = await arbitrator.setValidator(ethereumGatewayAddr, validatorAddr, isActive, '0x'); 50 | console.log(`The tx hash: ${tx.hash} , waiting confirm...`); 51 | await tx.wait(); 52 | console.log(`The tx confirmed`); 53 | }); 54 | -------------------------------------------------------------------------------- /examples/failedDeposit/.env-sample: -------------------------------------------------------------------------------- 1 | # This is a sample .env file for use in local development. 2 | 3 | # Duplicate this file as .env here 4 | 5 | # Your Private key 6 | 7 | PRIVATE_KEY="0x your key here" 8 | 9 | # Hosted Aggregator Node (JSON-RPC Endpoint). This is zkLink Sepolia Testnet 10 | 11 | L2RPC="https://sepolia.rpc.zklink.network" 12 | 13 | # This is zkLink Sepolia Testnet 14 | LINEA_RPC="https://rpc.sepolia.linea.build" 15 | 16 | # Ethereum chain name 17 | ETHEREUM="SEPOLIA" 18 | 19 | # Primary chain zklink address 20 | PRIMARY_CHAIN_ZKLINK="0x16393A77e1d5C2D285BDad9079afC5942f255407" -------------------------------------------------------------------------------- /examples/failedDeposit/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /examples/failedDeposit/hardhat.config.js: -------------------------------------------------------------------------------- 1 | require('@nomicfoundation/hardhat-toolbox'); 2 | require('./scripts/claimFailedDeposit'); 3 | 4 | const BaseConfig = require('../../hardhat.base.config'); 5 | 6 | module.exports = Object.assign({}, BaseConfig, { 7 | paths: { 8 | cache: '../../cache', 9 | artifacts: '../../artifacts', 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /examples/failedDeposit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "deposit-example", 3 | "license": "MIT", 4 | "version": "1.0.0", 5 | "scripts": {}, 6 | "devDependencies": { 7 | "@nomicfoundation/hardhat-toolbox": "^4.0.0", 8 | "ethers": "^6.9.2", 9 | "hardhat": "^2.12.4", 10 | "zksync-ethers": "^6.0.0", 11 | "@rainbow-me/fee-suggestions": "^2.1.0" 12 | }, 13 | "dependencies": { 14 | "dotenv": "^16.4.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/linea/.env-sample: -------------------------------------------------------------------------------- 1 | # This is a sample .env file for use in local development. 2 | 3 | # Duplicate this file as .env here 4 | 5 | # Your Private key 6 | 7 | DEVNET_PRIVKEY="0x your key here" 8 | 9 | # Hosted Aggregator Node (JSON-RPC Endpoint). This is Linea Goerli Testnet, can use any Linea chain 10 | 11 | L2RPC="https://rpc.goerli.linea.build" 12 | 13 | # Ethereum RPC; i.e., for Goerli https://goerli.infura.io/v3/ 14 | 15 | L1RPC="" 16 | 17 | # Linea chain name 18 | LINEA="LINEATEST" 19 | 20 | # Ethereum chain name 21 | ETHEREUM="GOERLI" -------------------------------------------------------------------------------- /examples/linea/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /examples/linea/hardhat.config.js: -------------------------------------------------------------------------------- 1 | require('@nomicfoundation/hardhat-toolbox'); 2 | require('./scripts/syncL2Requests'); 3 | require('./scripts/syncBatchRoot'); 4 | require('./scripts/setValidator'); 5 | require('./scripts/changeFeeParams'); 6 | require('./scripts/setSecondaryGateway'); 7 | require('./scripts/governance'); 8 | require('./scripts/claimL1Tx'); 9 | 10 | const BaseConfig = require('../../hardhat.base.config'); 11 | 12 | module.exports = Object.assign({}, BaseConfig, { 13 | paths: { 14 | cache: '../../cache', 15 | artifacts: '../../artifacts', 16 | }, 17 | }); 18 | -------------------------------------------------------------------------------- /examples/linea/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "linea-example", 3 | "license": "MIT", 4 | "version": "1.0.0", 5 | "scripts": {}, 6 | "devDependencies": { 7 | "@consensys/linea-sdk": "^0.2.1", 8 | "@nomicfoundation/hardhat-toolbox": "^4.0.0", 9 | "ethers": "^6.11.1" 10 | }, 11 | "dependencies": { 12 | "dotenv": "^16.4.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/linea/scripts/changeFeeParams.js: -------------------------------------------------------------------------------- 1 | const { JsonRpcProvider, Wallet, formatEther } = require('ethers'); 2 | const { readDeployContract, getLogName } = require('../../../script/utils'); 3 | const logName = require('../../../script/deploy_log_name'); 4 | const { task } = require('hardhat/config'); 5 | const { claimL1ToL2Message } = require('./common'); 6 | 7 | require('dotenv').config(); 8 | 9 | task('changeFeeParams', 'Change fee params for zkLink').setAction(async (taskArgs, hre) => { 10 | const walletPrivateKey = process.env.DEVNET_PRIVKEY; 11 | const l1Provider = new JsonRpcProvider(process.env.L1RPC); 12 | const l2Provider = new JsonRpcProvider(process.env.L2RPC); 13 | const ethereumName = process.env.ETHEREUM; 14 | const lineaName = process.env.LINEA; 15 | const l1Wallet = new Wallet(walletPrivateKey, l1Provider); 16 | const l2Wallet = new Wallet(walletPrivateKey, l2Provider); 17 | 18 | const l1WalletAddress = await l1Wallet.getAddress(); 19 | const l1WalletBalance = formatEther(await l1Provider.getBalance(l1WalletAddress)); 20 | console.log(`${l1WalletAddress} balance on l1: ${l1WalletBalance} ether`); 21 | const l2WalletAddress = await l2Wallet.getAddress(); 22 | const l2WalletBalance = formatEther(await l2Provider.getBalance(l2WalletAddress)); 23 | console.log(`${l2WalletAddress} balance on l2: ${l2WalletBalance} ether`); 24 | 25 | const arbitratorAddr = readDeployContract( 26 | logName.DEPLOY_ARBITRATOR_LOG_PREFIX, 27 | logName.DEPLOY_LOG_ARBITRATOR, 28 | ethereumName, 29 | ); 30 | if (arbitratorAddr === undefined) { 31 | console.log('The arbitrator address not exist'); 32 | return; 33 | } 34 | console.log(`The arbitrator address: ${arbitratorAddr}`); 35 | 36 | const l1GatewayLogName = getLogName(logName.DEPLOY_L1_GATEWAY_LOG_PREFIX, lineaName); 37 | const lineaL1GatewayAddr = readDeployContract(l1GatewayLogName, logName.DEPLOY_GATEWAY, ethereumName); 38 | if (lineaL1GatewayAddr === undefined) { 39 | console.log('linea l1 gateway address not exist'); 40 | return; 41 | } 42 | console.log(`The linea l1 gateway address: ${lineaL1GatewayAddr}`); 43 | 44 | const lineaL2GatewayAddr = readDeployContract( 45 | logName.DEPLOY_L2_GATEWAY_LOG_PREFIX, 46 | logName.DEPLOY_GATEWAY, 47 | lineaName, 48 | ); 49 | if (lineaL2GatewayAddr === undefined) { 50 | console.log('linea l2 gateway address not exist'); 51 | return; 52 | } 53 | console.log(`The linea l2 gateway address: ${lineaL2GatewayAddr}`); 54 | 55 | // forward message to L2 56 | const arbitrator = await hre.ethers.getContractAt('Arbitrator', arbitratorAddr, l1Wallet); 57 | const adapterParams = '0x'; 58 | const { INIT_FEE_PARAMS } = require('../../../script/zksync_era'); 59 | let tx = await arbitrator.changeFeeParams(lineaL1GatewayAddr, INIT_FEE_PARAMS, adapterParams); 60 | console.log(`The l1 tx hash: ${tx.hash}`); 61 | await tx.wait(); 62 | console.log(`The l1 tx confirmed`); 63 | await claimL1ToL2Message(tx.hash); 64 | }); 65 | -------------------------------------------------------------------------------- /examples/linea/scripts/claimL1Tx.js: -------------------------------------------------------------------------------- 1 | const { task, types } = require('hardhat/config'); 2 | const { claimL2ToL1Message } = require('./common'); 3 | 4 | require('dotenv').config(); 5 | 6 | task('claimL1Tx', 'Claim l1 tx') 7 | .addParam('txHash', 'The l2 to l1 tx hash', undefined, types.string) 8 | .addOptionalParam('index', 'The l2 to l1 message index', 0, types.int) 9 | .setAction(async taskArgs => { 10 | const l2ToL1TxHash = taskArgs.txHash; 11 | const messageIndex = taskArgs.index; 12 | console.log(`The l2 to l1 tx hash: ${l2ToL1TxHash}`); 13 | console.log(`The message index: ${messageIndex}`); 14 | 15 | await claimL2ToL1Message(l2ToL1TxHash, messageIndex); 16 | }); 17 | -------------------------------------------------------------------------------- /examples/linea/scripts/common.js: -------------------------------------------------------------------------------- 1 | const { LineaSDK, OnChainMessageStatus } = require('@consensys/linea-sdk'); 2 | 3 | function initSDK() { 4 | const walletPrivateKey = process.env.DEVNET_PRIVKEY; 5 | const ethereumName = process.env.ETHEREUM; 6 | const sdk = new LineaSDK({ 7 | l1RpcUrl: process.env.L1RPC ?? '', 8 | l2RpcUrl: process.env.L2RPC ?? '', 9 | l1SignerPrivateKey: walletPrivateKey ?? '', 10 | l2SignerPrivateKey: walletPrivateKey ?? '', 11 | network: ethereumName === 'GOERLI' ? 'linea-goerli' : ethereumName === 'SEPOLIA' ? 'localhost' : 'linea-mainnet', // sdk not support SEPOLIA 12 | mode: 'read-write', 13 | }); 14 | const sepoliaContracts = { 15 | l1ContractAddress: '0xB218f8A4Bc926cF1cA7b3423c154a0D627Bdb7E5', 16 | l2ContractAddress: '0x971e727e956690b9957be6d51Ec16E73AcAC83A7', 17 | }; 18 | const lineaL1Contract = sdk.getL1Contract(sepoliaContracts.l1ContractAddress); 19 | const lineaL2Contract = sdk.getL2Contract(sepoliaContracts.l2ContractAddress); 20 | const lineaL1ClaimingService = sdk.getL1ClaimingService( 21 | sepoliaContracts.l1ContractAddress, 22 | sepoliaContracts.l2ContractAddress, 23 | ); 24 | return { lineaL1Contract, lineaL2Contract, lineaL1ClaimingService }; 25 | } 26 | 27 | async function claimL1ToL2Message(l1TxHash, messageIndex) { 28 | const sdkInit = initSDK(); 29 | const lineaL1Contract = sdkInit.lineaL1Contract; 30 | const lineaL2Contract = sdkInit.lineaL2Contract; 31 | 32 | /** 33 | * Query the transaction status on L2 via messageHash. 34 | */ 35 | messageIndex = messageIndex ?? 0; 36 | const messages = await lineaL1Contract.getMessagesByTransactionHash(l1TxHash); 37 | const message = messages[messageIndex]; 38 | 39 | // Waiting for the official Linea bridge to forward the message to L2 40 | // And manually claim the message on L2 41 | /*eslint no-constant-condition: ["error", { "checkLoops": false }]*/ 42 | while (true) { 43 | const messageStatus = await lineaL2Contract.getMessageStatus(message.messageHash); 44 | console.log(`The message status: ${messageStatus}`); 45 | if (messageStatus === OnChainMessageStatus.CLAIMABLE) { 46 | const tx = await lineaL2Contract.claim(message); 47 | console.log(`The tx hash: ${tx.hash}`); 48 | await tx.wait(); 49 | console.log(`The tx confirmed`); 50 | break; 51 | } 52 | await sleep(60 * 1000); 53 | } 54 | } 55 | 56 | async function claimL2ToL1Message(l2TxHash, messageIndex) { 57 | const sdkInit = initSDK(); 58 | const lineaL1ClaimingService = sdkInit.lineaL1ClaimingService; 59 | const lineaL2Contract = sdkInit.lineaL2Contract; 60 | 61 | /** 62 | * Query the message informations on L2 via txHash. 63 | */ 64 | messageIndex = messageIndex ?? 0; 65 | const messages = await lineaL2Contract.getMessagesByTransactionHash(l2TxHash); 66 | const message = messages[messageIndex]; 67 | console.log(`The messageSender: ${message.messageSender}`); 68 | console.log(`The destination: ${message.destination}`); 69 | console.log(`The fee: ${message.fee}`); 70 | console.log(`The value: ${message.value}`); 71 | console.log(`The messageNonce: ${message.messageNonce}`); 72 | console.log(`The calldata: ${message.calldata}`); 73 | console.log(`The messageHash: ${message.messageHash}`); 74 | 75 | // Waiting for the official Linea bridge to forward the message to L1 76 | // And manually claim the message on L1 77 | /*eslint no-constant-condition: ["error", { "checkLoops": false }]*/ 78 | while (true) { 79 | /** 80 | * Query the transaction status on L1 via messageHash. 81 | */ 82 | const messageStatus = await lineaL1ClaimingService.getMessageStatus(message.messageHash); 83 | console.log(`The message status: ${messageStatus}`); 84 | if (messageStatus === OnChainMessageStatus.CLAIMABLE) { 85 | const tx = await lineaL1ClaimingService.claimMessage(message); 86 | console.log(`The tx hash: ${tx.hash}`); 87 | await tx.wait(); 88 | console.log(`The tx confirmed`); 89 | break; 90 | } 91 | await sleep(60 * 1000 * 30); 92 | } 93 | } 94 | 95 | function sleep(ms) { 96 | return new Promise(resolve => setTimeout(resolve, ms)); 97 | } 98 | 99 | module.exports = { 100 | claimL1ToL2Message, 101 | claimL2ToL1Message, 102 | }; 103 | -------------------------------------------------------------------------------- /examples/linea/scripts/governance.js: -------------------------------------------------------------------------------- 1 | const { JsonRpcProvider, formatEther } = require('ethers'); 2 | const { readDeployContract } = require('../../../script/utils'); 3 | const logName = require('../../../script/deploy_log_name'); 4 | const { task, types } = require('hardhat/config'); 5 | const { zkLinkConfig } = require('../../../script/zklink_config'); 6 | const { claimL1ToL2Message } = require('./common'); 7 | 8 | require('dotenv').config(); 9 | 10 | task('encodeL1ToL2Calldata', 'Encode call data for l1 to l2') 11 | .addParam('to', 'The l2 target address', undefined, types.string) 12 | .addParam('l2CallData', 'The l2 call data to target address', undefined, types.string) 13 | .addParam('l2CallValue', 'The l2 call value to target address', undefined, types.int) 14 | .setAction(async (taskArgs, hre) => { 15 | const l2ToContractAddress = taskArgs.to; 16 | const l2CallData = taskArgs.l2CallData; 17 | const l2CallValue = BigInt(taskArgs.l2CallValue); 18 | console.log(`The l2 target contract address: ${l2ToContractAddress}`); 19 | console.log(`The l2 call data to target address: ${l2CallData}`); 20 | console.log(`The l2 call value to target address: ${l2CallValue}`); 21 | 22 | const l2Provider = new JsonRpcProvider(process.env.L2RPC); 23 | const lineaName = process.env.LINEA; 24 | 25 | const l2ChainInfo = zkLinkConfig[lineaName]; 26 | if (l2ChainInfo === undefined) { 27 | console.log('The l2 chain info not exist'); 28 | return; 29 | } 30 | const messageServiceAddr = l2ChainInfo['l1Gateway']['constructParams'][0]; 31 | if (messageServiceAddr === undefined) { 32 | console.log('The arbitrum inbox address not exist'); 33 | return; 34 | } 35 | console.log(`The linea l1 message service address: ${messageServiceAddr}`); 36 | 37 | const lineaL2GovernanceAddr = readDeployContract( 38 | logName.DEPLOY_LINEA_L2_GOVERNANCE_LOG_PREFIX, 39 | logName.DEPLOY_LOG_GOVERNANCE, 40 | lineaName, 41 | ); 42 | if (lineaL2GovernanceAddr === undefined) { 43 | console.log('linea l2 governance address not exist'); 44 | return; 45 | } 46 | console.log(`The linea l2 governance address: ${lineaL2GovernanceAddr}`); 47 | if (l2CallValue > 0) { 48 | const l2GovernanceBalance = await l2Provider.getBalance(lineaL2GovernanceAddr); 49 | console.log(`The linea l2 governance balance: ${formatEther(l2GovernanceBalance)} ETH`); 50 | if (l2GovernanceBalance < l2CallValue) { 51 | console.log(`Please transfer some eth to linea l2 governance`); 52 | return; 53 | } 54 | } 55 | 56 | const call = { 57 | target: l2ToContractAddress, 58 | value: l2CallValue, 59 | data: l2CallData, 60 | }; 61 | const lineaL2Governance = await hre.ethers.getContractAt( 62 | 'LineaL2Governance', 63 | '0x0000000000000000000000000000000000000000', 64 | ); 65 | const lineaL2GovernanceCallData = lineaL2Governance.interface.encodeFunctionData('execute', [[call]]); 66 | const lineaMessageService = await hre.ethers.getContractAt( 67 | 'IMessageService', 68 | '0x0000000000000000000000000000000000000000', 69 | ); 70 | const l1ToL2Calldata = lineaMessageService.interface.encodeFunctionData('sendMessage', [ 71 | lineaL2GovernanceAddr, 72 | 0, 73 | lineaL2GovernanceCallData, 74 | ]); 75 | console.log(`The l1 to l2 call target: ${messageServiceAddr}`); 76 | console.log(`The l1 to l2 call data: ${l1ToL2Calldata}`); 77 | console.log(`The l1 to l2 call value: 0`); 78 | }); 79 | 80 | task('checkL1TxStatus', 'Check the l1 tx status') 81 | .addParam('l1TxHash', 'The l1 tx hash', undefined, types.string) 82 | .setAction(async taskArgs => { 83 | const l1TxHash = taskArgs.l1TxHash; 84 | console.log(`The l1 tx hash: ${l1TxHash}`); 85 | await claimL1ToL2Message(l1TxHash); 86 | }); 87 | -------------------------------------------------------------------------------- /examples/linea/scripts/setValidator.js: -------------------------------------------------------------------------------- 1 | const { JsonRpcProvider, Wallet, formatEther } = require('ethers'); 2 | const { readDeployContract, getLogName } = require('../../../script/utils'); 3 | const logName = require('../../../script/deploy_log_name'); 4 | const { task, types } = require('hardhat/config'); 5 | const { claimL1ToL2Message } = require('./common'); 6 | 7 | require('dotenv').config(); 8 | 9 | task('setValidator', 'Set validator for zkLink') 10 | .addParam('validator', 'Validator Address', undefined, types.string) 11 | .addOptionalParam('active', 'Whether to activate the validator address', true, types.boolean) 12 | .setAction(async (taskArgs, hre) => { 13 | const validatorAddr = taskArgs.validator; 14 | const isActive = taskArgs.active; 15 | console.log(`The validator: address: ${validatorAddr}, active: ${isActive}`); 16 | 17 | const walletPrivateKey = process.env.DEVNET_PRIVKEY; 18 | const l1Provider = new JsonRpcProvider(process.env.L1RPC); 19 | const l2Provider = new JsonRpcProvider(process.env.L2RPC); 20 | const ethereumName = process.env.ETHEREUM; 21 | const lineaName = process.env.LINEA; 22 | const l1Wallet = new Wallet(walletPrivateKey, l1Provider); 23 | const l2Wallet = new Wallet(walletPrivateKey, l2Provider); 24 | 25 | const l1WalletAddress = await l1Wallet.getAddress(); 26 | const l1WalletBalance = formatEther(await l1Provider.getBalance(l1WalletAddress)); 27 | console.log(`${l1WalletAddress} balance on l1: ${l1WalletBalance} ether`); 28 | const l2WalletAddress = await l2Wallet.getAddress(); 29 | const l2WalletBalance = formatEther(await l2Provider.getBalance(l2WalletAddress)); 30 | console.log(`${l2WalletAddress} balance on l2: ${l2WalletBalance} ether`); 31 | 32 | const arbitratorAddr = readDeployContract( 33 | logName.DEPLOY_ARBITRATOR_LOG_PREFIX, 34 | logName.DEPLOY_LOG_ARBITRATOR, 35 | ethereumName, 36 | ); 37 | if (arbitratorAddr === undefined) { 38 | console.log('The arbitrator address not exist'); 39 | return; 40 | } 41 | console.log(`The arbitrator address: ${arbitratorAddr}`); 42 | 43 | const l1GatewayLogName = getLogName(logName.DEPLOY_L1_GATEWAY_LOG_PREFIX, lineaName); 44 | const lineaL1GatewayAddr = readDeployContract(l1GatewayLogName, logName.DEPLOY_GATEWAY, ethereumName); 45 | if (lineaL1GatewayAddr === undefined) { 46 | console.log('linea l1 gateway address not exist'); 47 | return; 48 | } 49 | console.log(`The linea l1 gateway address: ${lineaL1GatewayAddr}`); 50 | 51 | const lineaL2GatewayAddr = readDeployContract( 52 | logName.DEPLOY_L2_GATEWAY_LOG_PREFIX, 53 | logName.DEPLOY_GATEWAY, 54 | lineaName, 55 | ); 56 | if (lineaL2GatewayAddr === undefined) { 57 | console.log('linea l2 gateway address not exist'); 58 | return; 59 | } 60 | console.log(`The linea l2 gateway address: ${lineaL2GatewayAddr}`); 61 | 62 | // forward message to L2 63 | const arbitrator = await hre.ethers.getContractAt('Arbitrator', arbitratorAddr, l1Wallet); 64 | const adapterParams = '0x'; 65 | let tx = await arbitrator.setValidator(lineaL1GatewayAddr, validatorAddr, isActive, adapterParams); 66 | console.log(`The l1 tx hash: ${tx.hash}`); 67 | await tx.wait(); 68 | console.log(`The l1 tx confirmed`); 69 | await claimL1ToL2Message(tx.hash); 70 | }); 71 | -------------------------------------------------------------------------------- /examples/linea/scripts/syncBatchRoot.js: -------------------------------------------------------------------------------- 1 | const { JsonRpcProvider, Wallet, formatEther, keccak256, toUtf8Bytes } = require('ethers'); 2 | const { readDeployContract, getLogName } = require('../../../script/utils'); 3 | const logName = require('../../../script/deploy_log_name'); 4 | const { task } = require('hardhat/config'); 5 | const { claimL1ToL2Message } = require('./common'); 6 | 7 | require('dotenv').config(); 8 | 9 | task('syncBatchRoot', 'Forward message to L2').setAction(async (taskArgs, hre) => { 10 | const walletPrivateKey = process.env.DEVNET_PRIVKEY; 11 | const l1Provider = new JsonRpcProvider(process.env.L1RPC); 12 | const l2Provider = new JsonRpcProvider(process.env.L2RPC); 13 | const ethereumName = process.env.ETHEREUM; 14 | const lineaName = process.env.LINEA; 15 | const l1Wallet = new Wallet(walletPrivateKey, l1Provider); 16 | const l2Wallet = new Wallet(walletPrivateKey, l2Provider); 17 | 18 | const l1WalletAddress = await l1Wallet.getAddress(); 19 | const l1WalletBalance = formatEther(await l1Provider.getBalance(l1WalletAddress)); 20 | console.log(`${l1WalletAddress} balance on l1: ${l1WalletBalance} ether`); 21 | const l2WalletAddress = await l2Wallet.getAddress(); 22 | const l2WalletBalance = formatEther(await l2Provider.getBalance(l2WalletAddress)); 23 | console.log(`${l2WalletAddress} balance on l2: ${l2WalletBalance} ether`); 24 | 25 | const arbitratorAddr = readDeployContract( 26 | logName.DEPLOY_ARBITRATOR_LOG_PREFIX, 27 | logName.DEPLOY_LOG_ARBITRATOR, 28 | ethereumName, 29 | ); 30 | if (arbitratorAddr === undefined) { 31 | console.log('The arbitrator address not exist'); 32 | return; 33 | } 34 | console.log(`The arbitrator address: ${arbitratorAddr}`); 35 | 36 | const zkLinkAddr = readDeployContract(logName.DEPLOY_ZKLINK_LOG_PREFIX, logName.DEPLOY_LOG_ZKLINK_PROXY, lineaName); 37 | if (zkLinkAddr === undefined) { 38 | console.log('zkLink address not exist'); 39 | return; 40 | } 41 | console.log(`The zkLink address: ${zkLinkAddr}`); 42 | 43 | const l1GatewayLogName = getLogName(logName.DEPLOY_L1_GATEWAY_LOG_PREFIX, lineaName); 44 | const lineaL1GatewayAddr = readDeployContract(l1GatewayLogName, logName.DEPLOY_GATEWAY, ethereumName); 45 | if (lineaL1GatewayAddr === undefined) { 46 | console.log('linea l1 gateway address not exist'); 47 | return; 48 | } 49 | console.log(`The linea l1 gateway address: ${lineaL1GatewayAddr}`); 50 | 51 | const lineaL2GatewayAddr = readDeployContract( 52 | logName.DEPLOY_L2_GATEWAY_LOG_PREFIX, 53 | logName.DEPLOY_GATEWAY, 54 | lineaName, 55 | ); 56 | if (lineaL2GatewayAddr === undefined) { 57 | console.log('linea l2 gateway address not exist'); 58 | return; 59 | } 60 | console.log(`The linea l2 gateway address: ${lineaL2GatewayAddr}`); 61 | 62 | // pre-execution calldata 63 | const zkLink = await hre.ethers.getContractAt('DummyZkLink', zkLinkAddr, l2Wallet); 64 | const zklinkIface = zkLink.interface; 65 | const blockNumber = await l2Provider.getBlockNumber(); 66 | console.log(`The current block number: ${blockNumber}`); 67 | const l2LogsRootHash = keccak256(toUtf8Bytes(`L2 logs root hash ${blockNumber}`)); 68 | console.log(`The l2 logs root hash: ${l2LogsRootHash}`); 69 | const executeCalldata = zklinkIface.encodeFunctionData('syncBatchRoot', [blockNumber, l2LogsRootHash, 0]); 70 | console.log(`The call data: ${executeCalldata}`); 71 | 72 | // forward message to L2 73 | const arbitrator = await hre.ethers.getContractAt('DummyArbitrator', arbitratorAddr, l1Wallet); 74 | const adapterParams = '0x'; 75 | let tx = await arbitrator.forwardMessage(lineaL1GatewayAddr, 0, executeCalldata, adapterParams); 76 | console.log(`The l1 tx hash: ${tx.hash}`); 77 | await tx.wait(); 78 | console.log(`The l1 tx confirmed`); 79 | await claimL1ToL2Message(tx.hash); 80 | 81 | // Example txs: 82 | // https://goerli.etherscan.io/tx/0x60eda85e11f963c5317559999bd7a54ae4aa1086e8eff0e306523f9f3947bd7c 83 | // https://goerli.lineascan.build/tx/0x12559d43b03b7bb00a7a0305c47526d9deb1541d61117b194813a83a4fc5a2d3 84 | }); 85 | -------------------------------------------------------------------------------- /examples/linea/scripts/syncL2Requests.js: -------------------------------------------------------------------------------- 1 | const { JsonRpcProvider, Wallet, formatEther } = require('ethers'); 2 | const { L2MessageServiceContract } = require('@consensys/linea-sdk'); 3 | const { readDeployContract } = require('../../../script/utils'); 4 | const logName = require('../../../script/deploy_log_name'); 5 | const { task, types } = require('hardhat/config'); 6 | const { claimL2ToL1Message } = require('./common'); 7 | 8 | require('dotenv').config(); 9 | 10 | task('syncL2Requests', 'Send sync point to arbitrator') 11 | .addParam('txs', 'New sync point', 100, types.int, true) 12 | .setAction(async (taskArgs, hre) => { 13 | const txs = taskArgs.txs; 14 | console.log(`The sync point: ${txs}`); 15 | 16 | const walletPrivateKey = process.env.DEVNET_PRIVKEY; 17 | const l1Provider = new JsonRpcProvider(process.env.L1RPC); 18 | const l2Provider = new JsonRpcProvider(process.env.L2RPC); 19 | const lineaName = process.env.LINEA; 20 | const l1Wallet = new Wallet(walletPrivateKey, l1Provider); 21 | const l2Wallet = new Wallet(walletPrivateKey, l2Provider); 22 | 23 | const l1WalletAddress = await l1Wallet.getAddress(); 24 | const l1WalletBalance = formatEther(await l1Provider.getBalance(l1WalletAddress)); 25 | console.log(`${l1WalletAddress} balance on l1: ${l1WalletBalance} ether`); 26 | const l2WalletAddress = await l2Wallet.getAddress(); 27 | const l2WalletBalance = formatEther(await l2Provider.getBalance(l2WalletAddress)); 28 | console.log(`${l2WalletAddress} balance on l2: ${l2WalletBalance} ether`); 29 | 30 | const zkLinkAddr = readDeployContract(logName.DEPLOY_ZKLINK_LOG_PREFIX, logName.DEPLOY_LOG_ZKLINK_PROXY, lineaName); 31 | if (zkLinkAddr === undefined) { 32 | console.log('zkLink address not exist'); 33 | return; 34 | } 35 | console.log(`The zkLink address: ${zkLinkAddr}`); 36 | 37 | const lineaL2GatewayAddr = readDeployContract( 38 | logName.DEPLOY_L2_GATEWAY_LOG_PREFIX, 39 | logName.DEPLOY_GATEWAY, 40 | lineaName, 41 | ); 42 | if (lineaL2GatewayAddr === undefined) { 43 | console.log('linea l2 gateway address not exist'); 44 | return; 45 | } 46 | console.log(`The linea l2 gateway address: ${lineaL2GatewayAddr}`); 47 | const l2Gateway = await hre.ethers.getContractAt('LineaL2Gateway', lineaL2GatewayAddr, l2Wallet); 48 | const l2MessageServiceAddress = await l2Gateway.MESSAGE_SERVICE(); 49 | 50 | // Transfer ETH to ZKLink as a fee 51 | const minimumFee = await L2MessageServiceContract.getContract( 52 | l2MessageServiceAddress, 53 | l2Provider, 54 | ).minimumFeeInWei(); 55 | console.log(`The minimum fee: ${formatEther(minimumFee.toBigInt())} ether`); 56 | 57 | // send tx 58 | const zkLink = await hre.ethers.getContractAt('DummyZkLink', zkLinkAddr, l2Wallet); 59 | console.log(`Send a l2 message to l1...`); 60 | let tx = await zkLink.syncL2Requests(txs, { 61 | value: minimumFee.toBigInt(), 62 | }); 63 | console.log(`The l2 tx hash: ${tx.hash}`); 64 | await tx.wait(); 65 | console.log(`The l2 tx confirmed`); 66 | await claimL2ToL1Message(tx.hash); 67 | 68 | // Example txs: 69 | // https://goerli.lineascan.build/tx/0x71ac2f88392b0045d0dd2e4eb657c875f7b076301b3ddb15e638e5856d7addd1 70 | // 71 | }); 72 | -------------------------------------------------------------------------------- /examples/manta/.env-sample: -------------------------------------------------------------------------------- 1 | # This is a sample .env file for use in local development. 2 | 3 | # Duplicate this file as .env here 4 | 5 | # Your Private key 6 | 7 | DEVNET_PRIVKEY="0x your key here" 8 | 9 | # Hosted Aggregator Node (JSON-RPC Endpoint). This is Manta pacific Testnet, can use any Manta chain 10 | 11 | L2RPC="https://pacific-rpc.sepolia-testnet.manta.network/http" 12 | 13 | # Ethereum RPC; i.e., for Sepolia https://sepolia.infura.io/v3/ 14 | 15 | L1RPC="" 16 | 17 | # Manta chain name 18 | MANTA="MANTATEST" 19 | 20 | # Ethereum chain name 21 | ETHEREUM="SEPOLIA" -------------------------------------------------------------------------------- /examples/manta/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /examples/manta/hardhat.config.js: -------------------------------------------------------------------------------- 1 | require('@nomiclabs/hardhat-ethers'); 2 | require('./scripts/mantaTasks'); 3 | 4 | const BaseConfig = require('../../hardhat.base.config'); 5 | 6 | module.exports = Object.assign({}, BaseConfig, { 7 | paths: { 8 | cache: '../../cache', 9 | artifacts: '../../artifacts', 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /examples/manta/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "manta-example", 3 | "license": "MIT", 4 | "version": "1.0.0", 5 | "scripts": {}, 6 | "devDependencies": { 7 | "@eth-optimism/sdk": "^3.2.3", 8 | "@nomiclabs/hardhat-ethers": "^2.0.2", 9 | "ethers": "^5.7.2", 10 | "hardhat": "^2.9.1" 11 | }, 12 | "dependencies": { 13 | "@eth-optimism/core-utils": "^0.13.1", 14 | "dotenv": "^16.4.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/manta/scripts/constants.js: -------------------------------------------------------------------------------- 1 | // Testnet 2 | const L1_TESTNET_CONTRACTS = { 3 | StateCommitmentChain: '0x0000000000000000000000000000000000000000', 4 | BondManager: '0x0000000000000000000000000000000000000000', 5 | CanonicalTransactionChain: '0x0000000000000000000000000000000000000000', 6 | AddressManager: '0x0691B7aaAc9B903c9a99B2371bCFB43601B45711', 7 | L1CrossDomainMessenger: '0xFe7cF31c4579bb1C578716e04E1Ae16Ac5549fF0', 8 | L1StandardBridge: '0xCAD25C95679839996F3162d8657B1CAe4517F78f', 9 | OptimismPortal: '0x80f86c5d3AE8cF84596FF22DB2829F1b7a9Fe83d', 10 | L2OutputOracle: '0x2dd44d1b04170C5623cCc55DD5ed43FAB08b0B46', 11 | }; 12 | 13 | // Mainnet 14 | const L1_MAINNET_CONTRACTS = { 15 | StateCommitmentChain: '0x0000000000000000000000000000000000000000', 16 | BondManager: '0x0000000000000000000000000000000000000000', 17 | CanonicalTransactionChain: '0x0000000000000000000000000000000000000000', 18 | AddressManager: '0x3Ad319BB4872F8cB75a26Ac30CC4bD2d56b67b05', 19 | L1CrossDomainMessenger: '0x635ba609680c55C3bDd0B3627b4c5dB21b13c310', 20 | L1StandardBridge: '0x3B95bC951EE0f553ba487327278cAc44f29715E5', 21 | OptimismPortal: '0x9168765EE952de7C6f8fC6FaD5Ec209B960b7622', 22 | L2OutputOracle: '0x30c789674ad3B458886BBC9abf42EEe19EA05C1D', 23 | }; 24 | 25 | module.exports = { 26 | L1_TESTNET_CONTRACTS, 27 | L1_MAINNET_CONTRACTS, 28 | }; 29 | -------------------------------------------------------------------------------- /examples/mantle/.env-sample: -------------------------------------------------------------------------------- 1 | # This is a sample .env file for use in local development. 2 | 3 | # Duplicate this file as .env here 4 | 5 | # Your Private key 6 | 7 | DEVNET_PRIVKEY="0x your key here" 8 | 9 | # Hosted Aggregator Node (JSON-RPC Endpoint). This is Mantle Testnet, can use any Mantle chain 10 | 11 | L2RPC="https://rpc.testnet.mantle.xyz" 12 | 13 | # Ethereum RPC; i.e., for Goerli https://goerli.infura.io/v3/ 14 | 15 | L1RPC="" 16 | 17 | # Mantle chain name 18 | MANTLE="MANTLETEST" 19 | 20 | # Ethereum chain name 21 | ETHEREUM="GOERLI" -------------------------------------------------------------------------------- /examples/mantle/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /examples/mantle/hardhat.config.js: -------------------------------------------------------------------------------- 1 | require('@nomiclabs/hardhat-ethers'); 2 | require('./scripts/mantleTasks'); 3 | 4 | const BaseConfig = require('../../hardhat.base.config'); 5 | 6 | module.exports = Object.assign({}, BaseConfig, { 7 | paths: { 8 | cache: '../../cache', 9 | artifacts: '../../artifacts', 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /examples/mantle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mantle-example", 3 | "license": "MIT", 4 | "version": "1.0.0", 5 | "scripts": {}, 6 | "devDependencies": { 7 | "@mantleio/sdk": "^1.0.3", 8 | "@nomiclabs/hardhat-ethers": "^2.0.2", 9 | "ethers": "^5.7.2", 10 | "hardhat": "^2.9.1" 11 | }, 12 | "dependencies": { 13 | "dotenv": "^16.4.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/optimism/.env-sample: -------------------------------------------------------------------------------- 1 | # This is a sample .env file for use in local development. 2 | 3 | # Duplicate this file as .env here 4 | 5 | # Your Private key 6 | 7 | DEVNET_PRIVKEY="0x your key here" 8 | 9 | # Hosted Aggregator Node (JSON-RPC Endpoint). This is Optimism Sepolia Testnet, can use any Optimism chain 10 | 11 | L2RPC="https://sepolia.optimism.io" 12 | 13 | # Ethereum RPC; i.e., for Sepolia https://sepolia.infura.io/v3/ 14 | 15 | L1RPC="" 16 | 17 | # Optimism chain name 18 | OPTIMISM="OPTIMISMTEST" 19 | 20 | # Ethereum chain name 21 | ETHEREUM="SEPOLIA" -------------------------------------------------------------------------------- /examples/optimism/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /examples/optimism/hardhat.config.js: -------------------------------------------------------------------------------- 1 | require('@nomiclabs/hardhat-ethers'); 2 | require('./scripts/optimismTasks'); 3 | 4 | const BaseConfig = require('../../hardhat.base.config'); 5 | 6 | module.exports = Object.assign({}, BaseConfig, { 7 | paths: { 8 | cache: '../../cache', 9 | artifacts: '../../artifacts', 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /examples/optimism/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "optimism-example", 3 | "license": "MIT", 4 | "version": "1.0.0", 5 | "scripts": {}, 6 | "devDependencies": { 7 | "@eth-optimism/sdk": "^3.3.2", 8 | "@nomiclabs/hardhat-ethers": "^2.0.2", 9 | "ethers": "^5.7.2", 10 | "hardhat": "^2.9.1" 11 | }, 12 | "dependencies": { 13 | "@eth-optimism/core-utils": "^0.13.1", 14 | "dotenv": "^16.4.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/scroll/.env-sample: -------------------------------------------------------------------------------- 1 | # This is a sample .env file for use in local development. 2 | 3 | # Duplicate this file as .env here 4 | 5 | # Your Private key 6 | 7 | DEVNET_PRIVKEY="0x your key here" 8 | 9 | # Hosted Aggregator Node (JSON-RPC Endpoint). This is Scroll Sepolia Testnet, can use any Scroll chain 10 | 11 | L2RPC="https://sepolia-rpc.scroll.io/" 12 | 13 | # Ethereum RPC; i.e., for Sepolia https://sepolia.infura.io/v3/ 14 | 15 | L1RPC="" 16 | 17 | # Scroll chain name 18 | SCROLL="SCROLLTEST" 19 | 20 | # Ethereum chain name 21 | ETHEREUM="SEPOLIA" -------------------------------------------------------------------------------- /examples/scroll/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /examples/scroll/hardhat.config.js: -------------------------------------------------------------------------------- 1 | require('@nomicfoundation/hardhat-toolbox'); 2 | require('./scripts/syncL2Requests'); 3 | require('./scripts/syncBatchRoot'); 4 | require('./scripts/setValidator'); 5 | require('./scripts/changeFeeParams'); 6 | require('./scripts/checkTxStatus'); 7 | 8 | const BaseConfig = require('../../hardhat.base.config'); 9 | 10 | BigInt.prototype.toJSON = function () { 11 | return this.toString(); 12 | }; 13 | 14 | module.exports = Object.assign({}, BaseConfig, { 15 | paths: { 16 | cache: '../../cache', 17 | artifacts: '../../artifacts', 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /examples/scroll/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scroll-example", 3 | "license": "MIT", 4 | "version": "1.0.0", 5 | "scripts": {}, 6 | "devDependencies": { 7 | "@eth-optimism/core-utils": "^0.13.2", 8 | "@nomicfoundation/hardhat-toolbox": "^4.0.0", 9 | "axios": "^1.7.2", 10 | "ethers": "^6.11.1", 11 | "hardhat": "^2.19.5" 12 | }, 13 | "dependencies": { 14 | "dotenv": "^16.4.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/scroll/scripts/changeFeeParams.js: -------------------------------------------------------------------------------- 1 | const { JsonRpcProvider, Wallet, AbiCoder, formatEther } = require('ethers'); 2 | const { readDeployContract, getLogName } = require('../../../script/utils'); 3 | const logName = require('../../../script/deploy_log_name'); 4 | const { task } = require('hardhat/config'); 5 | const { ScrollSDK } = require('./scrollSDK'); 6 | const { INIT_FEE_PARAMS } = require('../../../script/zksync_era'); 7 | 8 | require('dotenv').config(); 9 | 10 | task('changeFeeParams', 'Change fee params for zkLink').setAction(async (taskArgs, hre) => { 11 | const walletPrivateKey = process.env.DEVNET_PRIVKEY; 12 | const l1Provider = new JsonRpcProvider(process.env.L1RPC); 13 | const l2Provider = new JsonRpcProvider(process.env.L2RPC); 14 | const ethereumName = process.env.ETHEREUM; 15 | const scrollName = process.env.SCROLL; 16 | const scrollSDK = new ScrollSDK(ethereumName, l1Provider, l2Provider); 17 | const l1Wallet = new Wallet(walletPrivateKey, l1Provider); 18 | 19 | const l1WalletAddress = await l1Wallet.getAddress(); 20 | const l1WalletBalance = formatEther(await l1Provider.getBalance(l1WalletAddress)); 21 | console.log(`${l1WalletAddress} balance on l1: ${l1WalletBalance} ether`); 22 | 23 | const arbitratorAddr = readDeployContract( 24 | logName.DEPLOY_ARBITRATOR_LOG_PREFIX, 25 | logName.DEPLOY_LOG_ARBITRATOR, 26 | ethereumName, 27 | ); 28 | if (arbitratorAddr === undefined) { 29 | console.log('The arbitrator address not exist'); 30 | return; 31 | } 32 | console.log(`The arbitrator address: ${arbitratorAddr}`); 33 | 34 | const l1GatewayLogName = getLogName(logName.DEPLOY_L1_GATEWAY_LOG_PREFIX, scrollName); 35 | const l1GatewayAddr = readDeployContract(l1GatewayLogName, logName.DEPLOY_GATEWAY, ethereumName); 36 | if (l1GatewayAddr === undefined) { 37 | console.log('The l1 gateway address not exist'); 38 | return; 39 | } 40 | console.log(`The l1 gateway address: ${l1GatewayAddr}`); 41 | 42 | const l2GatewayAddr = readDeployContract(logName.DEPLOY_L2_GATEWAY_LOG_PREFIX, logName.DEPLOY_GATEWAY, scrollName); 43 | if (l2GatewayAddr === undefined) { 44 | console.log('l2 gateway address not exist'); 45 | return; 46 | } 47 | console.log(`The l2 gateway address: ${l2GatewayAddr}`); 48 | 49 | // forward message to L2 50 | const arbitrator = await hre.ethers.getContractAt('Arbitrator', arbitratorAddr, l1Wallet); 51 | /** 52 | * The adapterParams is the parameters for the adapter, which is used to parse the calldata. 53 | * finalizeMessageGasLimit: the gas limit for the L2 to finalize the message. 54 | */ 55 | const zkLink = await hre.ethers.getContractFactory('ZkLink'); 56 | const zkLinkCallValue = BigInt(0); 57 | const zkLinkCallData = zkLink.interface.encodeFunctionData('changeFeeParams', [INIT_FEE_PARAMS]); 58 | const l2GatewayFactory = await hre.ethers.getContractFactory('ScrollL2Gateway'); 59 | const l2GatewayCallData = l2GatewayFactory.interface.encodeFunctionData('claimMessageCallback', [ 60 | zkLinkCallValue, 61 | zkLinkCallData, 62 | ]); 63 | let finalizeMessageGasLimit = await scrollSDK.l2EstimateRelayMessageGasLimit( 64 | l1GatewayAddr, 65 | l2GatewayAddr, 66 | zkLinkCallValue, 67 | l2GatewayCallData, 68 | ); 69 | finalizeMessageGasLimit = (finalizeMessageGasLimit * BigInt(120)) / BigInt(100); 70 | console.log(`The l1 to l2 gas limit: ${finalizeMessageGasLimit}`); 71 | const gasValue = await scrollSDK.l1ToL2GasValue(finalizeMessageGasLimit); 72 | console.log(`The fee: ${formatEther(gasValue)} ether`); 73 | const adapterParams = AbiCoder.defaultAbiCoder().encode(['uint256'], [finalizeMessageGasLimit]); 74 | let tx = await arbitrator.changeFeeParams(l1GatewayAddr, INIT_FEE_PARAMS, adapterParams, { 75 | value: gasValue + zkLinkCallValue, 76 | }); 77 | console.log(`The tx hash: ${tx.hash} , waiting confirm...`); 78 | await tx.wait(); 79 | console.log(`The tx confirmed`); 80 | 81 | // Waiting for the official Scroll bridge to forward the message to L2 82 | // No user action is required for follow-up. 83 | }); 84 | 85 | task('encodeChangeFeeParams', 'Get the calldata of changing fee params for zkLink').setAction(async (_, hre) => { 86 | const ethereumName = process.env.ETHEREUM; 87 | const scrollName = process.env.SCROLL; 88 | const l1GatewayLogName = getLogName(logName.DEPLOY_L1_GATEWAY_LOG_PREFIX, scrollName); 89 | const l1GatewayAddr = readDeployContract(l1GatewayLogName, logName.DEPLOY_GATEWAY, ethereumName); 90 | if (l1GatewayAddr === undefined) { 91 | console.log('The l1 gateway address not exist'); 92 | return; 93 | } 94 | console.log(`The l1 gateway address: ${l1GatewayAddr}`); 95 | 96 | const adapterParams = AbiCoder.defaultAbiCoder().encode(['uint256'], [200000]); 97 | 98 | const arbitratorFactory = await hre.ethers.getContractFactory('Arbitrator'); 99 | const calldata = arbitratorFactory.interface.encodeFunctionData('changeFeeParams', [ 100 | l1GatewayAddr, 101 | INIT_FEE_PARAMS, 102 | adapterParams, 103 | ]); 104 | console.log(`The changeFeeParams calldata: ${calldata}`); 105 | }); 106 | -------------------------------------------------------------------------------- /examples/scroll/scripts/syncL2Requests.js: -------------------------------------------------------------------------------- 1 | const { JsonRpcProvider, Wallet, formatEther } = require('ethers'); 2 | const { readDeployContract } = require('../../../script/utils'); 3 | const logName = require('../../../script/deploy_log_name'); 4 | const { task, types } = require('hardhat/config'); 5 | 6 | require('dotenv').config(); 7 | 8 | task('syncL2Requests', 'Send sync point to arbitrator') 9 | .addOptionalParam('txs', 'New sync point', 100, types.int) 10 | .setAction(async (taskArgs, hre) => { 11 | const txs = taskArgs.txs; 12 | console.log(`The sync point: ${txs}`); 13 | 14 | const walletPrivateKey = process.env.DEVNET_PRIVKEY; 15 | const l2Provider = new JsonRpcProvider(process.env.L2RPC); 16 | const scrollName = process.env.SCROLL; 17 | const l2Wallet = new Wallet(walletPrivateKey, l2Provider); 18 | 19 | const l2WalletAddress = await l2Wallet.getAddress(); 20 | const l2WalletBalance = formatEther(await l2Provider.getBalance(l2WalletAddress)); 21 | console.log(`${l2WalletAddress} balance on l2: ${l2WalletBalance} ether`); 22 | 23 | const l2GatewayAddr = readDeployContract(logName.DEPLOY_L2_GATEWAY_LOG_PREFIX, logName.DEPLOY_GATEWAY, scrollName); 24 | if (l2GatewayAddr === undefined) { 25 | console.log('scroll l2 gateway address not exist'); 26 | return; 27 | } 28 | console.log(`The scroll l2 gateway address: ${l2GatewayAddr}`); 29 | 30 | const zkLinkAddr = readDeployContract( 31 | logName.DEPLOY_ZKLINK_LOG_PREFIX, 32 | logName.DEPLOY_LOG_ZKLINK_PROXY, 33 | scrollName, 34 | ); 35 | if (zkLinkAddr === undefined) { 36 | console.log('zkLink address not exist'); 37 | return; 38 | } 39 | console.log(`The zkLink address: ${zkLinkAddr}`); 40 | 41 | // send txs 42 | const zkLink = await hre.ethers.getContractAt('ZkLink', zkLinkAddr, l2Wallet); 43 | console.log(`Send a l2 message to l1...`); 44 | let tx = await zkLink.syncL2Requests(txs); 45 | console.log(`The tx hash: ${tx.hash}`); 46 | await tx.wait(); 47 | console.log(`The tx confirmed`); 48 | }); 49 | -------------------------------------------------------------------------------- /examples/zklinkNova/.env-sample: -------------------------------------------------------------------------------- 1 | # This is a sample .env file for use in local development. 2 | 3 | # Duplicate this file as .env here 4 | 5 | # Your Private key 6 | 7 | DEVNET_PRIVKEY="0x your key here" 8 | 9 | # This is zkLink Sepolia Testnet 10 | 11 | ZKLINK_NOVA_RPC="https://sepolia.rpc.zklink.io" 12 | 13 | # Linea RPC 14 | 15 | LINEA_RPC="https://rpc.sepolia.linea.build" 16 | 17 | # Ethereum RPC; i.e., for Sepolia https://sepolia.infura.io/v3/ 18 | 19 | ETHEREUM_RPC="" -------------------------------------------------------------------------------- /examples/zklinkNova/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /examples/zklinkNova/hardhat.config.js: -------------------------------------------------------------------------------- 1 | require('@nomicfoundation/hardhat-toolbox'); 2 | require('./scripts/getTxStatus'); 3 | require('./scripts/decodeRawTx'); 4 | require('./scripts/printGovernanceCall'); 5 | require('./scripts/sendRawTx'); 6 | 7 | const BaseConfig = require('../../hardhat.base.config'); 8 | 9 | module.exports = Object.assign({}, BaseConfig, { 10 | paths: { 11 | cache: '../../cache', 12 | artifacts: '../../artifacts', 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /examples/zklinkNova/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zksync-example", 3 | "license": "MIT", 4 | "version": "1.0.0", 5 | "scripts": {}, 6 | "devDependencies": { 7 | "@consensys/linea-sdk": "^0.3.0", 8 | "@nomicfoundation/hardhat-toolbox": "^4.0.0", 9 | "ethers": "^6.10.0", 10 | "hardhat": "^2.19.3", 11 | "zksync-ethers": "^6.7.0" 12 | }, 13 | "dependencies": { 14 | "dotenv": "^16.4.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/zklinkNova/scripts/decodeRawTx.js: -------------------------------------------------------------------------------- 1 | const zksync = require('zksync-ethers'); 2 | const { recoverAddress } = require('ethers'); 3 | const { task, types } = require('hardhat/config'); 4 | 5 | task('decodeRawTx', 'Decode the raw tx of nova') 6 | .addParam('rawTx', 'The raw tx', undefined, types.string) 7 | .setAction(async taskArgs => { 8 | const rawTx = taskArgs.rawTx; 9 | const tx = zksync.types.Transaction.from(rawTx); 10 | console.log(JSON.stringify(tx.toJSON())); 11 | const from = recoverAddress(tx.unsignedHash, tx.signature); 12 | console.log(from); 13 | }); 14 | -------------------------------------------------------------------------------- /examples/zklinkNova/scripts/getTxStatus.js: -------------------------------------------------------------------------------- 1 | const { Provider } = require('zksync-ethers'); 2 | const { ethers } = require('ethers'); 3 | const { LineaSDK } = require('@consensys/linea-sdk'); 4 | const { LineaRollup__factory } = require('@consensys/linea-sdk/dist/typechain'); 5 | const { task, types } = require('hardhat/config'); 6 | 7 | require('dotenv').config(); 8 | 9 | // This command is used to check if a tx of zklink nova is considered to be finalized on Ethereum 10 | // A tx of zklink nova is considered to be finalized on Ethereum means: 11 | // 1. The tx is confirmed on zklink nova 12 | // 2. The block of zklink nova which contain the tx is considered to be finalized on Linea 13 | // 3. The block of Linea which contain the finalize tx of nova is considered to be finalized on Ethereum 14 | task('getTxStatus', 'Get the tx status of nova') 15 | .addParam('txHash', 'The tx hash', undefined, types.string) 16 | .addOptionalParam('useCommit', 'Use commit as the nova finalize tx', true, types.boolean) 17 | .setAction(async taskArgs => { 18 | // This is a tx hash of zklink nova 19 | const txHash = taskArgs.txHash; 20 | // A block of zklink nova must first be committed to Linea, then proven, and finally executed. 21 | // Generally, a block can be committed to Linea in about 10 minutes after it is generated, but proof takes longer, which may take about an hour. 22 | // If `useCommit` set to true, then the finalize time of a nova tx to Linea is about 10 minutes 23 | // Otherwise it may take more than 1 hour 24 | const useCommit = taskArgs.useCommit; 25 | console.log(`Get the status of tx: ${txHash}`); 26 | const zkLinkNovaProvider = new Provider(process.env.ZKLINK_NOVA_RPC); 27 | const lineaProvider = new ethers.JsonRpcProvider(process.env.LINEA_RPC); 28 | const ethereumProvider = new ethers.JsonRpcProvider(process.env.ETHEREUM_RPC); 29 | 30 | // We check if the tx is confirmed on zklink nova 31 | const txReceipt = await zkLinkNovaProvider.getTransactionReceipt(txHash); 32 | if (!txReceipt.blockNumber) { 33 | console.log(`Tx status: not confirmed`); 34 | return; 35 | } 36 | console.log(`Tx block: ${txReceipt.blockNumber}`); 37 | 38 | // Then we check the finalize status on Linea of the block which contain the tx 39 | // If `useCommit` is true then the finalize time of the block will be about 10 minutes 40 | const blockDetails = await zkLinkNovaProvider.getBlockDetails(txReceipt.blockNumber); 41 | const novaFinalizeTx = useCommit ? blockDetails.commitTxHash : blockDetails.proveTxHash; 42 | if (!novaFinalizeTx) { 43 | console.log(`Tx status: not finalized on linea`); 44 | return; 45 | } 46 | console.log(`Nova finalize tx hash: ${novaFinalizeTx}`); 47 | 48 | const novaFinalizeTxReceipt = await lineaProvider.getTransactionReceipt(novaFinalizeTx); 49 | if (!novaFinalizeTxReceipt.blockNumber) { 50 | console.log(`Tx status: nova finalize tx not confirmed`); 51 | return; 52 | } 53 | console.log(`Nova finalize tx block number on linea: ${novaFinalizeTxReceipt.blockNumber}`); 54 | 55 | // After the nova block finalize tx is confirmed on linea, we continue to check the status of Linea's tx finalized on Ethereum 56 | // Linea deployed a rollup contract on Ethereum and has a readable interface `function currentL2BlockNumber() external view` 57 | // When the number of a block on Linea is smaller or equal to the value of `currentL2BlockNumber` it means the block is finalized on Ethereum 58 | const ethereumChainId = (await ethereumProvider.getNetwork()).chainId; 59 | const network = ethereumChainId === BigInt(1) ? 'linea-mainnet' : 'linea-sepolia'; 60 | const lineaSDK = new LineaSDK({ 61 | l1RpcUrl: process.env.ETHEREUM_RPC ?? '', 62 | l2RpcUrl: process.env.LINEA_RPC ?? '', 63 | network, 64 | mode: 'read-only', 65 | }); 66 | const lineaL1Contract = lineaSDK.getL1Contract(); 67 | 68 | const lineaRollup = LineaRollup__factory.connect(lineaL1Contract.contractAddress, ethereumProvider); 69 | const currentFinalizeBlockNumber = await lineaRollup.currentL2BlockNumber(); 70 | console.log(`Linea current finalize block number: ${currentFinalizeBlockNumber}`); 71 | if (currentFinalizeBlockNumber >= novaFinalizeTxReceipt.blockNumber) { 72 | console.log(`Tx status: finalized on ethereum`); 73 | } else { 74 | console.log(`Tx status: not finalized on ethereum`); 75 | } 76 | }); 77 | -------------------------------------------------------------------------------- /examples/zklinkNova/scripts/printGovernanceCall.js: -------------------------------------------------------------------------------- 1 | const { ethers } = require('ethers'); 2 | const { task, types } = require('hardhat/config'); 3 | 4 | task('printSetValidator', 'Print the operation data of setValidator') 5 | .addParam('zkLink', 'The zkLink address', undefined, types.string) 6 | .addParam('validator', 'The validator address', undefined, types.string) 7 | .addOptionalParam('active', 'The validator active status', true, types.boolean) 8 | .setAction(async (taskArgs, hre) => { 9 | const targetAddress = taskArgs.zkLink; 10 | const validatorAddress = taskArgs.validator; 11 | const active = taskArgs.active; 12 | const zkLink = await hre.ethers.getContractFactory('ZkLink'); 13 | const callData = zkLink.interface.encodeFunctionData('setValidator', [validatorAddress, active]); 14 | const governance = await hre.ethers.getContractFactory('Governance'); 15 | printOperation(governance, targetAddress, 0, callData); 16 | }); 17 | 18 | function printOperation(governance, targetAddress, value, callData) { 19 | const operation = { 20 | calls: [{ target: targetAddress, value: value, data: callData }], 21 | predecessor: ethers.ZeroHash, 22 | salt: ethers.hexlify(ethers.randomBytes(32)), 23 | }; 24 | console.log('Operation:', operation); 25 | console.log('Schedule operation: ', governance.interface.encodeFunctionData('scheduleTransparent', [operation, 0])); 26 | console.log( 27 | `Execute operation value: ${value}, calldata`, 28 | governance.interface.encodeFunctionData('execute', [operation]), 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /examples/zklinkNova/scripts/sendRawTx.js: -------------------------------------------------------------------------------- 1 | const { ethers } = require('ethers'); 2 | const { task, types } = require('hardhat/config'); 3 | 4 | require('dotenv').config(); 5 | 6 | task('sendRawTx', 'Send raw tx to nova') 7 | .addParam('to', 'The to address', undefined, types.string) 8 | .addParam('data', 'The call data', undefined, types.string) 9 | .addOptionalParam('value', 'The call value(unit: ether)', '0', types.string) 10 | .addOptionalParam('gasPrice', 'The gas price(unit: gwei)', '0', types.string) 11 | .setAction(async taskArgs => { 12 | const to = taskArgs.to; 13 | const data = taskArgs.data; 14 | const value = ethers.parseEther(taskArgs.value); 15 | const gasPrice = ethers.parseUnits(taskArgs.gasPrice, 'gwei'); 16 | 17 | const provider = new ethers.JsonRpcProvider(process.env.ZKLINK_NOVA_RPC); 18 | const wallet = new ethers.Wallet(process.env.DEVNET_PRIVKEY, provider); 19 | const tx = await wallet.sendTransaction({ 20 | to, 21 | data, 22 | value, 23 | gasPrice, 24 | }); 25 | console.log(`The tx hash: ${tx.hash}`); 26 | const txReceipt = await tx.wait(1); 27 | console.log(`The tx status: ${txReceipt.status}`); 28 | }); 29 | -------------------------------------------------------------------------------- /examples/zkpolygon/.env-sample: -------------------------------------------------------------------------------- 1 | # This is a sample .env file for use in local development. 2 | 3 | # Duplicate this file as .env here 4 | 5 | # Your Private key 6 | 7 | DEVNET_PRIVKEY="0x your key here" 8 | 9 | # Hosted Aggregator Node (JSON-RPC Endpoint). This is zkPolygon Goerli Testnet, can use any zkPolygon chain 10 | 11 | L2RPC="https://rpc.public.zkevm-test.net" 12 | 13 | # Ethereum RPC; i.e., for Goerli https://goerli.infura.io/v3/ 14 | 15 | L1RPC="" 16 | 17 | # zkPolygon chain name 18 | ZKPOLYGON="ZKPOLYGONTEST" 19 | 20 | # Ethereum chain name 21 | ETHEREUM="GOERLI" -------------------------------------------------------------------------------- /examples/zkpolygon/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /examples/zkpolygon/hardhat.config.js: -------------------------------------------------------------------------------- 1 | require('@nomiclabs/hardhat-ethers'); 2 | require('./scripts/syncL2Requests'); 3 | require('./scripts/syncBatchRoot'); 4 | require('./scripts/setValidator'); 5 | require('./scripts/changeFeeParams'); 6 | 7 | const BaseConfig = require('../../hardhat.base.config'); 8 | 9 | module.exports = Object.assign({}, BaseConfig, { 10 | paths: { 11 | cache: '../../cache', 12 | artifacts: '../../artifacts', 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /examples/zkpolygon/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zkpolygon-example", 3 | "license": "MIT", 4 | "version": "1.0.0", 5 | "scripts": {}, 6 | "devDependencies": { 7 | "@maticnetwork/maticjs": "^3.8.0", 8 | "@maticnetwork/maticjs-ethers": "^1.0.3", 9 | "@nomiclabs/hardhat-ethers": "^2.0.2", 10 | "hardhat": "^2.9.1" 11 | }, 12 | "dependencies": { 13 | "dotenv": "^16.4.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/zkpolygon/scripts/constants.js: -------------------------------------------------------------------------------- 1 | const CROSS_CHAIN_MESSENGER_ABI = [ 2 | { 3 | inputs: [ 4 | { internalType: 'bytes32[32]', name: 'smtProof', type: 'bytes32[32]' }, 5 | { internalType: 'uint32', name: 'index', type: 'uint32' }, 6 | { internalType: 'bytes32', name: 'mainnetExitRoot', type: 'bytes32' }, 7 | { internalType: 'bytes32', name: 'rollupExitRoot', type: 'bytes32' }, 8 | { internalType: 'uint32', name: 'originNetwork', type: 'uint32' }, 9 | { internalType: 'address', name: 'originAddress', type: 'address' }, 10 | { internalType: 'uint32', name: 'destinationNetwork', type: 'uint32' }, 11 | { internalType: 'address', name: 'destinationAddress', type: 'address' }, 12 | { internalType: 'uint256', name: 'amount', type: 'uint256' }, 13 | { internalType: 'bytes', name: 'metadata', type: 'bytes' }, 14 | ], 15 | name: 'claimMessage', 16 | outputs: [], 17 | stateMutability: 'nonpayable', 18 | type: 'function', 19 | }, 20 | ]; 21 | 22 | const L1_CROSS_CHAIN_MESSENGER_ADDREESS = '0xF6BEEeBB578e214CA9E23B0e9683454Ff88Ed2A7'; 23 | const L2_CROSS_CHAIN_MESSENGER_ADDREESS = '0xF6BEEeBB578e214CA9E23B0e9683454Ff88Ed2A7'; 24 | 25 | module.exports = { 26 | CROSS_CHAIN_MESSENGER_ABI, 27 | L1_CROSS_CHAIN_MESSENGER_ADDREESS, 28 | L2_CROSS_CHAIN_MESSENGER_ADDREESS, 29 | }; 30 | -------------------------------------------------------------------------------- /examples/zksync/.env-sample: -------------------------------------------------------------------------------- 1 | # This is a sample .env file for use in local development. 2 | 3 | # Duplicate this file as .env here 4 | 5 | # Your Private key 6 | 7 | DEVNET_PRIVKEY="0x your key here" 8 | 9 | # Hosted Aggregator Node (JSON-RPC Endpoint). This is zkSync Sepolia Testnet, can use any zkSync chain 10 | 11 | L2RPC="https://sepolia.era.zksync.dev" 12 | 13 | # Ethereum RPC; i.e., for Sepolia https://sepolia.infura.io/v3/ 14 | 15 | L1RPC="" 16 | 17 | # zkSync chain name 18 | ZKSYNC="ZKSYNCTEST" 19 | 20 | # Ethereum chain name 21 | ETHEREUM="SEPOLIA" 22 | 23 | # Your Private key 24 | 25 | ZKLINK_DEVNET_PRIVKEY="0x your key here" 26 | 27 | # Hosted Aggregator Node (JSON-RPC Endpoint). This is zkLink Testnet, can use any zkLink chain 28 | 29 | ZKLINK_L2RPC="https://goerli.rpc.zklink.io" 30 | 31 | # Linea RPC; i.e., for Sepolia https://rpc.sepolia.linea.build 32 | 33 | ZKLINK_L1RPC="https://rpc.sepolia.linea.build" 34 | 35 | # Linea chain name 36 | LINEA="LINEATEST" -------------------------------------------------------------------------------- /examples/zksync/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /examples/zksync/hardhat.config.js: -------------------------------------------------------------------------------- 1 | require('@nomicfoundation/hardhat-toolbox'); 2 | require('@matterlabs/hardhat-zksync-verify'); 3 | require('./scripts/syncL2Requests'); 4 | require('./scripts/syncBatchRoot'); 5 | require('./scripts/setValidator'); 6 | require('./scripts/changeFeeParams'); 7 | require('./scripts/governance'); 8 | 9 | const BaseConfig = require('../../hardhat.base.config'); 10 | 11 | module.exports = Object.assign({}, BaseConfig, { 12 | paths: { 13 | cache: '../../cache', 14 | artifacts: '../../artifacts', 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /examples/zksync/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zksync-example", 3 | "license": "MIT", 4 | "version": "1.0.0", 5 | "scripts": {}, 6 | "devDependencies": { 7 | "@matterlabs/hardhat-zksync-verify": "^1.3.0", 8 | "@nomicfoundation/hardhat-toolbox": "^4.0.0", 9 | "@nomicfoundation/hardhat-verify": "^2.0.4", 10 | "ethers": "^6.10.0", 11 | "hardhat": "^2.19.3" 12 | }, 13 | "dependencies": { 14 | "dotenv": "^16.4.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/zksync/scripts/syncL2Requests.js: -------------------------------------------------------------------------------- 1 | const { Provider, Wallet } = require('zksync-ethers'); 2 | const { ethers } = require('ethers'); 3 | const { readDeployContract, getLogName } = require('../../../script/utils'); 4 | const logName = require('../../../script/deploy_log_name'); 5 | const { task, types } = require('hardhat/config'); 6 | 7 | require('dotenv').config(); 8 | 9 | task('syncL2Requests', 'Send sync point from zkLink to arbitrator') 10 | .addParam('txs', 'New sync point', 100, types.int, true) 11 | .setAction(async (taskArgs, hre) => { 12 | const txs = taskArgs.txs; 13 | console.log(`The sync point: ${txs}`); 14 | 15 | const walletPrivateKey = process.env.DEVNET_PRIVKEY; 16 | const l1Provider = new Provider(process.env.L1RPC); 17 | console.log(`Block number: ${await l1Provider.getBlockNumber()}`); 18 | 19 | const l2Provider = new Provider(process.env.L2RPC); 20 | console.log(`Block number: ${await l2Provider.getBlockNumber()}`); 21 | 22 | const zksyncName = process.env.ZKSYNC; 23 | const ethereumName = process.env.ETHEREUM; 24 | const l1Wallet = new ethers.Wallet(walletPrivateKey, l1Provider); 25 | const l2Wallet = new Wallet(walletPrivateKey, l2Provider, l1Provider); 26 | 27 | const l2WalletAddress = await l2Wallet.getAddress(); 28 | const l2WalletBalance = ethers.formatEther(await l2Wallet.getBalance()); 29 | console.log(`${l2WalletAddress} balance on l2: ${l2WalletBalance} ether`); 30 | 31 | const zkLinkAddr = readDeployContract( 32 | logName.DEPLOY_ZKLINK_LOG_PREFIX, 33 | logName.DEPLOY_LOG_ZKLINK_PROXY, 34 | zksyncName, 35 | ); 36 | if (zkLinkAddr === undefined) { 37 | console.log('zkLink address not exist'); 38 | return; 39 | } 40 | console.log(`The zkLink address: ${zkLinkAddr}`); 41 | 42 | const l1GatewayLogName = getLogName(logName.DEPLOY_L1_GATEWAY_LOG_PREFIX, zksyncName); 43 | const l1GatewayAddr = readDeployContract(l1GatewayLogName, logName.DEPLOY_GATEWAY, ethereumName); 44 | if (l1GatewayAddr === undefined) { 45 | console.log('l1 gateway address not exist'); 46 | return; 47 | } 48 | console.log(`The l1 gateway address: ${l1GatewayAddr}`); 49 | 50 | const zkLink = await hre.ethers.getContractAt('DummyZkLink', zkLinkAddr, l2Wallet); 51 | console.log(`Send a l2 message to l1...`); 52 | const l2Tx = await zkLink.syncL2Requests(txs); 53 | const txHash = l2Tx.hash; 54 | console.log(`The l2 tx hash: ${txHash}`); 55 | // const txHash = "0x4948b5b62d415eca82629e9043bbcada07abeabc5f1a91bfbca664ce7bf3e046" 56 | const txHandle = await l2Provider.getTransaction(txHash); 57 | await txHandle.wait(); 58 | 59 | // waiting to finalize can take a few minutes. 60 | await txHandle.waitFinalize(); 61 | 62 | const withdrawalParams = await l2Wallet.finalizeWithdrawalParams(txHash); 63 | console.log('L1 Batch for block :>> ', withdrawalParams.l1BatchNumber); 64 | console.log('L2 message index :>> ', withdrawalParams.l2MessageIndex); 65 | console.log('L1 Index for Tx in block :>> ', withdrawalParams.l2TxNumberInBlock); 66 | console.log('L2 to L1 message :>> ', withdrawalParams.message); 67 | console.log('Proof :>> ', withdrawalParams.proof); 68 | 69 | /** 70 | * Now that its confirmed and not executed, we can execute our message in its outbox entry. 71 | */ 72 | const l1Gateway = await hre.ethers.getContractAt('ZkSyncL1Gateway', l1GatewayAddr, l1Wallet); 73 | const l1Tx = await l1Gateway.finalizeMessage( 74 | withdrawalParams.l1BatchNumber, 75 | withdrawalParams.l2MessageIndex, 76 | withdrawalParams.l2TxNumberInBlock, 77 | withdrawalParams.message, 78 | withdrawalParams.proof, 79 | ); 80 | const l1Receipt = await l1Tx.wait(); 81 | console.log('Done! Your transaction is executed', l1Receipt); 82 | 83 | /** Example Txs 84 | * https://sepolia.explorer.zksync.io/tx/0x4948b5b62d415eca82629e9043bbcada07abeabc5f1a91bfbca664ce7bf3e046 85 | * https://sepolia.etherscan.io/tx/0x61044b2b88c2947010917f7c57b5bd43123bc6824e9a2215e6622d6f88d9320b 86 | */ 87 | }); 88 | -------------------------------------------------------------------------------- /hardhat.base.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type import('hardhat/config').HardhatUserConfig 3 | */ 4 | const hardhatUserConfig = { 5 | solidity: { 6 | compilers: [ 7 | { 8 | version: '0.8.18', 9 | settings: { 10 | viaIR: true, 11 | optimizer: { 12 | enabled: true, 13 | runs: 200, 14 | }, 15 | }, 16 | }, 17 | ], 18 | overrides: { 19 | 'contracts/Arbitrator.sol': { 20 | version: '0.8.25', 21 | settings: { 22 | viaIR: true, 23 | optimizer: { 24 | enabled: true, 25 | runs: 200, 26 | }, 27 | evmVersion: 'cancun', 28 | }, 29 | }, 30 | }, 31 | }, 32 | networks: { 33 | hardhat: { 34 | allowUnlimitedContractSize: true, 35 | }, 36 | }, 37 | gasReporter: { 38 | enabled: !!process.env.REPORT_GAS, 39 | }, 40 | mocha: { 41 | timeout: 600000, 42 | }, 43 | }; 44 | 45 | // custom hardhat user config for different net 46 | if (process.env.NET !== undefined) { 47 | const netName = process.env.NET; 48 | hardhatUserConfig.defaultNetwork = netName; 49 | 50 | const netConfig = require(`./etc/${netName}.json`); 51 | hardhatUserConfig.networks[netName] = netConfig.network; 52 | 53 | // config contract verify key if exist 54 | if (netConfig.etherscan !== undefined) { 55 | hardhatUserConfig.etherscan = netConfig.etherscan; 56 | } 57 | } 58 | 59 | module.exports = hardhatUserConfig; 60 | -------------------------------------------------------------------------------- /hardhat.config.js: -------------------------------------------------------------------------------- 1 | require('@nomicfoundation/hardhat-toolbox'); 2 | require('@openzeppelin/hardhat-upgrades'); 3 | require('./script/deploy_zklink'); 4 | require('./script/deploy_arbitrator'); 5 | require('./script/deploy_l1_gateway'); 6 | require('./script/deploy_l2_gateway'); 7 | require('./script/deploy_eth_gateway'); 8 | require('./script/deploy_erc20_bridge'); 9 | require('./script/deploy_governance'); 10 | require('./script/deploy_linea_l2_governance'); 11 | require('./script/deploy_zklink_token'); 12 | require('./script/deploy_sync_l2_txHash_relayer'); 13 | require('./script/deploy_fastSettlement_network'); 14 | require('./script/deploy_fastSettlement_operator'); 15 | require('./script/deploy_fastSettlement_middleware'); 16 | 17 | const BaseConfig = require('./hardhat.base.config'); 18 | 19 | module.exports = Object.assign({}, BaseConfig, {}); 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zklink-evm-contracts", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "homepage": "https://zk.link", 6 | "keywords": [ 7 | "zklink", 8 | "zkevm-rollup", 9 | "cross chain" 10 | ], 11 | "devDependencies": { 12 | "@nomicfoundation/hardhat-toolbox": "^4.0.0", 13 | "@openzeppelin/hardhat-upgrades": "^3.0.0", 14 | "eslint": "^8.30.0", 15 | "eslint-config-prettier": "^9.0.0", 16 | "hardhat": "^2.19.3", 17 | "prettier": "^3.0.0", 18 | "prettier-plugin-solidity": "^1.1.0", 19 | "solhint": "^4.5.2", 20 | "@eth-optimism/core-utils": "^0.13.1" 21 | }, 22 | "scripts": { 23 | "compile": "npx hardhat compile", 24 | "test": "npx hardhat test", 25 | "lint": "npm run lint:js && npm run lint:sol", 26 | "lint:fix": "npm run lint:js:fix && npm run lint:sol:fix", 27 | "lint:js": "prettier --log-level warn --ignore-path .gitignore '**/*.{js,ts}' --check && eslint --ignore-path .gitignore .", 28 | "lint:js:fix": "prettier --log-level warn --ignore-path .gitignore '**/*.{js,ts}' --write && eslint --ignore-path .gitignore . --fix", 29 | "lint:sol": "prettier --log-level warn --ignore-path .gitignore 'contracts/**/*.sol' --check && solhint 'contracts/**/*.sol'", 30 | "lint:sol:fix": "prettier --log-level warn --ignore-path .gitignore 'contracts/**/*.sol' --write" 31 | }, 32 | "dependencies": { 33 | "@arbitrum/nitro-contracts": "^1.1.0", 34 | "@openzeppelin/contracts": "^4.9.5", 35 | "@openzeppelin/contracts-upgradeable": "^4.9.5", 36 | "@symbioticfi/core": "^1.0.0-devnet.8" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /script/deploy_linea_l2_governance.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const { 3 | verifyContractCode, 4 | getDeployTx, 5 | createOrGetDeployLog, 6 | readDeployContract, 7 | ChainContractDeployer, 8 | } = require('./utils'); 9 | const logName = require('./deploy_log_name'); 10 | const { zkLinkConfig } = require('./zklink_config'); 11 | const { task, types } = require('hardhat/config'); 12 | 13 | task('deployLineaL2Governance', 'Deploy linea l2 governance') 14 | .addParam('skipVerify', 'Skip verify', false, types.boolean, true) 15 | .setAction(async (taskArgs, hardhat) => { 16 | let skipVerify = taskArgs.skipVerify; 17 | console.log('skip verify contracts?', skipVerify); 18 | 19 | const netName = process.env.NET; 20 | if (!netName || !netName.startsWith('LINEA')) { 21 | console.log('LineaL2Governance only can be deployed on linea'); 22 | return; 23 | } 24 | const chainInfo = zkLinkConfig[netName]; 25 | if (chainInfo === undefined) { 26 | console.log('current net not support'); 27 | return; 28 | } 29 | const l2GatewayInfo = chainInfo.l2Gateway; 30 | if (l2GatewayInfo === undefined) { 31 | console.log('l2 gateway config not exist'); 32 | return; 33 | } 34 | const messageServiceAddr = l2GatewayInfo['constructParams'][0]; 35 | console.log('l2 message service address', messageServiceAddr); 36 | const l1GatewayInfo = chainInfo.l1Gateway; 37 | if (l1GatewayInfo === undefined) { 38 | console.log('l1 gateway config not exist'); 39 | return; 40 | } 41 | const l1NetName = l1GatewayInfo.netName; 42 | const l1GovernanceAddr = readDeployContract( 43 | logName.DEPLOY_GOVERNANCE_LOG_PREFIX, 44 | logName.DEPLOY_LOG_GOVERNANCE, 45 | l1NetName, 46 | ); 47 | if (l1GovernanceAddr === undefined) { 48 | console.log('l1 governance not exist'); 49 | return; 50 | } 51 | console.log('l1 governance', l1GovernanceAddr); 52 | 53 | const { deployLogPath, deployLog } = createOrGetDeployLog(logName.DEPLOY_LINEA_L2_GOVERNANCE_LOG_PREFIX); 54 | const contractDeployer = new ChainContractDeployer(hardhat); 55 | await contractDeployer.init(); 56 | 57 | // deploy governance 58 | let governanceAddr; 59 | const allConstructParams = [messageServiceAddr, l1GovernanceAddr]; 60 | if (!(logName.DEPLOY_LOG_GOVERNANCE in deployLog)) { 61 | console.log('deploy governance...'); 62 | const contract = await contractDeployer.deployContract('LineaL2Governance', allConstructParams); 63 | const transaction = await getDeployTx(contract); 64 | governanceAddr = await contract.getAddress(); 65 | deployLog[logName.DEPLOY_LOG_GOVERNANCE] = governanceAddr; 66 | deployLog[logName.DEPLOY_LOG_DEPLOY_TX_HASH] = transaction.hash; 67 | deployLog[logName.DEPLOY_LOG_DEPLOY_BLOCK_NUMBER] = transaction.blockNumber; 68 | fs.writeFileSync(deployLogPath, JSON.stringify(deployLog, null, 2)); 69 | } else { 70 | governanceAddr = deployLog[logName.DEPLOY_LOG_GOVERNANCE]; 71 | } 72 | console.log('linea l2 governance', governanceAddr); 73 | 74 | // verify governance 75 | if (!(logName.DEPLOY_LOG_GOVERNANCE_VERIFIED in deployLog) && !skipVerify) { 76 | await verifyContractCode(hardhat, governanceAddr, allConstructParams); 77 | deployLog[logName.DEPLOY_LOG_GOVERNANCE_VERIFIED] = true; 78 | fs.writeFileSync(deployLogPath, JSON.stringify(deployLog, null, 2)); 79 | } 80 | }); 81 | -------------------------------------------------------------------------------- /script/deploy_sync_l2_txHash_relayer.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const { 3 | verifyContractCode, 4 | createOrGetDeployLog, 5 | ChainContractDeployer, 6 | getDeployTx, 7 | readDeployLogField, 8 | } = require('./utils'); 9 | const logName = require('./deploy_log_name'); 10 | const { task, types } = require('hardhat/config'); 11 | 12 | function getRelayerContractName() { 13 | return 'SyncL2TxHashRelayer'; 14 | } 15 | 16 | task('deploySyncL2TxHashRelayer', 'Deploy SyncL2TxHashRelayer') 17 | .addParam( 18 | 'arbitrator', 19 | 'The arbitrator address (default get from arbitrator deploy log)', 20 | undefined, 21 | types.string, 22 | true, 23 | ) 24 | .addParam('skipVerify', 'Skip verify', false, types.boolean, true) 25 | .setAction(async (taskArgs, hardhat) => { 26 | let arbitrator = taskArgs.arbitrator; 27 | if (arbitrator === undefined) { 28 | arbitrator = readDeployLogField(logName.DEPLOY_ARBITRATOR_LOG_PREFIX, logName.DEPLOY_LOG_ARBITRATOR); 29 | } 30 | let skipVerify = taskArgs.skipVerify; 31 | console.log('arbitrator', arbitrator); 32 | console.log('skip verify contracts?', skipVerify); 33 | 34 | const contractDeployer = new ChainContractDeployer(hardhat); 35 | await contractDeployer.init(); 36 | const deployerWallet = contractDeployer.deployerWallet; 37 | 38 | const { deployLogPath, deployLog } = createOrGetDeployLog(logName.DEPLOY_SYNCL2TXHASHRELAYER_LOG_PREFIX); 39 | deployLog[logName.DEPLOY_LOG_DEPLOYER] = deployerWallet.address; 40 | fs.writeFileSync(deployLogPath, JSON.stringify(deployLog, null, 2)); 41 | 42 | // deploy syncL2TxHashRelayer 43 | let syncL2TxHashRelayerAddr; 44 | if (!(logName.DEPLOY_LOG_SYNCL2TXHASHRELAYER in deployLog)) { 45 | console.log('deploy syncL2TxHashRelayer...'); 46 | const contractName = getRelayerContractName(); 47 | const contract = await contractDeployer.deployContract(contractName, [arbitrator]); 48 | const transaction = await getDeployTx(contract); 49 | syncL2TxHashRelayerAddr = await contract.getAddress(); 50 | deployLog[logName.DEPLOY_LOG_SYNCL2TXHASHRELAYER] = syncL2TxHashRelayerAddr; 51 | deployLog[logName.DEPLOY_LOG_DEPLOY_TX_HASH] = transaction.hash; 52 | deployLog[logName.DEPLOY_LOG_DEPLOY_BLOCK_NUMBER] = transaction.blockNumber; 53 | fs.writeFileSync(deployLogPath, JSON.stringify(deployLog, null, 2)); 54 | } else { 55 | syncL2TxHashRelayerAddr = deployLog[logName.DEPLOY_LOG_SYNCL2TXHASHRELAYER]; 56 | } 57 | console.log('syncL2TxHashRelayer', syncL2TxHashRelayerAddr); 58 | 59 | // verify target contract 60 | if (!(logName.DEPLOY_LOG_SYNCL2TXHASHRELAYER_VERIFIED in deployLog) && !skipVerify) { 61 | await verifyContractCode(hardhat, syncL2TxHashRelayerAddr, [arbitrator]); 62 | deployLog[logName.DEPLOY_LOG_SYNCL2TXHASHRELAYER_VERIFIED] = true; 63 | fs.writeFileSync(deployLogPath, JSON.stringify(deployLog, null, 2)); 64 | } 65 | }); 66 | -------------------------------------------------------------------------------- /script/deploy_zklink_token.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const { getImplementationAddress } = require('@openzeppelin/upgrades-core'); 3 | const { verifyContractCode, createOrGetDeployLog, ChainContractDeployer, getDeployTx } = require('./utils'); 4 | const logName = require('./deploy_log_name'); 5 | const { task, types } = require('hardhat/config'); 6 | 7 | task('deployZkLinkToken', 'Deploy zkLink token') 8 | .addOptionalParam('skipVerify', 'Skip verify', false, types.boolean) 9 | .setAction(async (taskArgs, hardhat) => { 10 | let skipVerify = taskArgs.skipVerify; 11 | console.log('skip verify contracts?', skipVerify); 12 | 13 | const contractDeployer = new ChainContractDeployer(hardhat); 14 | await contractDeployer.init(); 15 | const deployerWallet = contractDeployer.deployerWallet; 16 | 17 | const { deployLogPath, deployLog } = createOrGetDeployLog(logName.DEPLOY_ZKLINK_TOKEN_LOG_PREFIX); 18 | deployLog[logName.DEPLOY_LOG_GOVERNOR] = deployerWallet.address; 19 | fs.writeFileSync(deployLogPath, JSON.stringify(deployLog, null, 2)); 20 | 21 | // deploy zkLink token 22 | let zkLinkTokenAddr; 23 | if (!(logName.DEPLOY_LOG_ZKLINK_TOKEN_PROXY in deployLog)) { 24 | console.log('deploy zkLink token...'); 25 | const contract = await contractDeployer.deployProxy('ZkLinkToken', [], []); 26 | const transaction = await getDeployTx(contract); 27 | zkLinkTokenAddr = await contract.getAddress(); 28 | deployLog[logName.DEPLOY_LOG_ZKLINK_TOKEN_PROXY] = zkLinkTokenAddr; 29 | deployLog[logName.DEPLOY_LOG_DEPLOY_TX_HASH] = transaction.hash; 30 | deployLog[logName.DEPLOY_LOG_DEPLOY_BLOCK_NUMBER] = transaction.blockNumber; 31 | fs.writeFileSync(deployLogPath, JSON.stringify(deployLog, null, 2)); 32 | } else { 33 | zkLinkTokenAddr = deployLog[logName.DEPLOY_LOG_ZKLINK_TOKEN_PROXY]; 34 | } 35 | console.log('zkLinkToken', zkLinkTokenAddr); 36 | 37 | let zkLinkTokenTargetAddr; 38 | if (!(logName.DEPLOY_LOG_ZKLINK_TOKEN_TARGET in deployLog)) { 39 | console.log('get zkLink token target...'); 40 | zkLinkTokenTargetAddr = await getImplementationAddress(hardhat.ethers.provider, zkLinkTokenAddr); 41 | deployLog[logName.DEPLOY_LOG_ZKLINK_TOKEN_TARGET] = zkLinkTokenTargetAddr; 42 | fs.writeFileSync(deployLogPath, JSON.stringify(deployLog, null, 2)); 43 | } else { 44 | zkLinkTokenTargetAddr = deployLog[logName.DEPLOY_LOG_ZKLINK_TOKEN_TARGET]; 45 | } 46 | console.log('zkLink token target', zkLinkTokenTargetAddr); 47 | 48 | // verify target contract 49 | if (!(logName.DEPLOY_LOG_ZKLINK_TOKEN_TARGET_VERIFIED in deployLog) && !skipVerify) { 50 | await verifyContractCode(hardhat, zkLinkTokenTargetAddr, []); 51 | deployLog[logName.DEPLOY_LOG_ZKLINK_TOKEN_TARGET_VERIFIED] = true; 52 | fs.writeFileSync(deployLogPath, JSON.stringify(deployLog, null, 2)); 53 | } 54 | 55 | // verify proxy contract 56 | if (!(logName.DEPLOY_LOG_ZKLINK_TOKEN_PROXY_VERIFIED in deployLog) && !skipVerify) { 57 | await verifyContractCode(hardhat, zkLinkTokenAddr, []); 58 | deployLog[logName.DEPLOY_LOG_ZKLINK_TOKEN_PROXY_VERIFIED] = true; 59 | fs.writeFileSync(deployLogPath, JSON.stringify(deployLog, null, 2)); 60 | } 61 | }); 62 | 63 | task('deployZkLinkTokenTarget', 'Deploy zkLink token target') 64 | .addOptionalParam('skipVerify', 'Skip verify', false, types.boolean) 65 | .setAction(async (taskArgs, hardhat) => { 66 | let skipVerify = taskArgs.skipVerify; 67 | console.log('skip verify contracts?', skipVerify); 68 | 69 | const contractDeployer = new ChainContractDeployer(hardhat); 70 | await contractDeployer.init(); 71 | 72 | // deploy zkLink token target 73 | console.log('deploy zkLink token target...'); 74 | const contract = await contractDeployer.deployContract('ZkLinkToken', [], []); 75 | const zkLinkTokenTargetAddr = await contract.getAddress(); 76 | console.log('zkLinkTokenTarget', zkLinkTokenTargetAddr); 77 | 78 | // verify target contract 79 | if (!skipVerify) { 80 | await verifyContractCode(hardhat, zkLinkTokenTargetAddr, []); 81 | } 82 | }); 83 | -------------------------------------------------------------------------------- /script/zklink_config.js: -------------------------------------------------------------------------------- 1 | // zkLink chain info 2 | const zkLinkConfig = require('./ChainConfig.json'); 3 | 4 | module.exports = { 5 | zkLinkConfig, 6 | }; 7 | -------------------------------------------------------------------------------- /script/zksync_era.js: -------------------------------------------------------------------------------- 1 | const ZKSYNC_HOME = process.env.ZKSYNC_HOME; 2 | if (ZKSYNC_HOME === undefined) { 3 | throw Error('ZKSYNC_HOME not config'); 4 | } 5 | 6 | const SYSTEM_CONFIG_JSON = require(`${ZKSYNC_HOME}/contracts/SystemConfig.json`); 7 | 8 | const SYSTEM_CONFIG = { 9 | requiredL2GasPricePerPubdata: SYSTEM_CONFIG_JSON.REQUIRED_L2_GAS_PRICE_PER_PUBDATA, 10 | priorityTxMinimalGasPrice: SYSTEM_CONFIG_JSON.PRIORITY_TX_MINIMAL_GAS_PRICE, 11 | priorityTxMaxGasPerBatch: SYSTEM_CONFIG_JSON.PRIORITY_TX_MAX_GAS_PER_BATCH, 12 | priorityTxPubdataPerBatch: SYSTEM_CONFIG_JSON.PRIORITY_TX_PUBDATA_PER_BATCH, 13 | priorityTxBatchOverheadL1Gas: SYSTEM_CONFIG_JSON.PRIORITY_TX_BATCH_OVERHEAD_L1_GAS, 14 | priorityTxMaxPubdata: SYSTEM_CONFIG_JSON.PRIORITY_TX_MAX_PUBDATA, 15 | }; 16 | 17 | const INIT_FEE_PARAMS = { 18 | pubdataPricingMode: 0, // rollup 19 | batchOverheadL1Gas: SYSTEM_CONFIG.priorityTxBatchOverheadL1Gas, 20 | maxPubdataPerBatch: SYSTEM_CONFIG.priorityTxPubdataPerBatch, 21 | priorityTxMaxPubdata: SYSTEM_CONFIG.priorityTxMaxPubdata, 22 | maxL2GasPerBatch: SYSTEM_CONFIG.priorityTxMaxGasPerBatch, 23 | minimalL2GasPrice: SYSTEM_CONFIG.priorityTxMinimalGasPrice, 24 | }; 25 | 26 | module.exports = { 27 | INIT_FEE_PARAMS, 28 | }; 29 | -------------------------------------------------------------------------------- /zksync/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | node_modules 4 | 5 | #Hardhat files 6 | cache-zk 7 | artifacts-zk 8 | 9 | # Openzeppelin upgrade 10 | .upgradable 11 | 12 | #Soft links 13 | contracts 14 | script -------------------------------------------------------------------------------- /zksync/hardhat.config.js: -------------------------------------------------------------------------------- 1 | require('@nomicfoundation/hardhat-ethers'); 2 | require('@matterlabs/hardhat-zksync-deploy'); 3 | require('@matterlabs/hardhat-zksync-solc'); 4 | require('@matterlabs/hardhat-zksync-verify'); 5 | require('@matterlabs/hardhat-zksync-upgradable'); 6 | const { TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS } = require('hardhat/builtin-tasks/task-names'); 7 | const path = require('path'); 8 | 9 | const fs = require('fs'); 10 | 11 | if (!fs.existsSync('contracts')) { 12 | // create a soft link 13 | fs.symlinkSync('../contracts', 'contracts'); 14 | console.log('Create contracts soft link success!'); 15 | } 16 | 17 | if (!fs.existsSync('script')) { 18 | // create a soft link 19 | fs.symlinkSync('../script', 'script'); 20 | console.log('Create script soft link success!'); 21 | } 22 | 23 | require('./script/deploy_zklink'); 24 | require('./script/deploy_l2_gateway'); 25 | require('./script/deploy_erc20_bridge'); 26 | 27 | const BaseConfig = require('../hardhat.base.config'); 28 | const { subtask } = require('hardhat/config'); 29 | subtask(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS, async (_, { config }, runSuper) => { 30 | const paths = await runSuper(); 31 | 32 | return paths.filter(solidityFilePath => { 33 | const relativePath = path.relative(config.paths.sources, solidityFilePath); 34 | 35 | return relativePath !== 'Arbitrator.sol'; 36 | }); 37 | }); 38 | 39 | module.exports = Object.assign({}, BaseConfig, { 40 | zksolc: { 41 | version: '1.3.22', 42 | settings: {}, 43 | }, 44 | }); 45 | -------------------------------------------------------------------------------- /zksync/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zklink-contracts", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "homepage": "https://zk.link", 6 | "keywords": [ 7 | "zklink", 8 | "zkevm-rollup", 9 | "cross chain" 10 | ], 11 | "devDependencies": { 12 | "@matterlabs/hardhat-zksync-deploy": "^1.1.1", 13 | "@matterlabs/hardhat-zksync-solc": "^1.0.3", 14 | "@matterlabs/hardhat-zksync-upgradable": "^1.2.1", 15 | "@matterlabs/hardhat-zksync-verify": "^1.2.0", 16 | "@nomicfoundation/hardhat-ethers": "^3.0.5", 17 | "hardhat": "^2.19.3", 18 | "zksync-ethers": "^6.0.0" 19 | }, 20 | "scripts": { 21 | "compile": "npx hardhat compile" 22 | }, 23 | "dependencies": { 24 | "@openzeppelin/contracts": "^4.9.5", 25 | "@openzeppelin/contracts-upgradeable": "^4.9.5" 26 | } 27 | } 28 | --------------------------------------------------------------------------------