From 5663550e0febdc1b246a0646f0b5821bab209079 Mon Sep 17 00:00:00 2001 From: josojo Date: Wed, 5 Jul 2023 11:21:46 +0200 Subject: [PATCH 01/12] Activate ci (#12) --- .github/workflows/main.yml | 55 +++----------------------------------- 1 file changed, 4 insertions(+), 51 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a209c68ab..7ae30d20a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,7 +7,7 @@ on: push: branches: [main, develop] pull_request: - branches: [main, develop] + repository_dispatch: types: [ok-to-test-command] @@ -18,26 +18,17 @@ jobs: strategy: matrix: - node-version: [14.x] + node-version: [16.x] steps: - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - - name: Set int-bot SSH key - run: | - touch /tmp/ssh-key - echo "${{ secrets.INT_BOT_SSH_KEY }}" > /tmp/ssh-key - chmod 400 /tmp/ssh-key - eval "$(ssh-agent -s)" - ssh-add /tmp/ssh-key - name: Checkout code uses: actions/checkout@v2 - name: setup run: | - eval "$(ssh-agent -s)" - ssh-add /tmp/ssh-key npm install -g npm@latest npm i - name: linter @@ -53,60 +44,22 @@ jobs: strategy: matrix: - node-version: [14.x] + node-version: [16.x] steps: - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - - name: Set int-bot SSH key - run: | - touch /tmp/ssh-key - echo "${{ secrets.INT_BOT_SSH_KEY }}" > /tmp/ssh-key - chmod 400 /tmp/ssh-key - eval "$(ssh-agent -s)" - ssh-add /tmp/ssh-key - name: Fork based /ok-to-test checkout uses: actions/checkout@v2 with: ref: 'refs/pull/${{ github.event.client_payload.pull_request.number }}/merge' - name: setup run: | - eval "$(ssh-agent -s)" - ssh-add /tmp/ssh-key npm install -g npm@latest npm i - name: linter run: npm run lint - name: test - run: npm run test - # Update check run - - uses: actions/github-script@v5 - id: update-check-run - if: ${{ always() }} - env: - number: ${{ github.event.client_payload.pull_request.number }} - job: ${{ github.job }} - # Conveniently, job.status maps to https://developer.github.com/v3/checks/runs/#update-a-check-run - conclusion: ${{ job.status }} - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const { data: pull } = await github.rest.pulls.get({ - ...context.repo, - pull_number: process.env.number - }); - const ref = pull.head.sha; - const { data: checks } = await github.rest.checks.listForRef({ - ...context.repo, - ref - }); - const check = checks.check_runs.filter(c => c.name === process.env.job); - const { data: result } = await github.rest.checks.update({ - ...context.repo, - check_run_id: check[0].id, - status: 'completed', - conclusion: process.env.conclusion - }); - return result; + run: npm run test \ No newline at end of file From dfd03be6e09a4b36c1e1ec6ed62a788b75555480 Mon Sep 17 00:00:00 2001 From: josojo Date: Wed, 5 Jul 2023 11:24:48 +0200 Subject: [PATCH 02/12] don't do docker builds on main by default, only on tags (#14) --- .github/workflows/build-docker.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index fecde9c29..da751c05b 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -5,7 +5,10 @@ name: Build-Docker image on: push: - branches: [main] + # Pattern matched against refs/tags + tags: + - '*' + jobs: build: runs-on: ubuntu-latest From 037539866f7ccf0afac661c6219fbd5678141969 Mon Sep 17 00:00:00 2001 From: josojo Date: Wed, 5 Jul 2023 11:29:48 +0200 Subject: [PATCH 03/12] refactor function for bridged token issuing (#13) --- contracts/PolygonZkEVMBridge.sol | 94 ++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 41 deletions(-) diff --git a/contracts/PolygonZkEVMBridge.sol b/contracts/PolygonZkEVMBridge.sol index f6b23e3c3..610a7d72f 100644 --- a/contracts/PolygonZkEVMBridge.sol +++ b/contracts/PolygonZkEVMBridge.sol @@ -355,47 +355,7 @@ contract PolygonZkEVMBridge is ); } else { // The tokens is not from this network - // Create a wrapper for the token if not exist yet - bytes32 tokenInfoHash = keccak256( - abi.encodePacked(originNetwork, originTokenAddress) - ); - address wrappedToken = tokenInfoToWrappedToken[tokenInfoHash]; - - if (wrappedToken == address(0)) { - // Get ERC20 metadata - ( - string memory name, - string memory symbol, - uint8 decimals - ) = abi.decode(metadata, (string, string, uint8)); - - // Create a new wrapped erc20 using create2 - TokenWrapped newWrappedToken = (new TokenWrapped){ - salt: tokenInfoHash - }(name, symbol, decimals); - - // Mint tokens for the destination address - newWrappedToken.mint(destinationAddress, amount); - - // Create mappings - tokenInfoToWrappedToken[tokenInfoHash] = address( - newWrappedToken - ); - - wrappedTokenToTokenInfo[ - address(newWrappedToken) - ] = TokenInformation(originNetwork, originTokenAddress); - - emit NewWrappedToken( - originNetwork, - originTokenAddress, - address(newWrappedToken), - metadata - ); - } else { - // Use the existing wrapped erc20 - TokenWrapped(wrappedToken).mint(destinationAddress, amount); - } + _issueBridgedTokens( originNetwork, originTokenAddress, metadata, destinationAddress, amount); } } @@ -408,6 +368,58 @@ contract PolygonZkEVMBridge is ); } + /** + * @notice Internal function that issues bridged tokens + * @param originNetwork is bridged network origin + * @param originTokenAddress is the address of the token from the bridged network + * @param metadata of transferred token + * @param destinationAddress describes the address where the tokens are issued + * @param amount of tokens to be issued + */ + function _issueBridgedTokens(uint32 originNetwork, address originTokenAddress, bytes memory metadata, address destinationAddress, uint256 amount) internal { + // Create a wrapper for the token if not exist yet + bytes32 tokenInfoHash = keccak256( + abi.encodePacked(originNetwork, originTokenAddress) + ); + address wrappedToken = tokenInfoToWrappedToken[tokenInfoHash]; + + if (wrappedToken == address(0)) { + // Get ERC20 metadata + ( + string memory name, + string memory symbol, + uint8 decimals + ) = abi.decode(metadata, (string, string, uint8)); + + // Create a new wrapped erc20 using create2 + TokenWrapped newWrappedToken = (new TokenWrapped){ + salt: tokenInfoHash + }(name, symbol, decimals); + + // Mint tokens for the destination address + newWrappedToken.mint(destinationAddress, amount); + + // Create mappings + tokenInfoToWrappedToken[tokenInfoHash] = address( + newWrappedToken + ); + + wrappedTokenToTokenInfo[ + address(newWrappedToken) + ] = TokenInformation(originNetwork, originTokenAddress); + + emit NewWrappedToken( + originNetwork, + originTokenAddress, + address(newWrappedToken), + metadata + ); + } else { + // Use the existing wrapped erc20 + TokenWrapped(wrappedToken).mint(destinationAddress, amount); + } + } + /** * @notice Verify merkle proof and execute message * If the receiving address is an EOA, the call will result as a success From ca0991b69a792abfe59c453266cb07e71604959d Mon Sep 17 00:00:00 2001 From: josojo Date: Thu, 6 Jul 2023 12:16:03 +0200 Subject: [PATCH 04/12] Using initializers instead of constructors (#15) --- contracts/PolygonZkEVM.sol | 40 ++++++++--------- contracts/mocks/PolygonZkEVMMock.sol | 24 ----------- .../PolygonZkEVMTestnetClearStorage.sol | 25 ----------- contracts/testnet/PolygonZkEVMTestnetV2.sol | 25 ----------- deployment/3_deployContracts.js | 15 +++---- test/contracts/emergencyManager.test.js | 15 +++---- test/contracts/polygonZkEVM.test.js | 43 ++++++++++++------- test/contracts/polygonZkEVMTestnetV2.test.js | 21 +++++---- test/contracts/snark_stark_input.test.js | 15 +++---- 9 files changed, 75 insertions(+), 148 deletions(-) diff --git a/contracts/PolygonZkEVM.sol b/contracts/PolygonZkEVM.sol index 58bd17f02..416670115 100644 --- a/contracts/PolygonZkEVM.sol +++ b/contracts/PolygonZkEVM.sol @@ -145,22 +145,22 @@ contract PolygonZkEVM is uint256 internal constant _MAX_UINT_64 = type(uint64).max; // 0xFFFFFFFFFFFFFFFF // MATIC token address - IERC20Upgradeable public immutable matic; + IERC20Upgradeable public matic; // Rollup verifier interface - IVerifierRollup public immutable rollupVerifier; + IVerifierRollup public rollupVerifier; // Global Exit Root interface - IPolygonZkEVMGlobalExitRoot public immutable globalExitRootManager; + IPolygonZkEVMGlobalExitRoot public globalExitRootManager; // PolygonZkEVM Bridge Address - IPolygonZkEVMBridge public immutable bridgeAddress; + IPolygonZkEVMBridge public bridgeAddress; // L2 chain identifier - uint64 public immutable chainID; + uint64 public chainID; // L2 chain identifier - uint64 public immutable forkID; + uint64 public forkID; // Time target of the verification of a batch // Adaptatly the batchFee will be updated to achieve this target @@ -368,6 +368,10 @@ contract PolygonZkEVM is event UpdateZkEVMVersion(uint64 numBatch, uint64 forkID, string version); /** + * @param initializePackedParameters Struct to save gas and avoid stack too deep errors + * @param genesisRoot Rollup genesis root + * @param _trustedSequencerURL Trusted sequencer URL + * @param _networkName L2 network name * @param _globalExitRootManager Global exit root manager address * @param _matic MATIC token address * @param _rollupVerifier Rollup verifier address @@ -375,35 +379,25 @@ contract PolygonZkEVM is * @param _chainID L2 chainID * @param _forkID Fork Id */ - constructor( + function initialize( + InitializePackedParameters calldata initializePackedParameters, + bytes32 genesisRoot, + string memory _trustedSequencerURL, + string memory _networkName, + string calldata _version, IPolygonZkEVMGlobalExitRoot _globalExitRootManager, IERC20Upgradeable _matic, IVerifierRollup _rollupVerifier, IPolygonZkEVMBridge _bridgeAddress, uint64 _chainID, uint64 _forkID - ) { + ) external initializer { globalExitRootManager = _globalExitRootManager; matic = _matic; rollupVerifier = _rollupVerifier; bridgeAddress = _bridgeAddress; chainID = _chainID; forkID = _forkID; - } - - /** - * @param initializePackedParameters Struct to save gas and avoid stack too deep errors - * @param genesisRoot Rollup genesis root - * @param _trustedSequencerURL Trusted sequencer URL - * @param _networkName L2 network name - */ - function initialize( - InitializePackedParameters calldata initializePackedParameters, - bytes32 genesisRoot, - string memory _trustedSequencerURL, - string memory _networkName, - string calldata _version - ) external initializer { admin = initializePackedParameters.admin; trustedSequencer = initializePackedParameters.trustedSequencer; trustedAggregator = initializePackedParameters.trustedAggregator; diff --git a/contracts/mocks/PolygonZkEVMMock.sol b/contracts/mocks/PolygonZkEVMMock.sol index 9cde325bf..b31ebfc5f 100644 --- a/contracts/mocks/PolygonZkEVMMock.sol +++ b/contracts/mocks/PolygonZkEVMMock.sol @@ -10,30 +10,6 @@ import "../PolygonZkEVM.sol"; * To enter and exit of the L2 network will be used a PolygonZkEVM Bridge smart contract */ contract PolygonZkEVMMock is PolygonZkEVM { - /** - * @param _globalExitRootManager Global exit root manager address - * @param _matic MATIC token address - * @param _rollupVerifier Rollup verifier address - * @param _bridgeAddress Bridge address - * @param _chainID L2 chainID - */ - constructor( - IPolygonZkEVMGlobalExitRoot _globalExitRootManager, - IERC20Upgradeable _matic, - IVerifierRollup _rollupVerifier, - IPolygonZkEVMBridge _bridgeAddress, - uint64 _chainID, - uint64 _forkID - ) - PolygonZkEVM( - _globalExitRootManager, - _matic, - _rollupVerifier, - _bridgeAddress, - _chainID, - _forkID - ) - {} /** * @notice calculate accumulate input hash from parameters diff --git a/contracts/testnet/PolygonZkEVMTestnetClearStorage.sol b/contracts/testnet/PolygonZkEVMTestnetClearStorage.sol index 27b7661e6..554fee006 100644 --- a/contracts/testnet/PolygonZkEVMTestnetClearStorage.sol +++ b/contracts/testnet/PolygonZkEVMTestnetClearStorage.sol @@ -11,31 +11,6 @@ contract PolygonZkEVMTestnetClearStorage is PolygonZkEVM { // Indicates the current version uint256 public version; - /** - * @param _globalExitRootManager Global exit root manager address - * @param _matic MATIC token address - * @param _rollupVerifier Rollup verifier address - * @param _bridgeAddress Bridge address - * @param _chainID L2 chainID - */ - constructor( - IPolygonZkEVMGlobalExitRoot _globalExitRootManager, - IERC20Upgradeable _matic, - IVerifierRollup _rollupVerifier, - IPolygonZkEVMBridge _bridgeAddress, - uint64 _chainID, - uint64 _forkID - ) - PolygonZkEVM( - _globalExitRootManager, - _matic, - _rollupVerifier, - _bridgeAddress, - _chainID, - _forkID - ) - {} - /** * @dev Thrown when try to update version when it's already updated */ diff --git a/contracts/testnet/PolygonZkEVMTestnetV2.sol b/contracts/testnet/PolygonZkEVMTestnetV2.sol index 95f2430bb..2a3c0fb46 100644 --- a/contracts/testnet/PolygonZkEVMTestnetV2.sol +++ b/contracts/testnet/PolygonZkEVMTestnetV2.sol @@ -11,31 +11,6 @@ contract PolygonZkEVMTestnetV2 is PolygonZkEVM { // Indicates the current version uint256 public version; - /** - * @param _globalExitRootManager Global exit root manager address - * @param _matic MATIC token address - * @param _rollupVerifier Rollup verifier address - * @param _bridgeAddress Bridge address - * @param _chainID L2 chainID - */ - constructor( - IPolygonZkEVMGlobalExitRoot _globalExitRootManager, - IERC20Upgradeable _matic, - IVerifierRollup _rollupVerifier, - IPolygonZkEVMBridge _bridgeAddress, - uint64 _chainID, - uint64 _forkID - ) - PolygonZkEVM( - _globalExitRootManager, - _matic, - _rollupVerifier, - _bridgeAddress, - _chainID, - _forkID - ) - {} - /** * @dev Thrown when try to update version when it's already updated */ diff --git a/deployment/3_deployContracts.js b/deployment/3_deployContracts.js index 4b04425da..16b12b9af 100644 --- a/deployment/3_deployContracts.js +++ b/deployment/3_deployContracts.js @@ -370,16 +370,15 @@ async function main() { trustedSequencerURL, networkName, version, + polygonZkEVMGlobalExitRoot.address, + maticTokenAddress, + verifierContract.address, + polygonZkEVMBridgeContract.address, + chainID, + forkID, ], { - constructorArgs: [ - polygonZkEVMGlobalExitRoot.address, - maticTokenAddress, - verifierContract.address, - polygonZkEVMBridgeContract.address, - chainID, - forkID, - ], + constructorArgs: [], unsafeAllow: ['constructor', 'state-variable-immutable'], }, ); diff --git a/test/contracts/emergencyManager.test.js b/test/contracts/emergencyManager.test.js index 382203310..7ae883d09 100644 --- a/test/contracts/emergencyManager.test.js +++ b/test/contracts/emergencyManager.test.js @@ -81,14 +81,7 @@ describe('Emergency mode test', () => { const PolygonZkEVMFactory = await ethers.getContractFactory('PolygonZkEVMMock'); polygonZkEVMContract = await upgrades.deployProxy(PolygonZkEVMFactory, [], { initializer: false, - constructorArgs: [ - polygonZkEVMGlobalExitRoot.address, - maticTokenContract.address, - verifierContract.address, - polygonZkEVMBridgeContract.address, - chainID, - 0, - ], + constructorArgs: [], unsafeAllow: ['constructor', 'state-variable-immutable'], }); @@ -108,6 +101,12 @@ describe('Emergency mode test', () => { urlSequencer, networkName, version, + polygonZkEVMGlobalExitRoot.address, + maticTokenContract.address, + verifierContract.address, + polygonZkEVMBridgeContract.address, + chainID, + 0, ); // fund sequencer address with Matic tokens diff --git a/test/contracts/polygonZkEVM.test.js b/test/contracts/polygonZkEVM.test.js index 034d04723..b5e87bdb5 100644 --- a/test/contracts/polygonZkEVM.test.js +++ b/test/contracts/polygonZkEVM.test.js @@ -71,7 +71,7 @@ describe('Polygon ZK-EVM', () => { firstDeployment = false; } const nonceProxyBridge = Number((await ethers.provider.getTransactionCount(deployer.address))) + (firstDeployment ? 3 : 2); - const nonceProxyZkevm = nonceProxyBridge + 2; // Always have to redeploy impl since the polygonZkEVMGlobalExitRoot address changes + const nonceProxyZkevm = nonceProxyBridge + (firstDeployment ? 2 : 1); const precalculateBridgeAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyBridge }); const precalculateZkevmAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyZkevm }); @@ -92,17 +92,9 @@ describe('Polygon ZK-EVM', () => { const PolygonZkEVMFactory = await ethers.getContractFactory('PolygonZkEVMMock'); polygonZkEVMContract = await upgrades.deployProxy(PolygonZkEVMFactory, [], { initializer: false, - constructorArgs: [ - polygonZkEVMGlobalExitRoot.address, - maticTokenContract.address, - verifierContract.address, - polygonZkEVMBridgeContract.address, - chainID, - forkID, - ], + constructorArgs: [], unsafeAllow: ['constructor', 'state-variable-immutable'], }); - expect(precalculateBridgeAddress).to.be.equal(polygonZkEVMBridgeContract.address); expect(precalculateZkevmAddress).to.be.equal(polygonZkEVMContract.address); @@ -119,6 +111,12 @@ describe('Polygon ZK-EVM', () => { urlSequencer, networkName, version, + polygonZkEVMGlobalExitRoot.address, + maticTokenContract.address, + verifierContract.address, + polygonZkEVMBridgeContract.address, + chainID, + forkID, ); // fund sequencer address with Matic tokens @@ -156,12 +154,7 @@ describe('Polygon ZK-EVM', () => { const polygonZkEVMContractInitialize = await upgrades.deployProxy(PolygonZkEVMFactory, [], { initializer: false, constructorArgs: [ - polygonZkEVMGlobalExitRoot.address, - maticTokenContract.address, - verifierContract.address, - polygonZkEVMBridgeContract.address, - chainID, - forkID, + ], unsafeAllow: ['constructor', 'state-variable-immutable'], }); @@ -178,6 +171,12 @@ describe('Polygon ZK-EVM', () => { urlSequencer, networkName, version, + polygonZkEVMGlobalExitRoot.address, + maticTokenContract.address, + verifierContract.address, + polygonZkEVMBridgeContract.address, + chainID, + forkID, )).to.be.revertedWith('PendingStateTimeoutExceedHaltAggregationTimeout'); await expect(polygonZkEVMContractInitialize.initialize( @@ -192,6 +191,12 @@ describe('Polygon ZK-EVM', () => { urlSequencer, networkName, version, + polygonZkEVMGlobalExitRoot.address, + maticTokenContract.address, + verifierContract.address, + polygonZkEVMBridgeContract.address, + chainID, + forkID, )).to.be.revertedWith('TrustedAggregatorTimeoutExceedHaltAggregationTimeout'); await expect( @@ -207,6 +212,12 @@ describe('Polygon ZK-EVM', () => { urlSequencer, networkName, version, + polygonZkEVMGlobalExitRoot.address, + maticTokenContract.address, + verifierContract.address, + polygonZkEVMBridgeContract.address, + chainID, + forkID, ), ).to.emit(polygonZkEVMContractInitialize, 'UpdateZkEVMVersion').withArgs(0, forkID, version); }); diff --git a/test/contracts/polygonZkEVMTestnetV2.test.js b/test/contracts/polygonZkEVMTestnetV2.test.js index 508da7981..35d157317 100644 --- a/test/contracts/polygonZkEVMTestnetV2.test.js +++ b/test/contracts/polygonZkEVMTestnetV2.test.js @@ -29,6 +29,7 @@ describe('Polygon ZK-EVM TestnetV2', () => { const pendingStateTimeoutDefault = 100; const trustedAggregatorTimeoutDefault = 10; let firstDeployment = true; + let doesNeedImplementationDeployment = true; beforeEach('Deploy contract', async () => { upgrades.silenceWarnings(); @@ -61,12 +62,11 @@ describe('Polygon ZK-EVM TestnetV2', () => { firstDeployment = false; } const nonceProxyBridge = Number((await ethers.provider.getTransactionCount(deployer.address))) + (firstDeployment ? 3 : 2); - const nonceProxyZkevm = nonceProxyBridge + 2; // Always have to redeploy impl since the polygonZkEVMGlobalExitRoot address changes - + const nonceProxyZkevm = nonceProxyBridge + (doesNeedImplementationDeployment ? 2 : 1); const precalculateBridgeAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyBridge }); const precalculateZkevmAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyZkevm }); firstDeployment = false; - + doesNeedImplementationDeployment = false; const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRoot'); polygonZkEVMGlobalExitRoot = await upgrades.deployProxy(PolygonZkEVMGlobalExitRootFactory, [], { initializer: false, @@ -82,14 +82,7 @@ describe('Polygon ZK-EVM TestnetV2', () => { const PolygonZkEVMFactory = await ethers.getContractFactory('PolygonZkEVMTestnetV2'); polygonZkEVMContract = await upgrades.deployProxy(PolygonZkEVMFactory, [], { initializer: false, - constructorArgs: [ - polygonZkEVMGlobalExitRoot.address, - maticTokenContract.address, - verifierContract.address, - polygonZkEVMBridgeContract.address, - chainID, - forkID, - ], + constructorArgs: [], unsafeAllow: ['constructor', 'state-variable-immutable'], }); @@ -109,6 +102,12 @@ describe('Polygon ZK-EVM TestnetV2', () => { urlSequencer, networkName, version, + polygonZkEVMGlobalExitRoot.address, + maticTokenContract.address, + verifierContract.address, + polygonZkEVMBridgeContract.address, + chainID, + forkID, ); // fund sequencer address with Matic tokens diff --git a/test/contracts/snark_stark_input.test.js b/test/contracts/snark_stark_input.test.js index 47c404673..1c1aed626 100644 --- a/test/contracts/snark_stark_input.test.js +++ b/test/contracts/snark_stark_input.test.js @@ -23,14 +23,7 @@ describe('Polygon ZK-EVM snark stark input test', () => { const PolygonZkEVMFactory = await ethers.getContractFactory('PolygonZkEVMMock'); polygonZkEVMContract = await upgrades.deployProxy(PolygonZkEVMFactory, [], { initializer: false, - constructorArgs: [ - randomSigner.address, - randomSigner.address, - randomSigner.address, - randomSigner.address, - chainID, - 0, - ], + constructorArgs: [], unsafeAllow: ['constructor', 'state-variable-immutable'], }); @@ -46,6 +39,12 @@ describe('Polygon ZK-EVM snark stark input test', () => { urlSequencer, networkName, version, + randomSigner.address, + randomSigner.address, + randomSigner.address, + randomSigner.address, + chainID, + 0, ); await polygonZkEVMContract.deployed(); From a9ceb597c5fea7327704292ec6a4ee1577b1f702 Mon Sep 17 00:00:00 2001 From: josojo Date: Thu, 6 Jul 2023 12:27:19 +0200 Subject: [PATCH 05/12] New interface definition for zkEVM (#16) --- contracts/PolygonZkEVM.sol | 22 +++----------- contracts/interfaces/IPolygonZkEVM.sol | 42 ++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 18 deletions(-) create mode 100644 contracts/interfaces/IPolygonZkEVM.sol diff --git a/contracts/PolygonZkEVM.sol b/contracts/PolygonZkEVM.sol index 416670115..6fbf33856 100644 --- a/contracts/PolygonZkEVM.sol +++ b/contracts/PolygonZkEVM.sol @@ -5,9 +5,10 @@ import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeab import "./interfaces/IVerifierRollup.sol"; import "./interfaces/IPolygonZkEVMGlobalExitRoot.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; -import "./interfaces/IPolygonZkEVMBridge.sol"; import "./lib/EmergencyManager.sol"; import "./interfaces/IPolygonZkEVMErrors.sol"; +import "./interfaces/IPolygonZkEVM.sol"; +import "./interfaces/IPolygonZkEVMBridge.sol"; /** * Contract responsible for managing the states and the updates of L2 network. @@ -20,7 +21,8 @@ import "./interfaces/IPolygonZkEVMErrors.sol"; contract PolygonZkEVM is OwnableUpgradeable, EmergencyManager, - IPolygonZkEVMErrors + IPolygonZkEVMErrors, + IPolygonZkEVM { using SafeERC20Upgradeable for IERC20Upgradeable; @@ -84,22 +86,6 @@ contract PolygonZkEVM is bytes32 stateRoot; } - /** - * @notice Struct to call initialize, this saves gas because pack the parameters and avoid stack too deep errors. - * @param admin Admin address - * @param trustedSequencer Trusted sequencer address - * @param pendingStateTimeout Pending state timeout - * @param trustedAggregator Trusted aggregator - * @param trustedAggregatorTimeout Trusted aggregator timeout - */ - struct InitializePackedParameters { - address admin; - address trustedSequencer; - uint64 pendingStateTimeout; - address trustedAggregator; - uint64 trustedAggregatorTimeout; - } - // Modulus zkSNARK uint256 internal constant _RFIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; diff --git a/contracts/interfaces/IPolygonZkEVM.sol b/contracts/interfaces/IPolygonZkEVM.sol new file mode 100644 index 000000000..6ce02d880 --- /dev/null +++ b/contracts/interfaces/IPolygonZkEVM.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.17; + +import "./IPolygonZkEVMGlobalExitRoot.sol"; +import "./IVerifierRollup.sol"; + +interface IPolygonZkEVM { + + /** + * @notice Struct to call initialize, this saves gas because pack the parameters and avoid stack too deep errors. + * @param admin Admin address + * @param trustedSequencer Trusted sequencer address + * @param pendingStateTimeout Pending state timeout + * @param trustedAggregator Trusted aggregator + * @param trustedAggregatorTimeout Trusted aggregator timeout + */ + struct InitializePackedParameters { + address admin; + address trustedSequencer; + uint64 pendingStateTimeout; + address trustedAggregator; + uint64 trustedAggregatorTimeout; + } + + function chainID() external view returns (uint64); + function forkID() external view returns (uint64); + function globalExitRootManager() external view returns (IPolygonZkEVMGlobalExitRoot); + function rollupVerifier() external view returns (IVerifierRollup); + function verifyBatchTimeTarget() external view returns (uint64); + function multiplierBatchFee() external view returns (uint16); + function batchFee() external view returns (uint256); + function forceBatchTimeout() external view returns (uint64); + function lastVerifiedBatch() external view returns (uint64); + function batchNumToStateRoot(uint64) external view returns (bytes32); + function trustedSequencerURL() external view returns (string calldata); + function networkName() external view returns(string calldata); + function admin() external view returns(address); + function trustedSequencer() external view returns(address); + function pendingStateTimeout() external view returns(uint64); + function trustedAggregator() external view returns(address); + function trustedAggregatorTimeout() external view returns(uint64); +} \ No newline at end of file From fa7c8f7a804ee9c97d5dcefef1c20ace84b34c1d Mon Sep 17 00:00:00 2001 From: josojo Date: Thu, 6 Jul 2023 12:38:59 +0200 Subject: [PATCH 06/12] initializer -> OnlyInitializing (#17) --- contracts/PolygonZkEVMBridgeWrapper.sol | 14 ++++++++ contracts/PolygonZkEVMTimelock.sol | 2 +- contracts/PolygonZkEVMWrapper.sol | 34 +++++++++++++++++++ .../PolygonZkEVM.sol | 14 ++++---- .../PolygonZkEVMBridge.sol | 16 ++++----- contracts/mocks/PolygonZkEVMBridgeMock.sol | 4 +-- contracts/mocks/PolygonZkEVMMock.sol | 4 +-- .../PolygonZkEVMTestnetClearStorage.sol | 2 +- contracts/testnet/PolygonZkEVMTestnetV2.sol | 4 +-- deployment/1_createGenesis.js | 2 +- deployment/3_deployContracts.js | 4 +-- test/contracts/bridge.test.js | 2 +- test/contracts/bridge_metadata.test.js | 2 +- test/contracts/bridge_permit.test.js | 2 +- test/contracts/emergencyManager.test.js | 2 +- test/contracts/polygonZkEVM.test.js | 2 +- test/contracts/polygonZkEVMTestnetV2.test.js | 2 +- test/contracts/real-prover/real-flow.test.js | 2 +- test/contracts/timelockUpgradeTest.js | 2 +- 19 files changed, 82 insertions(+), 34 deletions(-) create mode 100644 contracts/PolygonZkEVMBridgeWrapper.sol create mode 100644 contracts/PolygonZkEVMWrapper.sol rename contracts/{ => inheritedMainContracts}/PolygonZkEVM.sol (99%) rename contracts/{ => inheritedMainContracts}/PolygonZkEVMBridge.sol (98%) diff --git a/contracts/PolygonZkEVMBridgeWrapper.sol b/contracts/PolygonZkEVMBridgeWrapper.sol new file mode 100644 index 000000000..fe5eab487 --- /dev/null +++ b/contracts/PolygonZkEVMBridgeWrapper.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity 0.8.17; + +import "./inheritedMainContracts/PolygonZkEVMBridge.sol"; + +contract PolygonZkEVMBridgeWrapper is PolygonZkEVMBridge{ + function initialize( + uint32 _networkID, + IBasePolygonZkEVMGlobalExitRoot _globalExitRootManager, + address _polygonZkEVMaddress + ) public virtual override initializer { + PolygonZkEVMBridge.initialize(_networkID, _globalExitRootManager, _polygonZkEVMaddress); + } +} \ No newline at end of file diff --git a/contracts/PolygonZkEVMTimelock.sol b/contracts/PolygonZkEVMTimelock.sol index 3dd415e72..09e986312 100644 --- a/contracts/PolygonZkEVMTimelock.sol +++ b/contracts/PolygonZkEVMTimelock.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.17; import "@openzeppelin/contracts/governance/TimelockController.sol"; -import "./PolygonZkEVM.sol"; +import "./inheritedMainContracts/PolygonZkEVM.sol"; /** * @dev Contract module which acts as a timelocked controller. diff --git a/contracts/PolygonZkEVMWrapper.sol b/contracts/PolygonZkEVMWrapper.sol new file mode 100644 index 000000000..b3255f769 --- /dev/null +++ b/contracts/PolygonZkEVMWrapper.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity 0.8.17; + +import "./inheritedMainContracts/PolygonZkEVM.sol"; + +contract PolygonZkEVMWrapper is PolygonZkEVM{ + function initialize( + InitializePackedParameters calldata initializePackedParameters, + bytes32 genesisRoot, + string memory _trustedSequencerURL, + string memory _networkName, + string calldata _version, + IPolygonZkEVMGlobalExitRoot _globalExitRootManager, + IERC20Upgradeable _matic, + IVerifierRollup _rollupVerifier, + IPolygonZkEVMBridge _bridgeAddress, + uint64 _chainID, + uint64 _forkID + ) public override initializer { + PolygonZkEVM.initialize( + initializePackedParameters, + genesisRoot, + _trustedSequencerURL, + _networkName, + _version, + _globalExitRootManager, + _matic, + _rollupVerifier, + _bridgeAddress, + _chainID, + _forkID + ); + } +} \ No newline at end of file diff --git a/contracts/PolygonZkEVM.sol b/contracts/inheritedMainContracts/PolygonZkEVM.sol similarity index 99% rename from contracts/PolygonZkEVM.sol rename to contracts/inheritedMainContracts/PolygonZkEVM.sol index 6fbf33856..f1dab0400 100644 --- a/contracts/PolygonZkEVM.sol +++ b/contracts/inheritedMainContracts/PolygonZkEVM.sol @@ -2,13 +2,13 @@ pragma solidity 0.8.17; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; -import "./interfaces/IVerifierRollup.sol"; -import "./interfaces/IPolygonZkEVMGlobalExitRoot.sol"; +import "../interfaces/IVerifierRollup.sol"; +import "../interfaces/IPolygonZkEVMGlobalExitRoot.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; -import "./lib/EmergencyManager.sol"; -import "./interfaces/IPolygonZkEVMErrors.sol"; -import "./interfaces/IPolygonZkEVM.sol"; -import "./interfaces/IPolygonZkEVMBridge.sol"; +import "../lib/EmergencyManager.sol"; +import "../interfaces/IPolygonZkEVMErrors.sol"; +import "../interfaces/IPolygonZkEVM.sol"; +import "../interfaces/IPolygonZkEVMBridge.sol"; /** * Contract responsible for managing the states and the updates of L2 network. @@ -377,7 +377,7 @@ contract PolygonZkEVM is IPolygonZkEVMBridge _bridgeAddress, uint64 _chainID, uint64 _forkID - ) external initializer { + ) public virtual onlyInitializing { globalExitRootManager = _globalExitRootManager; matic = _matic; rollupVerifier = _rollupVerifier; diff --git a/contracts/PolygonZkEVMBridge.sol b/contracts/inheritedMainContracts/PolygonZkEVMBridge.sol similarity index 98% rename from contracts/PolygonZkEVMBridge.sol rename to contracts/inheritedMainContracts/PolygonZkEVMBridge.sol index 610a7d72f..9c1c3f8ec 100644 --- a/contracts/PolygonZkEVMBridge.sol +++ b/contracts/inheritedMainContracts/PolygonZkEVMBridge.sol @@ -2,15 +2,15 @@ pragma solidity 0.8.17; -import "./lib/DepositContract.sol"; +import "../lib/DepositContract.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; -import "./lib/TokenWrapped.sol"; -import "./interfaces/IBasePolygonZkEVMGlobalExitRoot.sol"; -import "./interfaces/IBridgeMessageReceiver.sol"; -import "./interfaces/IPolygonZkEVMBridge.sol"; +import "../lib/TokenWrapped.sol"; +import "../interfaces/IBasePolygonZkEVMGlobalExitRoot.sol"; +import "../interfaces/IBridgeMessageReceiver.sol"; +import "../interfaces/IPolygonZkEVMBridge.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol"; -import "./lib/EmergencyManager.sol"; -import "./lib/GlobalExitRootLib.sol"; +import "../lib/EmergencyManager.sol"; +import "../lib/GlobalExitRootLib.sol"; /** * PolygonZkEVMBridge that will be deployed on both networks Ethereum and Polygon zkEVM @@ -79,7 +79,7 @@ contract PolygonZkEVMBridge is uint32 _networkID, IBasePolygonZkEVMGlobalExitRoot _globalExitRootManager, address _polygonZkEVMaddress - ) external virtual initializer { + ) public virtual onlyInitializing { networkID = _networkID; globalExitRootManager = _globalExitRootManager; polygonZkEVMaddress = _polygonZkEVMaddress; diff --git a/contracts/mocks/PolygonZkEVMBridgeMock.sol b/contracts/mocks/PolygonZkEVMBridgeMock.sol index 8123bfa50..b77689ea4 100644 --- a/contracts/mocks/PolygonZkEVMBridgeMock.sol +++ b/contracts/mocks/PolygonZkEVMBridgeMock.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.8.17; -import "../PolygonZkEVMBridge.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import "../PolygonZkEVMBridgeWrapper.sol"; /** * PolygonZkEVMBridge that will be deployed on both networks Ethereum and Polygon zkEVM * Contract responsible to manage the token interactions with other networks */ -contract PolygonZkEVMBridgeMock is PolygonZkEVMBridge, OwnableUpgradeable { +contract PolygonZkEVMBridgeMock is PolygonZkEVMBridgeWrapper, OwnableUpgradeable { uint256 public maxEtherBridge; /** diff --git a/contracts/mocks/PolygonZkEVMMock.sol b/contracts/mocks/PolygonZkEVMMock.sol index b31ebfc5f..7230181cf 100644 --- a/contracts/mocks/PolygonZkEVMMock.sol +++ b/contracts/mocks/PolygonZkEVMMock.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.8.17; -import "../PolygonZkEVM.sol"; +import "../PolygonZkEVMWrapper.sol"; /** * Contract responsible for managing the state and the updates of the L2 network @@ -9,7 +9,7 @@ import "../PolygonZkEVM.sol"; * The aggregators are forced to process and validate the sequencers transactions in the same order by using a verifier. * To enter and exit of the L2 network will be used a PolygonZkEVM Bridge smart contract */ -contract PolygonZkEVMMock is PolygonZkEVM { +contract PolygonZkEVMMock is PolygonZkEVMWrapper { /** * @notice calculate accumulate input hash from parameters diff --git a/contracts/testnet/PolygonZkEVMTestnetClearStorage.sol b/contracts/testnet/PolygonZkEVMTestnetClearStorage.sol index 554fee006..6fe3dbd50 100644 --- a/contracts/testnet/PolygonZkEVMTestnetClearStorage.sol +++ b/contracts/testnet/PolygonZkEVMTestnetClearStorage.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.8.17; -import "../PolygonZkEVM.sol"; +import "../inheritedMainContracts/PolygonZkEVM.sol"; /** * Contract responsible for managing the state and the updates of the L2 network diff --git a/contracts/testnet/PolygonZkEVMTestnetV2.sol b/contracts/testnet/PolygonZkEVMTestnetV2.sol index 2a3c0fb46..cd0f66d06 100644 --- a/contracts/testnet/PolygonZkEVMTestnetV2.sol +++ b/contracts/testnet/PolygonZkEVMTestnetV2.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.8.17; -import "../PolygonZkEVM.sol"; +import "../PolygonZkEVMWrapper.sol"; /** * Contract responsible for managing the state and the updates of the L2 network * This contract will NOT BE USED IN PRODUCTION, will be used only in testnet enviroment */ -contract PolygonZkEVMTestnetV2 is PolygonZkEVM { +contract PolygonZkEVMTestnetV2 is PolygonZkEVMWrapper { // Indicates the current version uint256 public version; diff --git a/deployment/1_createGenesis.js b/deployment/1_createGenesis.js index 3104293b2..cbb641ab0 100644 --- a/deployment/1_createGenesis.js +++ b/deployment/1_createGenesis.js @@ -77,7 +77,7 @@ async function main() { const [proxyAdminAddress] = await create2Deployment(zkEVMDeployerContract, salt, deployTransactionAdmin, dataCallAdmin, deployer); // Deploy implementation PolygonZkEVMBridg - const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridge', deployer); + const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridgeWrapper', deployer); const deployTransactionBridge = (polygonZkEVMBridgeFactory.getDeployTransaction()).data; // Mandatory to override the gasLimit since the estimation with create are mess up D: const overrideGasLimit = ethers.BigNumber.from(5500000); diff --git a/deployment/3_deployContracts.js b/deployment/3_deployContracts.js index 16b12b9af..a722ec9a9 100644 --- a/deployment/3_deployContracts.js +++ b/deployment/3_deployContracts.js @@ -179,7 +179,7 @@ async function main() { } // Deploy implementation PolygonZkEVMBridge - const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridge', deployer); + const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridgeWrapper', deployer); const deployTransactionBridge = (polygonZkEVMBridgeFactory.getDeployTransaction()).data; const dataCallNull = null; // Mandatory to override the gasLimit since the estimation with create are mess up D: @@ -349,7 +349,7 @@ async function main() { console.log('networkName:', networkName); console.log('forkID:', forkID); - const PolygonZkEVMFactory = await ethers.getContractFactory('PolygonZkEVM', deployer); + const PolygonZkEVMFactory = await ethers.getContractFactory('PolygonZkEVMWrapper', deployer); let polygonZkEVMContract; let deploymentBlockNumber; diff --git a/test/contracts/bridge.test.js b/test/contracts/bridge.test.js index a2bbc84a4..15d76a2f5 100644 --- a/test/contracts/bridge.test.js +++ b/test/contracts/bridge.test.js @@ -41,7 +41,7 @@ describe('PolygonZkEVMBridge Contract', () => { [deployer, rollup, acc1] = await ethers.getSigners(); // deploy PolygonZkEVMBridge - const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridge'); + const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridgeWrapper'); polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { initializer: false }); // deploy global exit root manager diff --git a/test/contracts/bridge_metadata.test.js b/test/contracts/bridge_metadata.test.js index f6c8b34df..93c945e48 100644 --- a/test/contracts/bridge_metadata.test.js +++ b/test/contracts/bridge_metadata.test.js @@ -29,7 +29,7 @@ describe('PolygonZkEVMBridge Contract werid metadata', () => { [deployer, rollup] = await ethers.getSigners(); // deploy PolygonZkEVMBridge - const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridge'); + const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridgeWrapper'); polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { initializer: false }); // deploy global exit root manager diff --git a/test/contracts/bridge_permit.test.js b/test/contracts/bridge_permit.test.js index e5d4f3f39..fe0bbf015 100644 --- a/test/contracts/bridge_permit.test.js +++ b/test/contracts/bridge_permit.test.js @@ -46,7 +46,7 @@ describe('PolygonZkEVMBridge Contract Permit tests', () => { [deployer, rollup] = await ethers.getSigners(); // deploy PolygonZkEVMBridge - const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridge'); + const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridgeWrapper'); polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { initializer: false }); // deploy global exit root manager diff --git a/test/contracts/emergencyManager.test.js b/test/contracts/emergencyManager.test.js index 7ae883d09..57f1d2bde 100644 --- a/test/contracts/emergencyManager.test.js +++ b/test/contracts/emergencyManager.test.js @@ -74,7 +74,7 @@ describe('Emergency mode test', () => { }); // deploy PolygonZkEVMBridge - const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridge'); + const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridgeWrapper'); polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { initializer: false }); // deploy PolygonZkEVMMock diff --git a/test/contracts/polygonZkEVM.test.js b/test/contracts/polygonZkEVM.test.js index b5e87bdb5..34410a91e 100644 --- a/test/contracts/polygonZkEVM.test.js +++ b/test/contracts/polygonZkEVM.test.js @@ -85,7 +85,7 @@ describe('Polygon ZK-EVM', () => { }); // deploy PolygonZkEVMBridge - const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridge'); + const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridgeWrapper'); polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { initializer: false }); // deploy PolygonZkEVMMock diff --git a/test/contracts/polygonZkEVMTestnetV2.test.js b/test/contracts/polygonZkEVMTestnetV2.test.js index 35d157317..d9e100ef4 100644 --- a/test/contracts/polygonZkEVMTestnetV2.test.js +++ b/test/contracts/polygonZkEVMTestnetV2.test.js @@ -75,7 +75,7 @@ describe('Polygon ZK-EVM TestnetV2', () => { }); // deploy PolygonZkEVMBridge - const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridge'); + const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridgeWrapper'); polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { initializer: false }); // deploy PolygonZkEVMTestnet diff --git a/test/contracts/real-prover/real-flow.test.js b/test/contracts/real-prover/real-flow.test.js index 1760b43f4..97e8cbdd4 100644 --- a/test/contracts/real-prover/real-flow.test.js +++ b/test/contracts/real-prover/real-flow.test.js @@ -97,7 +97,7 @@ describe('Real flow test', () => { }); // deploy PolygonZkEVMBridge - const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridge'); + const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridgeWrapper'); polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { initializer: false }); // deploy PolygonZkEVMMock diff --git a/test/contracts/timelockUpgradeTest.js b/test/contracts/timelockUpgradeTest.js index 752391685..9006ab434 100644 --- a/test/contracts/timelockUpgradeTest.js +++ b/test/contracts/timelockUpgradeTest.js @@ -97,7 +97,7 @@ describe('Polygon ZK-EVM', () => { }); // deploy PolygonZkEVMBridge - const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridge'); + const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridgeWrapper'); polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { initializer: false }); // deploy PolygonZkEVMMock From be0d8f676550963d86361ddb6716119c91f8b755 Mon Sep 17 00:00:00 2001 From: josojo Date: Fri, 7 Jul 2023 08:58:55 +0200 Subject: [PATCH 07/12] Allowing to specify another gas token than eth (#18) --- contracts/PolygonZkEVMBridgeWrapper.sol | 6 +- .../PolygonZkEVMBridge.sol | 45 +- contracts/mocks/PolygonZkEVMBridgeMock.sol | 6 +- test/contracts/bridge.test.js | 466 ++++++++++++------ test/contracts/bridgeMock.test.js | 22 +- test/contracts/bridge_metadata.test.js | 21 +- test/contracts/bridge_permit.test.js | 22 +- test/contracts/emergencyManager.test.js | 25 +- test/contracts/polygonZkEVM.test.js | 27 +- test/contracts/polygonZkEVMTestnetV2.test.js | 27 +- 10 files changed, 494 insertions(+), 173 deletions(-) diff --git a/contracts/PolygonZkEVMBridgeWrapper.sol b/contracts/PolygonZkEVMBridgeWrapper.sol index fe5eab487..c1ac52599 100644 --- a/contracts/PolygonZkEVMBridgeWrapper.sol +++ b/contracts/PolygonZkEVMBridgeWrapper.sol @@ -7,8 +7,10 @@ contract PolygonZkEVMBridgeWrapper is PolygonZkEVMBridge{ function initialize( uint32 _networkID, IBasePolygonZkEVMGlobalExitRoot _globalExitRootManager, - address _polygonZkEVMaddress + address _polygonZkEVMaddress, + address _gasTokenAddress, + bool _isDeployedOnL2 ) public virtual override initializer { - PolygonZkEVMBridge.initialize(_networkID, _globalExitRootManager, _polygonZkEVMaddress); + PolygonZkEVMBridge.initialize(_networkID, _globalExitRootManager, _polygonZkEVMaddress, _gasTokenAddress, _isDeployedOnL2); } } \ No newline at end of file diff --git a/contracts/inheritedMainContracts/PolygonZkEVMBridge.sol b/contracts/inheritedMainContracts/PolygonZkEVMBridge.sol index 9c1c3f8ec..80a5b3517 100644 --- a/contracts/inheritedMainContracts/PolygonZkEVMBridge.sol +++ b/contracts/inheritedMainContracts/PolygonZkEVMBridge.sol @@ -47,6 +47,17 @@ contract PolygonZkEVMBridge is // Leaf type message uint8 private constant _LEAF_TYPE_MESSAGE = 1; + // gas token address on L1 for L2 + address public gasTokenAddress; + + // flag to indicate if the contract is deployed on L2 + // This is needed, as the logic for the L2 and L1 diverges + // On L1: We allow deposits in gasTokenAddress that will be used to + // represent the native token (ether) in L2 + // On L2: We pay out the native token (ether) and allow deposits + // in the native token (ether) + bool public isDeployedOnL2; + // Network identifier uint32 public networkID; @@ -78,11 +89,15 @@ contract PolygonZkEVMBridge is function initialize( uint32 _networkID, IBasePolygonZkEVMGlobalExitRoot _globalExitRootManager, - address _polygonZkEVMaddress + address _polygonZkEVMaddress, + address _gasTokenAddress, + bool _isDeployedOnL2 ) public virtual onlyInitializing { networkID = _networkID; globalExitRootManager = _globalExitRootManager; polygonZkEVMaddress = _polygonZkEVMaddress; + gasTokenAddress = _gasTokenAddress; + isDeployedOnL2 = _isDeployedOnL2; // Initialize OZ contracts __ReentrancyGuard_init(); @@ -159,16 +174,18 @@ contract PolygonZkEVMBridge is bytes memory metadata; uint256 leafAmount = amount; - if (token == address(0)) { + if (token == address(0) && isDeployedOnL2) { // Ether transfer if (msg.value != amount) { revert AmountDoesNotMatchMsgValue(); } + // We use originTokenAddress = address(0) to indicate an native token deposit + // originTokenAddress = address(0) is set by initialization of originTokenAddress // Ether is treated as ether from mainnet originNetwork = _MAINNET_NETWORK_ID; } else { - // Check msg.value is 0 if tokens are bridged + // Check msg.value is 0 if tokens are bridged or the contract is deployed on L1 if (msg.value != 0) { revert MsgValueNotZero(); } @@ -205,7 +222,12 @@ contract PolygonZkEVMBridge is // Override leafAmount with the received amount leafAmount = balanceAfter - balanceBefore; - originTokenAddress = token; + if (token == gasTokenAddress) { + // deposits of the gas tokens will be represented as the native token on L2 + originTokenAddress = address(0); + } else { + originTokenAddress = token; + } originNetwork = networkID; // Encode metadata @@ -259,6 +281,9 @@ contract PolygonZkEVMBridge is bool forceUpdateGlobalExitRoot, bytes calldata metadata ) external payable ifNotEmergencyState { + if (msg.value != 0 && !isDeployedOnL2) { + revert MsgValueNotZero(); + } if ( destinationNetwork == networkID || destinationNetwork >= _CURRENT_SUPPORTED_NETWORKS @@ -336,8 +361,8 @@ contract PolygonZkEVMBridge is ); // Transfer funds - if (originTokenAddress == address(0)) { - // Transfer ether + if (originTokenAddress == address(0) && isDeployedOnL2) { + // Transfer gasTokenAddress as native asset /* solhint-disable avoid-low-level-calls */ (bool success, ) = destinationAddress.call{value: amount}( new bytes(0) @@ -347,7 +372,13 @@ contract PolygonZkEVMBridge is } } else { // Transfer tokens - if (originNetwork == networkID) { + if(originTokenAddress == address(0) && !isDeployedOnL2) { + // The was deposited token was the native token on L2 + IERC20Upgradeable(gasTokenAddress).safeTransfer( + destinationAddress, + amount + ); + } else if (originNetwork == networkID) { // The token is an ERC20 from this network IERC20Upgradeable(originTokenAddress).safeTransfer( destinationAddress, diff --git a/contracts/mocks/PolygonZkEVMBridgeMock.sol b/contracts/mocks/PolygonZkEVMBridgeMock.sol index b77689ea4..52a540650 100644 --- a/contracts/mocks/PolygonZkEVMBridgeMock.sol +++ b/contracts/mocks/PolygonZkEVMBridgeMock.sol @@ -17,11 +17,15 @@ contract PolygonZkEVMBridgeMock is PolygonZkEVMBridgeWrapper, OwnableUpgradeable function initialize( uint32 _networkID, IBasePolygonZkEVMGlobalExitRoot _globalExitRootManager, - address _polygonZkEVMaddress + address _polygonZkEVMaddress, + address _gasTokenAddress, + bool _isDeployedOnL2 ) public override initializer { networkID = _networkID; globalExitRootManager = _globalExitRootManager; polygonZkEVMaddress = _polygonZkEVMaddress; + gasTokenAddress = _gasTokenAddress; + isDeployedOnL2= _isDeployedOnL2; maxEtherBridge = 0.25 ether; diff --git a/test/contracts/bridge.test.js b/test/contracts/bridge.test.js index 15d76a2f5..1d0eac262 100644 --- a/test/contracts/bridge.test.js +++ b/test/contracts/bridge.test.js @@ -10,35 +10,51 @@ function calculateGlobalExitRoot(mainnetExitRoot, rollupExitRoot) { return ethers.utils.solidityKeccak256(['bytes32', 'bytes32'], [mainnetExitRoot, rollupExitRoot]); } -describe('PolygonZkEVMBridge Contract', () => { +const tokenName = 'Matic Token'; +const tokenSymbol = 'MATIC'; +const decimals = 18; +const tokenInitialBalance = ethers.utils.parseEther('20000000'); +const metadataToken = ethers.utils.defaultAbiCoder.encode( + ['string', 'string', 'uint8'], + [tokenName, tokenSymbol, decimals], +); +const gasTokenName = 'Fork Token'; +const gasTokenSymbol = 'FORK'; +const metadataGasToken = ethers.utils.defaultAbiCoder.encode( + ['string', 'string', 'uint8'], + [gasTokenName, gasTokenSymbol, decimals], +); + +const networkIDMainnet = 0; +const networkIDRollup = 1; + +const LEAF_TYPE_ASSET = 0; +const LEAF_TYPE_MESSAGE = 1; + +const polygonZkEVMAddress = ethers.constants.AddressZero; + +describe('PolygonZkEVMBridge Contract - for L2', () => { let deployer; let rollup; - let acc1; let polygonZkEVMGlobalExitRoot; let polygonZkEVMBridgeContract; let tokenContract; - - const tokenName = 'Matic Token'; - const tokenSymbol = 'MATIC'; - const decimals = 18; - const tokenInitialBalance = ethers.utils.parseEther('20000000'); - const metadataToken = ethers.utils.defaultAbiCoder.encode( - ['string', 'string', 'uint8'], - [tokenName, tokenSymbol, decimals], - ); - - const networkIDMainnet = 0; - const networkIDRollup = 1; - - const LEAF_TYPE_ASSET = 0; - const LEAF_TYPE_MESSAGE = 1; - - const polygonZkEVMAddress = ethers.constants.AddressZero; + let gasTokenContract; beforeEach('Deploy contracts', async () => { // load signers - [deployer, rollup, acc1] = await ethers.getSigners(); + [deployer, rollup] = await ethers.getSigners(); + + // deploy gas token + const gasTokenFactory = await ethers.getContractFactory('ERC20PermitMock'); + gasTokenContract = await gasTokenFactory.deploy( + gasTokenName, + gasTokenSymbol, + deployer.address, + tokenInitialBalance, + ); + await gasTokenContract.deployed(); // deploy PolygonZkEVMBridge const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridgeWrapper'); @@ -48,7 +64,13 @@ describe('PolygonZkEVMBridge Contract', () => { const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRoot'); polygonZkEVMGlobalExitRoot = await PolygonZkEVMGlobalExitRootFactory.deploy(rollup.address, polygonZkEVMBridgeContract.address); - await polygonZkEVMBridgeContract.initialize(networkIDMainnet, polygonZkEVMGlobalExitRoot.address, polygonZkEVMAddress); + await polygonZkEVMBridgeContract.initialize( + networkIDMainnet, + polygonZkEVMGlobalExitRoot.address, + polygonZkEVMAddress, + gasTokenContract.address, + true, + ); // deploy token const maticTokenFactory = await ethers.getContractFactory('ERC20PermitMock'); @@ -299,131 +321,6 @@ describe('PolygonZkEVMBridge Contract', () => { expect(await polygonZkEVMBridgeContract.lastUpdatedDepositCount()).to.be.equal(2); expect(await polygonZkEVMGlobalExitRoot.lastMainnetExitRoot()).to.not.be.equal(rootJSMainnet); - - // Just to have the metric of a low cost bridge Asset - const tokenAddress2 = ethers.constants.AddressZero; // Ether - const amount2 = ethers.utils.parseEther('10'); - await polygonZkEVMBridgeContract.bridgeAsset(destinationNetwork, destinationAddress, amount2, tokenAddress2, false, '0x', { value: amount2 }); - }); - - it('should claim tokens from Mainnet to Mainnet', async () => { - const originNetwork = networkIDMainnet; - const tokenAddress = tokenContract.address; - const amount = ethers.utils.parseEther('10'); - const destinationNetwork = networkIDMainnet; - const destinationAddress = acc1.address; - - const metadata = metadataToken; - const metadataHash = ethers.utils.solidityKeccak256(['bytes'], [metadata]); - - const mainnetExitRoot = await polygonZkEVMGlobalExitRoot.lastMainnetExitRoot(); - - // compute root merkle tree in Js - const height = 32; - const merkleTree = new MerkleTreeBridge(height); - const leafValue = getLeafValue( - LEAF_TYPE_ASSET, - originNetwork, - tokenAddress, - destinationNetwork, - destinationAddress, - amount, - metadataHash, - ); - merkleTree.add(leafValue); - - // check merkle root with SC - const rootJSRollup = merkleTree.getRoot(); - - // check only rollup account with update rollup exit root - await expect(polygonZkEVMGlobalExitRoot.updateExitRoot(rootJSRollup)) - .to.be.revertedWith('OnlyAllowedContracts'); - - // add rollup Merkle root - await expect(polygonZkEVMGlobalExitRoot.connect(rollup).updateExitRoot(rootJSRollup)) - .to.emit(polygonZkEVMGlobalExitRoot, 'UpdateGlobalExitRoot') - .withArgs(mainnetExitRoot, rootJSRollup); - - // check roots - const rollupExitRootSC = await polygonZkEVMGlobalExitRoot.lastRollupExitRoot(); - expect(rollupExitRootSC).to.be.equal(rootJSRollup); - - const computedGlobalExitRoot = calculateGlobalExitRoot(mainnetExitRoot, rollupExitRootSC); - expect(computedGlobalExitRoot).to.be.equal(await polygonZkEVMGlobalExitRoot.getLastGlobalExitRoot()); - - // check merkle proof - const proof = merkleTree.getProofTreeByIndex(0); - const index = 0; - - // verify merkle proof - expect(verifyMerkleProof(leafValue, proof, index, rootJSRollup)).to.be.equal(true); - expect(await polygonZkEVMBridgeContract.verifyMerkleProof( - leafValue, - proof, - index, - rootJSRollup, - )).to.be.equal(true); - - /* - * claim - * Can't claim without tokens - */ - await expect(polygonZkEVMBridgeContract.claimAsset( - proof, - index, - mainnetExitRoot, - rollupExitRootSC, - originNetwork, - tokenAddress, - destinationNetwork, - destinationAddress, - amount, - metadata, - )).to.be.revertedWith('ERC20: transfer amount exceeds balance'); - - // transfer tokens, then claim - await expect(tokenContract.transfer(polygonZkEVMBridgeContract.address, amount)) - .to.emit(tokenContract, 'Transfer') - .withArgs(deployer.address, polygonZkEVMBridgeContract.address, amount); - - expect(false).to.be.equal(await polygonZkEVMBridgeContract.isClaimed(index)); - - await expect(polygonZkEVMBridgeContract.claimAsset( - proof, - index, - mainnetExitRoot, - rollupExitRootSC, - originNetwork, - tokenAddress, - destinationNetwork, - destinationAddress, - amount, - metadata, - )) - .to.emit(polygonZkEVMBridgeContract, 'ClaimEvent') - .withArgs( - index, - originNetwork, - tokenAddress, - destinationAddress, - amount, - ).to.emit(tokenContract, 'Transfer') - .withArgs(polygonZkEVMBridgeContract.address, acc1.address, amount); - - // Can't claim because nullifier - await expect(polygonZkEVMBridgeContract.claimAsset( - proof, - index, - mainnetExitRoot, - rollupExitRootSC, - originNetwork, - tokenAddress, - destinationNetwork, - destinationAddress, - amount, - metadata, - )).to.be.revertedWith('AlreadyClaimed'); - expect(true).to.be.equal(await polygonZkEVMBridgeContract.isClaimed(index)); }); it('should claim tokens from Rollup to Mainnet', async () => { @@ -957,7 +854,7 @@ describe('PolygonZkEVMBridge Contract', () => { )).to.be.revertedWith('AlreadyClaimed'); }); - it('should claim ether', async () => { + it('should claim ether (on L2 if gas tokens have been deposited on L1)', async () => { // Add a claim leaf to rollup exit tree const originNetwork = networkIDMainnet; const tokenAddress = ethers.constants.AddressZero; // ether @@ -1031,8 +928,7 @@ describe('PolygonZkEVMBridge Contract', () => { const balanceDeployer = await ethers.provider.getBalance(deployer.address); /* - * Create a deposit to add ether to the PolygonZkEVMBridge - * Check deposit amount ether asserts + * Create a deposit to add gasTokens to the PolygonZkEVMBridge */ await expect(polygonZkEVMBridgeContract.bridgeAsset( networkIDRollup, @@ -1041,7 +937,6 @@ describe('PolygonZkEVMBridge Contract', () => { tokenAddress, true, '0x', - { value: ethers.utils.parseEther('100') }, )).to.be.revertedWith('AmountDoesNotMatchMsgValue'); // Check mainnet destination assert @@ -1055,7 +950,7 @@ describe('PolygonZkEVMBridge Contract', () => { { value: amount }, )).to.be.revertedWith('DestinationNetworkInvalid'); - // This is used just to pay ether to the PolygonZkEVMBridge smart contract and be able to claim it afterwards. + // Make ether available in the contract expect(await polygonZkEVMBridgeContract.bridgeAsset( networkIDRollup, destinationAddress, @@ -1294,3 +1189,272 @@ describe('PolygonZkEVMBridge Contract', () => { )).to.be.revertedWith('AlreadyClaimed'); }); }); + +describe('PolygonZkEVMBridge Contract - for L1', () => { + let deployer; + let rollup; + let acc1; + + let polygonZkEVMGlobalExitRoot; + let polygonZkEVMBridgeContract; + let tokenContract; + let gasTokenContract; + + beforeEach('Deploy contracts', async () => { + // load signers + [deployer, rollup, acc1] = await ethers.getSigners(); + + // deploy gas token + const gasTokenFactory = await ethers.getContractFactory('ERC20PermitMock'); + gasTokenContract = await gasTokenFactory.deploy( + gasTokenName, + gasTokenSymbol, + deployer.address, + tokenInitialBalance, + ); + await gasTokenContract.deployed(); + + // deploy PolygonZkEVMBridge + const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridgeWrapper'); + polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { initializer: false }); + + // deploy global exit root manager + const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRoot'); + polygonZkEVMGlobalExitRoot = await PolygonZkEVMGlobalExitRootFactory.deploy(rollup.address, polygonZkEVMBridgeContract.address); + + await polygonZkEVMBridgeContract.initialize( + networkIDMainnet, + polygonZkEVMGlobalExitRoot.address, + polygonZkEVMAddress, + gasTokenContract.address, + false, + ); + + // deploy token + const maticTokenFactory = await ethers.getContractFactory('ERC20PermitMock'); + tokenContract = await maticTokenFactory.deploy( + tokenName, + tokenSymbol, + deployer.address, + tokenInitialBalance, + ); + await tokenContract.deployed(); + }); + + it('should claim gasTokens to Mainnet from address(0) deposits', async () => { + const originNetwork = networkIDMainnet; + const tokenAddress = ethers.constants.AddressZero; + const amount = ethers.utils.parseEther('10'); + const destinationNetwork = networkIDMainnet; + const destinationAddress = acc1.address; + + const metadata = metadataToken; + const metadataHash = ethers.utils.solidityKeccak256(['bytes'], [metadata]); + + const mainnetExitRoot = await polygonZkEVMGlobalExitRoot.lastMainnetExitRoot(); + + // compute root merkle tree in Js + const height = 32; + const merkleTree = new MerkleTreeBridge(height); + const leafValue = getLeafValue( + LEAF_TYPE_ASSET, + originNetwork, + tokenAddress, + destinationNetwork, + destinationAddress, + amount, + metadataHash, + ); + merkleTree.add(leafValue); + + // check merkle root with SC + const rootJSRollup = merkleTree.getRoot(); + + // check only rollup account with update rollup exit root + await expect(polygonZkEVMGlobalExitRoot.updateExitRoot(rootJSRollup)) + .to.be.revertedWith('OnlyAllowedContracts'); + + // add rollup Merkle root + await expect(polygonZkEVMGlobalExitRoot.connect(rollup).updateExitRoot(rootJSRollup)) + .to.emit(polygonZkEVMGlobalExitRoot, 'UpdateGlobalExitRoot') + .withArgs(mainnetExitRoot, rootJSRollup); + + // check roots + const rollupExitRootSC = await polygonZkEVMGlobalExitRoot.lastRollupExitRoot(); + expect(rollupExitRootSC).to.be.equal(rootJSRollup); + + const computedGlobalExitRoot = calculateGlobalExitRoot(mainnetExitRoot, rollupExitRootSC); + expect(computedGlobalExitRoot).to.be.equal(await polygonZkEVMGlobalExitRoot.getLastGlobalExitRoot()); + + // check merkle proof + const proof = merkleTree.getProofTreeByIndex(0); + const index = 0; + + // verify merkle proof + expect(verifyMerkleProof(leafValue, proof, index, rootJSRollup)).to.be.equal(true); + expect(await polygonZkEVMBridgeContract.verifyMerkleProof( + leafValue, + proof, + index, + rootJSRollup, + )).to.be.equal(true); + + /* + * claim + * Can't claim without tokens + */ + await expect(polygonZkEVMBridgeContract.claimAsset( + proof, + index, + mainnetExitRoot, + rollupExitRootSC, + originNetwork, + tokenAddress, + destinationNetwork, + destinationAddress, + amount, + metadata, + )).to.be.revertedWith('ERC20: transfer amount exceeds balance'); + + // fund the bridge with tokens + gasTokenContract.transfer(polygonZkEVMBridgeContract.address, amount); + + // transfer tokens, then claim + await expect(tokenContract.transfer(polygonZkEVMBridgeContract.address, amount)) + .to.emit(tokenContract, 'Transfer') + .withArgs(deployer.address, polygonZkEVMBridgeContract.address, amount); + + expect(false).to.be.equal(await polygonZkEVMBridgeContract.isClaimed(index)); + + await expect(polygonZkEVMBridgeContract.claimAsset( + proof, + index, + mainnetExitRoot, + rollupExitRootSC, + originNetwork, + tokenAddress, + destinationNetwork, + destinationAddress, + amount, + metadata, + )) + .to.emit(polygonZkEVMBridgeContract, 'ClaimEvent') + .withArgs( + index, + originNetwork, + tokenAddress, + destinationAddress, + amount, + ).to.emit(gasTokenContract, 'Transfer') + .withArgs(polygonZkEVMBridgeContract.address, acc1.address, amount); + + // Can't claim because nullifier + await expect(polygonZkEVMBridgeContract.claimAsset( + proof, + index, + mainnetExitRoot, + rollupExitRootSC, + originNetwork, + tokenAddress, + destinationNetwork, + destinationAddress, + amount, + metadata, + )).to.be.revertedWith('AlreadyClaimed'); + expect(true).to.be.equal(await polygonZkEVMBridgeContract.isClaimed(index)); + }); + it('should create leaf with tokenOrigin=address(0) if gasTokens is deposited', async () => { + const depositCount = await polygonZkEVMBridgeContract.depositCount(); + const originNetwork = networkIDMainnet; + const tokenAddress = gasTokenContract.address; // Ether + const amount = ethers.utils.parseEther('10'); + const destinationNetwork = networkIDRollup; + const destinationAddress = deployer.address; + + const metadata = metadataGasToken; + gasTokenContract.approve(polygonZkEVMBridgeContract.address, amount); + + await expect(polygonZkEVMBridgeContract.bridgeAsset( + destinationNetwork, + destinationAddress, + amount, + tokenAddress, + true, + '0x', + )) + .to.emit( + polygonZkEVMBridgeContract, + 'BridgeEvent', + ) + .withArgs( + LEAF_TYPE_ASSET, + originNetwork, + ethers.constants.AddressZero, + destinationNetwork, + destinationAddress, + amount, + metadata, + depositCount, + ); + + // Prepare merkle tree + const height = 32; + const merkleTree = new MerkleTreeBridge(height); + + // Get the deposit's events + const filter = polygonZkEVMBridgeContract.filters.BridgeEvent( + null, + null, + null, + null, + null, + ); + const events = await polygonZkEVMBridgeContract.queryFilter(filter, 0, 'latest'); + events.forEach((e) => { + const { args } = e; + const leafValue = getLeafValue( + args.leafType, + args.originNetwork, + args.originAddress, + args.destinationNetwork, + args.destinationAddress, + args.amount, + ethers.utils.solidityKeccak256(['bytes'], [args.metadata]), + ); + merkleTree.add(leafValue); + }); + + // Check merkle root with SC + const rootSC = await polygonZkEVMBridgeContract.getDepositRoot(); + const rootJS = merkleTree.getRoot(); + + expect(rootSC).to.be.equal(rootJS); + }); + + it('should not allow to bridge ether', async () => { + const amount = ethers.utils.parseEther('10'); + const destinationAddress = acc1.address; + const tokenAddress = ethers.constants.AddressZero; + const destinationNetwork = networkIDRollup; + + await expect(polygonZkEVMBridgeContract.bridgeAsset( + destinationNetwork, + destinationAddress, + amount, + tokenAddress, + true, + '0x', + { value: amount }, + )).to.be.revertedWith('MsgValueNotZero()'); + }); + it('should not allow to send message with ether', async () => { + const amount = ethers.utils.parseEther('10'); + await expect(polygonZkEVMBridgeContract.bridgeMessage( + networkIDRollup, + ethers.constants.AddressZero, + true, + '0x', + { value: amount }, + )).to.be.revertedWith('MsgValueNotZero()'); + }); +}); diff --git a/test/contracts/bridgeMock.test.js b/test/contracts/bridgeMock.test.js index aa1cbb1dd..eca003411 100644 --- a/test/contracts/bridgeMock.test.js +++ b/test/contracts/bridgeMock.test.js @@ -18,6 +18,7 @@ describe('PolygonZkEVMBridge Mock Contract', () => { let polygonZkEVMGlobalExitRoot; let polygonZkEVMBridgeContract; let tokenContract; + let gasTokenContract; const tokenName = 'Matic Token'; const tokenSymbol = 'MATIC'; @@ -27,7 +28,8 @@ describe('PolygonZkEVMBridge Mock Contract', () => { ['string', 'string', 'uint8'], [tokenName, tokenSymbol, decimals], ); - + const gasTokenName = 'Fork Token'; + const gasTokenSymbol = 'FORK'; const networkIDMainnet = 0; const networkIDRollup = 1; @@ -38,6 +40,16 @@ describe('PolygonZkEVMBridge Mock Contract', () => { // load signers [deployer, rollup, acc1] = await ethers.getSigners(); + // deploy gas token + const gasTokenFactory = await ethers.getContractFactory('ERC20PermitMock'); + gasTokenContract = await gasTokenFactory.deploy( + gasTokenName, + gasTokenSymbol, + deployer.address, + tokenInitialBalance, + ); + await gasTokenContract.deployed(); + // deploy global exit root manager const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRootMock'); @@ -46,7 +58,13 @@ describe('PolygonZkEVMBridge Mock Contract', () => { polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { initializer: false }); polygonZkEVMGlobalExitRoot = await PolygonZkEVMGlobalExitRootFactory.deploy(rollup.address, polygonZkEVMBridgeContract.address); - await polygonZkEVMBridgeContract.initialize(networkIDMainnet, polygonZkEVMGlobalExitRoot.address, polygonZkEVMAddress); + await polygonZkEVMBridgeContract.initialize( + networkIDMainnet, + polygonZkEVMGlobalExitRoot.address, + polygonZkEVMAddress, + gasTokenContract.address, + true, + ); // deploy token const maticTokenFactory = await ethers.getContractFactory('ERC20PermitMock'); diff --git a/test/contracts/bridge_metadata.test.js b/test/contracts/bridge_metadata.test.js index 93c945e48..11b6ffa04 100644 --- a/test/contracts/bridge_metadata.test.js +++ b/test/contracts/bridge_metadata.test.js @@ -12,12 +12,15 @@ describe('PolygonZkEVMBridge Contract werid metadata', () => { let polygonZkEVMGlobalExitRoot; let polygonZkEVMBridgeContract; let tokenContract; + let gasTokenContract; const tokenName = 'Matic Token'; const tokenSymbol = 'MATIC'; const decimals = 18; const tokenInitialBalance = ethers.utils.parseEther('20000000'); + const gasTokenName = 'Fork Token'; + const gasTokenSymbol = 'FORK'; const networkIDMainnet = 0; const networkIDRollup = 1; const LEAF_TYPE_ASSET = 0; @@ -28,6 +31,16 @@ describe('PolygonZkEVMBridge Contract werid metadata', () => { // load signers [deployer, rollup] = await ethers.getSigners(); + // deploy gas token + const gasTokenFactory = await ethers.getContractFactory('ERC20PermitMock'); + gasTokenContract = await gasTokenFactory.deploy( + gasTokenName, + gasTokenSymbol, + deployer.address, + tokenInitialBalance, + ); + await gasTokenContract.deployed(); + // deploy PolygonZkEVMBridge const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridgeWrapper'); polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { initializer: false }); @@ -36,7 +49,13 @@ describe('PolygonZkEVMBridge Contract werid metadata', () => { const polygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRoot'); polygonZkEVMGlobalExitRoot = await polygonZkEVMGlobalExitRootFactory.deploy(rollup.address, polygonZkEVMBridgeContract.address); - await polygonZkEVMBridgeContract.initialize(networkIDMainnet, polygonZkEVMGlobalExitRoot.address, polygonZkEVMAddress); + await polygonZkEVMBridgeContract.initialize( + networkIDMainnet, + polygonZkEVMGlobalExitRoot.address, + polygonZkEVMAddress, + gasTokenContract.address, + true, + ); // deploy token const maticTokenFactory = await ethers.getContractFactory('TokenWrapped'); diff --git a/test/contracts/bridge_permit.test.js b/test/contracts/bridge_permit.test.js index fe0bbf015..15b78460f 100644 --- a/test/contracts/bridge_permit.test.js +++ b/test/contracts/bridge_permit.test.js @@ -25,6 +25,7 @@ describe('PolygonZkEVMBridge Contract Permit tests', () => { let polygonZkEVMGlobalExitRoot; let polygonZkEVMBridgeContract; let tokenContract; + let gasTokenContract; const tokenName = 'Matic Token'; const tokenSymbol = 'MATIC'; @@ -35,6 +36,9 @@ describe('PolygonZkEVMBridge Contract Permit tests', () => { [tokenName, tokenSymbol, decimals], ); + const gasTokenName = 'Fork Token'; + const gasTokenSymbol = 'FORK'; + const networkIDMainnet = 0; const networkIDRollup = 1; const LEAF_TYPE_ASSET = 0; @@ -45,6 +49,16 @@ describe('PolygonZkEVMBridge Contract Permit tests', () => { // load signers [deployer, rollup] = await ethers.getSigners(); + // deploy gas token + const gasTokenFactory = await ethers.getContractFactory('ERC20PermitMock'); + gasTokenContract = await gasTokenFactory.deploy( + gasTokenName, + gasTokenSymbol, + deployer.address, + tokenInitialBalance, + ); + await gasTokenContract.deployed(); + // deploy PolygonZkEVMBridge const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridgeWrapper'); polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { initializer: false }); @@ -53,7 +67,13 @@ describe('PolygonZkEVMBridge Contract Permit tests', () => { const polygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRoot'); polygonZkEVMGlobalExitRoot = await polygonZkEVMGlobalExitRootFactory.deploy(rollup.address, polygonZkEVMBridgeContract.address); - await polygonZkEVMBridgeContract.initialize(networkIDMainnet, polygonZkEVMGlobalExitRoot.address, polygonZkEVMAddress); + await polygonZkEVMBridgeContract.initialize( + networkIDMainnet, + polygonZkEVMGlobalExitRoot.address, + polygonZkEVMAddress, + gasTokenContract.address, + true, + ); // deploy token const maticTokenFactory = await ethers.getContractFactory('TokenWrapped'); diff --git a/test/contracts/emergencyManager.test.js b/test/contracts/emergencyManager.test.js index 57f1d2bde..e79904f0b 100644 --- a/test/contracts/emergencyManager.test.js +++ b/test/contracts/emergencyManager.test.js @@ -12,11 +12,16 @@ describe('Emergency mode test', () => { let polygonZkEVMContract; let maticTokenContract; let polygonZkEVMGlobalExitRoot; + let gasTokenContract; const maticTokenName = 'Matic Token'; const maticTokenSymbol = 'MATIC'; const maticTokenInitialBalance = ethers.utils.parseEther('20000000'); + const gasTokenName = 'Fork Token'; + const gasTokenSymbol = 'FORK'; + const gasTokenInitialBalance = ethers.utils.parseEther('20000000'); + const genesisRoot = '0x0000000000000000000000000000000000000000000000000000000000000001'; const networkIDMainnet = 0; @@ -34,6 +39,16 @@ describe('Emergency mode test', () => { // load signers [deployer, trustedAggregator, trustedSequencer, admin] = await ethers.getSigners(); + // deploy gas token + const gasTokenFactory = await ethers.getContractFactory('ERC20PermitMock'); + gasTokenContract = await gasTokenFactory.deploy( + gasTokenName, + gasTokenSymbol, + deployer.address, + gasTokenInitialBalance, + ); + await gasTokenContract.deployed(); + // deploy mock verifier const VerifierRollupHelperFactory = await ethers.getContractFactory( 'VerifierRollupHelperMock', @@ -59,7 +74,7 @@ describe('Emergency mode test', () => { firstDeployment = false; } - const nonceProxyBridge = Number((await ethers.provider.getTransactionCount(deployer.address))) + (firstDeployment ? 3 : 2); + const nonceProxyBridge = Number((await ethers.provider.getTransactionCount(deployer.address))) + (firstDeployment ? 2 : 1); const nonceProxyZkevm = nonceProxyBridge + 2; // Always have to redeploy impl since the polygonZkEVMGlobalExitRoot address changes const precalculateBridgeAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyBridge }); @@ -88,7 +103,13 @@ describe('Emergency mode test', () => { expect(precalculateBridgeAddress).to.be.equal(polygonZkEVMBridgeContract.address); expect(precalculateZkevmAddress).to.be.equal(polygonZkEVMContract.address); - await polygonZkEVMBridgeContract.initialize(networkIDMainnet, polygonZkEVMGlobalExitRoot.address, polygonZkEVMContract.address); + await polygonZkEVMBridgeContract.initialize( + networkIDMainnet, + polygonZkEVMGlobalExitRoot.address, + polygonZkEVMContract.address, + gasTokenContract.address, + true, + ); await polygonZkEVMContract.initialize( { admin: admin.address, diff --git a/test/contracts/polygonZkEVM.test.js b/test/contracts/polygonZkEVM.test.js index 34410a91e..a4042c848 100644 --- a/test/contracts/polygonZkEVM.test.js +++ b/test/contracts/polygonZkEVM.test.js @@ -12,6 +12,7 @@ describe('Polygon ZK-EVM', () => { let trustedSequencer; let admin; let aggregator1; + let gasTokenContract; let verifierContract; let polygonZkEVMBridgeContract; @@ -23,6 +24,10 @@ describe('Polygon ZK-EVM', () => { const maticTokenSymbol = 'MATIC'; const maticTokenInitialBalance = ethers.utils.parseEther('20000000'); + const gasTokenName = 'Fork Token'; + const gasTokenSymbol = 'FORK'; + const gasTokenInitialBalance = ethers.utils.parseEther('20000000'); + const genesisRoot = '0x0000000000000000000000000000000000000000000000000000000000000001'; const networkIDMainnet = 0; @@ -52,6 +57,16 @@ describe('Polygon ZK-EVM', () => { ); verifierContract = await VerifierRollupHelperFactory.deploy(); + // deploy gas token + const gasTokenFactory = await ethers.getContractFactory('ERC20PermitMock'); + gasTokenContract = await gasTokenFactory.deploy( + gasTokenName, + gasTokenSymbol, + deployer.address, + gasTokenInitialBalance, + ); + await gasTokenContract.deployed(); + // deploy MATIC const maticTokenFactory = await ethers.getContractFactory('ERC20PermitMock'); maticTokenContract = await maticTokenFactory.deploy( @@ -70,8 +85,8 @@ describe('Polygon ZK-EVM', () => { if ((await upgrades.admin.getInstance()).address !== '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0') { firstDeployment = false; } - const nonceProxyBridge = Number((await ethers.provider.getTransactionCount(deployer.address))) + (firstDeployment ? 3 : 2); - const nonceProxyZkevm = nonceProxyBridge + (firstDeployment ? 2 : 1); + const nonceProxyBridge = Number((await ethers.provider.getTransactionCount(deployer.address))) + (firstDeployment ? 2 : 2); + const nonceProxyZkevm = nonceProxyBridge + (firstDeployment ? 1 : 1); const precalculateBridgeAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyBridge }); const precalculateZkevmAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyZkevm }); @@ -98,7 +113,13 @@ describe('Polygon ZK-EVM', () => { expect(precalculateBridgeAddress).to.be.equal(polygonZkEVMBridgeContract.address); expect(precalculateZkevmAddress).to.be.equal(polygonZkEVMContract.address); - await polygonZkEVMBridgeContract.initialize(networkIDMainnet, polygonZkEVMGlobalExitRoot.address, polygonZkEVMContract.address); + await polygonZkEVMBridgeContract.initialize( + networkIDMainnet, + polygonZkEVMGlobalExitRoot.address, + polygonZkEVMContract.address, + gasTokenContract.address, + true, + ); await polygonZkEVMContract.initialize( { admin: admin.address, diff --git a/test/contracts/polygonZkEVMTestnetV2.test.js b/test/contracts/polygonZkEVMTestnetV2.test.js index d9e100ef4..317e471d2 100644 --- a/test/contracts/polygonZkEVMTestnetV2.test.js +++ b/test/contracts/polygonZkEVMTestnetV2.test.js @@ -7,6 +7,7 @@ describe('Polygon ZK-EVM TestnetV2', () => { let trustedAggregator; let trustedSequencer; let admin; + let gasTokenContract; let verifierContract; let polygonZkEVMBridgeContract; @@ -18,6 +19,10 @@ describe('Polygon ZK-EVM TestnetV2', () => { const maticTokenSymbol = 'MATIC'; const maticTokenInitialBalance = ethers.utils.parseEther('20000000'); + const gasTokenName = 'Fork Token'; + const gasTokenSymbol = 'FORK'; + const gasTokenInitialBalance = ethers.utils.parseEther('20000000'); + const genesisRoot = '0x0000000000000000000000000000000000000000000000000000000000000001'; const networkIDMainnet = 0; @@ -43,6 +48,16 @@ describe('Polygon ZK-EVM TestnetV2', () => { ); verifierContract = await VerifierRollupHelperFactory.deploy(); + // deploy gas token + const gasTokenFactory = await ethers.getContractFactory('ERC20PermitMock'); + gasTokenContract = await gasTokenFactory.deploy( + gasTokenName, + gasTokenSymbol, + deployer.address, + gasTokenInitialBalance, + ); + await gasTokenContract.deployed(); + // deploy MATIC const maticTokenFactory = await ethers.getContractFactory('ERC20PermitMock'); maticTokenContract = await maticTokenFactory.deploy( @@ -61,8 +76,9 @@ describe('Polygon ZK-EVM TestnetV2', () => { if ((await upgrades.admin.getInstance()).address !== '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0') { firstDeployment = false; } - const nonceProxyBridge = Number((await ethers.provider.getTransactionCount(deployer.address))) + (firstDeployment ? 3 : 2); + const nonceProxyBridge = Number((await ethers.provider.getTransactionCount(deployer.address))) + (firstDeployment ? 2 : 2); const nonceProxyZkevm = nonceProxyBridge + (doesNeedImplementationDeployment ? 2 : 1); + const precalculateBridgeAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyBridge }); const precalculateZkevmAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyZkevm }); firstDeployment = false; @@ -85,11 +101,16 @@ describe('Polygon ZK-EVM TestnetV2', () => { constructorArgs: [], unsafeAllow: ['constructor', 'state-variable-immutable'], }); - expect(precalculateBridgeAddress).to.be.equal(polygonZkEVMBridgeContract.address); expect(precalculateZkevmAddress).to.be.equal(polygonZkEVMContract.address); - await polygonZkEVMBridgeContract.initialize(networkIDMainnet, polygonZkEVMGlobalExitRoot.address, polygonZkEVMContract.address); + await polygonZkEVMBridgeContract.initialize( + networkIDMainnet, + polygonZkEVMGlobalExitRoot.address, + polygonZkEVMContract.address, + gasTokenContract.address, + true, + ); await polygonZkEVMContract.initialize( { admin: admin.address, From bda9dd5d1e0e138fda0fb642069a150dc3168c40 Mon Sep 17 00:00:00 2001 From: josojo Date: Tue, 11 Jul 2023 10:18:15 +0200 Subject: [PATCH 08/12] Initalize gloabl exit root without constructor (#19) --- contracts/PolygonZkEVMGlobalExitRootWrapper.sol | 17 +++++++++++++++++ .../PolygonZkEVMGlobalExitRoot.sol | 13 +++++++------ .../mocks/PolygonZkEVMGlobalExitRootMock.sol | 12 ++---------- test/contracts/bridge.test.js | 12 ++++++++---- test/contracts/bridgeMock.test.js | 9 +++++---- test/contracts/bridge_metadata.test.js | 5 +++-- test/contracts/bridge_permit.test.js | 5 +++-- test/contracts/emergencyManager.test.js | 6 +++--- test/contracts/globalExitRootManager.test.js | 10 ++++------ test/contracts/globalExitRootManagerL2.test.js | 1 + test/contracts/polygonZkEVM.test.js | 6 +++--- test/contracts/polygonZkEVMTestnetV2.test.js | 6 +++--- 12 files changed, 59 insertions(+), 43 deletions(-) create mode 100644 contracts/PolygonZkEVMGlobalExitRootWrapper.sol rename contracts/{ => inheritedMainContracts}/PolygonZkEVMGlobalExitRoot.sol (87%) diff --git a/contracts/PolygonZkEVMGlobalExitRootWrapper.sol b/contracts/PolygonZkEVMGlobalExitRootWrapper.sol new file mode 100644 index 000000000..5dd289827 --- /dev/null +++ b/contracts/PolygonZkEVMGlobalExitRootWrapper.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: AGPL-3.0 + +pragma solidity 0.8.17; + +import "./interfaces/IPolygonZkEVMGlobalExitRoot.sol"; +import "./inheritedMainContracts/PolygonZkEVMGlobalExitRoot.sol"; + +contract PolygonZkEVMGlobalExitRootWrapper is PolygonZkEVMGlobalExitRoot { + /** + * @param _rollupAddress Rollup contract address + * @param _bridgeAddress PolygonZkEVMBridge contract address + */ + function initialize(address _rollupAddress, address _bridgeAddress) public override initializer { + PolygonZkEVMGlobalExitRoot.initialize(_rollupAddress, _bridgeAddress); + } +} + diff --git a/contracts/PolygonZkEVMGlobalExitRoot.sol b/contracts/inheritedMainContracts/PolygonZkEVMGlobalExitRoot.sol similarity index 87% rename from contracts/PolygonZkEVMGlobalExitRoot.sol rename to contracts/inheritedMainContracts/PolygonZkEVMGlobalExitRoot.sol index 5b91d9e83..c859f9021 100644 --- a/contracts/PolygonZkEVMGlobalExitRoot.sol +++ b/contracts/inheritedMainContracts/PolygonZkEVMGlobalExitRoot.sol @@ -2,18 +2,19 @@ pragma solidity 0.8.17; -import "./interfaces/IPolygonZkEVMGlobalExitRoot.sol"; -import "./lib/GlobalExitRootLib.sol"; +import "../interfaces/IPolygonZkEVMGlobalExitRoot.sol"; +import "../lib/GlobalExitRootLib.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; /** * Contract responsible for managing the exit roots across multiple networks */ -contract PolygonZkEVMGlobalExitRoot is IPolygonZkEVMGlobalExitRoot { +contract PolygonZkEVMGlobalExitRoot is IPolygonZkEVMGlobalExitRoot, Initializable { // PolygonZkEVMBridge address - address public immutable bridgeAddress; + address public bridgeAddress; // Rollup contract address - address public immutable rollupAddress; + address public rollupAddress; // Rollup exit root, this will be updated every time a batch is verified bytes32 public lastRollupExitRoot; @@ -36,7 +37,7 @@ contract PolygonZkEVMGlobalExitRoot is IPolygonZkEVMGlobalExitRoot { * @param _rollupAddress Rollup contract address * @param _bridgeAddress PolygonZkEVMBridge contract address */ - constructor(address _rollupAddress, address _bridgeAddress) { + function initialize(address _rollupAddress, address _bridgeAddress) public virtual onlyInitializing { rollupAddress = _rollupAddress; bridgeAddress = _bridgeAddress; } diff --git a/contracts/mocks/PolygonZkEVMGlobalExitRootMock.sol b/contracts/mocks/PolygonZkEVMGlobalExitRootMock.sol index c41acea23..1aa937f54 100644 --- a/contracts/mocks/PolygonZkEVMGlobalExitRootMock.sol +++ b/contracts/mocks/PolygonZkEVMGlobalExitRootMock.sol @@ -1,21 +1,13 @@ // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.8.17; -import "../PolygonZkEVMGlobalExitRoot.sol"; +import "../PolygonZkEVMGlobalExitRootWrapper.sol"; /** * Contract responsible for managing the exit roots across multiple networks */ -contract PolygonZkEVMGlobalExitRootMock is PolygonZkEVMGlobalExitRoot { - /** - * @param _rollupAddress Rollup contract address - * @param _bridgeAddress PolygonZkEVM Bridge contract address - */ - constructor( - address _rollupAddress, - address _bridgeAddress - ) PolygonZkEVMGlobalExitRoot(_rollupAddress, _bridgeAddress) {} +contract PolygonZkEVMGlobalExitRootMock is PolygonZkEVMGlobalExitRootWrapper { /** * @notice Set last global exit root diff --git a/test/contracts/bridge.test.js b/test/contracts/bridge.test.js index 1d0eac262..654c07a81 100644 --- a/test/contracts/bridge.test.js +++ b/test/contracts/bridge.test.js @@ -61,8 +61,10 @@ describe('PolygonZkEVMBridge Contract - for L2', () => { polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { initializer: false }); // deploy global exit root manager - const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRoot'); - polygonZkEVMGlobalExitRoot = await PolygonZkEVMGlobalExitRootFactory.deploy(rollup.address, polygonZkEVMBridgeContract.address); + const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRootWrapper'); + polygonZkEVMGlobalExitRoot = await upgrades.deployProxy(PolygonZkEVMGlobalExitRootFactory, [], { initializer: false }); + + await polygonZkEVMGlobalExitRoot.initialize(rollup.address, polygonZkEVMBridgeContract.address); await polygonZkEVMBridgeContract.initialize( networkIDMainnet, @@ -1219,8 +1221,10 @@ describe('PolygonZkEVMBridge Contract - for L1', () => { polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { initializer: false }); // deploy global exit root manager - const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRoot'); - polygonZkEVMGlobalExitRoot = await PolygonZkEVMGlobalExitRootFactory.deploy(rollup.address, polygonZkEVMBridgeContract.address); + const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRootWrapper'); + polygonZkEVMGlobalExitRoot = await upgrades.deployProxy(PolygonZkEVMGlobalExitRootFactory, [], { initializer: false }); + + await polygonZkEVMGlobalExitRoot.initialize(rollup.address, polygonZkEVMBridgeContract.address); await polygonZkEVMBridgeContract.initialize( networkIDMainnet, diff --git a/test/contracts/bridgeMock.test.js b/test/contracts/bridgeMock.test.js index eca003411..276ca3117 100644 --- a/test/contracts/bridgeMock.test.js +++ b/test/contracts/bridgeMock.test.js @@ -50,14 +50,15 @@ describe('PolygonZkEVMBridge Mock Contract', () => { ); await gasTokenContract.deployed(); - // deploy global exit root manager - const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRootMock'); - // deploy PolygonZkEVMBridge const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridgeMock'); polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { initializer: false }); - polygonZkEVMGlobalExitRoot = await PolygonZkEVMGlobalExitRootFactory.deploy(rollup.address, polygonZkEVMBridgeContract.address); + // deploy global exit root manager + const polygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRootMock'); + polygonZkEVMGlobalExitRoot = await upgrades.deployProxy(polygonZkEVMGlobalExitRootFactory, [], { initializer: false }); + await polygonZkEVMGlobalExitRoot.initialize(rollup.address, polygonZkEVMBridgeContract.address); + await polygonZkEVMBridgeContract.initialize( networkIDMainnet, polygonZkEVMGlobalExitRoot.address, diff --git a/test/contracts/bridge_metadata.test.js b/test/contracts/bridge_metadata.test.js index 11b6ffa04..80da57170 100644 --- a/test/contracts/bridge_metadata.test.js +++ b/test/contracts/bridge_metadata.test.js @@ -46,8 +46,9 @@ describe('PolygonZkEVMBridge Contract werid metadata', () => { polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { initializer: false }); // deploy global exit root manager - const polygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRoot'); - polygonZkEVMGlobalExitRoot = await polygonZkEVMGlobalExitRootFactory.deploy(rollup.address, polygonZkEVMBridgeContract.address); + const polygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRootWrapper'); + polygonZkEVMGlobalExitRoot = await upgrades.deployProxy(polygonZkEVMGlobalExitRootFactory, [], { initializer: false }); + await polygonZkEVMGlobalExitRoot.initialize(rollup.address, polygonZkEVMBridgeContract.address); await polygonZkEVMBridgeContract.initialize( networkIDMainnet, diff --git a/test/contracts/bridge_permit.test.js b/test/contracts/bridge_permit.test.js index 15b78460f..75e898396 100644 --- a/test/contracts/bridge_permit.test.js +++ b/test/contracts/bridge_permit.test.js @@ -64,8 +64,9 @@ describe('PolygonZkEVMBridge Contract Permit tests', () => { polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { initializer: false }); // deploy global exit root manager - const polygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRoot'); - polygonZkEVMGlobalExitRoot = await polygonZkEVMGlobalExitRootFactory.deploy(rollup.address, polygonZkEVMBridgeContract.address); + const polygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRootWrapper'); + polygonZkEVMGlobalExitRoot = await upgrades.deployProxy(polygonZkEVMGlobalExitRootFactory, [], { initializer: false }); + await polygonZkEVMGlobalExitRoot.initialize(rollup.address, polygonZkEVMBridgeContract.address); await polygonZkEVMBridgeContract.initialize( networkIDMainnet, diff --git a/test/contracts/emergencyManager.test.js b/test/contracts/emergencyManager.test.js index e79904f0b..722cb7203 100644 --- a/test/contracts/emergencyManager.test.js +++ b/test/contracts/emergencyManager.test.js @@ -81,12 +81,12 @@ describe('Emergency mode test', () => { const precalculateZkevmAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyZkevm }); firstDeployment = false; - const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRoot'); + const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRootWrapper'); polygonZkEVMGlobalExitRoot = await upgrades.deployProxy(PolygonZkEVMGlobalExitRootFactory, [], { initializer: false, - constructorArgs: [precalculateZkevmAddress, precalculateBridgeAddress], - unsafeAllow: ['constructor', 'state-variable-immutable'], + constructorArgs: [], }); + await polygonZkEVMGlobalExitRoot.initialize(precalculateZkevmAddress, precalculateBridgeAddress); // deploy PolygonZkEVMBridge const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridgeWrapper'); diff --git a/test/contracts/globalExitRootManager.test.js b/test/contracts/globalExitRootManager.test.js index 78ff7a6aa..444f2a698 100644 --- a/test/contracts/globalExitRootManager.test.js +++ b/test/contracts/globalExitRootManager.test.js @@ -1,5 +1,5 @@ const { expect } = require('chai'); -const { ethers } = require('hardhat'); +const { ethers, upgrades } = require('hardhat'); function calculateGlobalExitRoot(mainnetExitRoot, rollupExitRoot) { return ethers.utils.solidityKeccak256(['bytes32', 'bytes32'], [mainnetExitRoot, rollupExitRoot]); @@ -16,12 +16,10 @@ describe('Global Exit Root', () => { [, rollup, PolygonZkEVMBridge] = await ethers.getSigners(); // deploy global exit root manager - const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRoot'); + const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRootWrapper'); + polygonZkEVMGlobalExitRoot = await upgrades.deployProxy(PolygonZkEVMGlobalExitRootFactory, [], { initializer: false }); - polygonZkEVMGlobalExitRoot = await PolygonZkEVMGlobalExitRootFactory.deploy( - rollup.address, - PolygonZkEVMBridge.address, - ); + await polygonZkEVMGlobalExitRoot.initialize(rollup.address, PolygonZkEVMBridge.address); await polygonZkEVMGlobalExitRoot.deployed(); }); diff --git a/test/contracts/globalExitRootManagerL2.test.js b/test/contracts/globalExitRootManagerL2.test.js index 69bbc7f59..c923375ef 100644 --- a/test/contracts/globalExitRootManagerL2.test.js +++ b/test/contracts/globalExitRootManagerL2.test.js @@ -15,6 +15,7 @@ describe('Global Exit Root L2', () => { // deploy global exit root manager const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRootL2Mock', deployer); polygonZkEVMGlobalExitRoot = await PolygonZkEVMGlobalExitRootFactory.deploy(PolygonZkEVMBridge.address); + await polygonZkEVMGlobalExitRoot.deployed(); }); it('should check the constructor parameters', async () => { diff --git a/test/contracts/polygonZkEVM.test.js b/test/contracts/polygonZkEVM.test.js index a4042c848..ec638b2c7 100644 --- a/test/contracts/polygonZkEVM.test.js +++ b/test/contracts/polygonZkEVM.test.js @@ -92,12 +92,12 @@ describe('Polygon ZK-EVM', () => { const precalculateZkevmAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyZkevm }); firstDeployment = false; - const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRoot'); + const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRootWrapper'); polygonZkEVMGlobalExitRoot = await upgrades.deployProxy(PolygonZkEVMGlobalExitRootFactory, [], { initializer: false, - constructorArgs: [precalculateZkevmAddress, precalculateBridgeAddress], - unsafeAllow: ['constructor', 'state-variable-immutable'], + constructorArgs: [], }); + await polygonZkEVMGlobalExitRoot.initialize(precalculateZkevmAddress, precalculateBridgeAddress); // deploy PolygonZkEVMBridge const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridgeWrapper'); diff --git a/test/contracts/polygonZkEVMTestnetV2.test.js b/test/contracts/polygonZkEVMTestnetV2.test.js index 317e471d2..aa8be2aac 100644 --- a/test/contracts/polygonZkEVMTestnetV2.test.js +++ b/test/contracts/polygonZkEVMTestnetV2.test.js @@ -83,13 +83,13 @@ describe('Polygon ZK-EVM TestnetV2', () => { const precalculateZkevmAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyZkevm }); firstDeployment = false; doesNeedImplementationDeployment = false; - const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRoot'); + const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRootWrapper'); polygonZkEVMGlobalExitRoot = await upgrades.deployProxy(PolygonZkEVMGlobalExitRootFactory, [], { initializer: false, - constructorArgs: [precalculateZkevmAddress, precalculateBridgeAddress], - unsafeAllow: ['constructor', 'state-variable-immutable'], + constructorArgs: [], }); + await polygonZkEVMGlobalExitRoot.initialize(precalculateZkevmAddress, precalculateBridgeAddress); // deploy PolygonZkEVMBridge const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridgeWrapper'); polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { initializer: false }); From 4fcf679bf68c534039e5160f19dc12a234d169c0 Mon Sep 17 00:00:00 2001 From: josojo Date: Tue, 11 Jul 2023 10:22:07 +0200 Subject: [PATCH 09/12] Packing chainId and ForkID into initializerParams (#20) --- contracts/PolygonZkEVMWrapper.sol | 8 ++------ .../inheritedMainContracts/PolygonZkEVM.sol | 12 ++++-------- contracts/interfaces/IPolygonZkEVM.sol | 4 ++++ test/contracts/emergencyManager.test.js | 4 ++-- test/contracts/polygonZkEVM.test.js | 18 ++++++++++-------- test/contracts/polygonZkEVMTestnetV2.test.js | 5 +++-- test/contracts/snark_stark_input.test.js | 4 ++-- 7 files changed, 27 insertions(+), 28 deletions(-) diff --git a/contracts/PolygonZkEVMWrapper.sol b/contracts/PolygonZkEVMWrapper.sol index b3255f769..113fbc17a 100644 --- a/contracts/PolygonZkEVMWrapper.sol +++ b/contracts/PolygonZkEVMWrapper.sol @@ -13,9 +13,7 @@ contract PolygonZkEVMWrapper is PolygonZkEVM{ IPolygonZkEVMGlobalExitRoot _globalExitRootManager, IERC20Upgradeable _matic, IVerifierRollup _rollupVerifier, - IPolygonZkEVMBridge _bridgeAddress, - uint64 _chainID, - uint64 _forkID + IPolygonZkEVMBridge _bridgeAddress ) public override initializer { PolygonZkEVM.initialize( initializePackedParameters, @@ -26,9 +24,7 @@ contract PolygonZkEVMWrapper is PolygonZkEVM{ _globalExitRootManager, _matic, _rollupVerifier, - _bridgeAddress, - _chainID, - _forkID + _bridgeAddress ); } } \ No newline at end of file diff --git a/contracts/inheritedMainContracts/PolygonZkEVM.sol b/contracts/inheritedMainContracts/PolygonZkEVM.sol index f1dab0400..d52a01d69 100644 --- a/contracts/inheritedMainContracts/PolygonZkEVM.sol +++ b/contracts/inheritedMainContracts/PolygonZkEVM.sol @@ -362,8 +362,6 @@ contract PolygonZkEVM is * @param _matic MATIC token address * @param _rollupVerifier Rollup verifier address * @param _bridgeAddress Bridge address - * @param _chainID L2 chainID - * @param _forkID Fork Id */ function initialize( InitializePackedParameters calldata initializePackedParameters, @@ -374,16 +372,14 @@ contract PolygonZkEVM is IPolygonZkEVMGlobalExitRoot _globalExitRootManager, IERC20Upgradeable _matic, IVerifierRollup _rollupVerifier, - IPolygonZkEVMBridge _bridgeAddress, - uint64 _chainID, - uint64 _forkID + IPolygonZkEVMBridge _bridgeAddress ) public virtual onlyInitializing { globalExitRootManager = _globalExitRootManager; matic = _matic; rollupVerifier = _rollupVerifier; bridgeAddress = _bridgeAddress; - chainID = _chainID; - forkID = _forkID; + chainID = initializePackedParameters.chainID; + forkID = initializePackedParameters.forkID; admin = initializePackedParameters.admin; trustedSequencer = initializePackedParameters.trustedSequencer; trustedAggregator = initializePackedParameters.trustedAggregator; @@ -421,7 +417,7 @@ contract PolygonZkEVM is __Ownable_init_unchained(); // emit version event - emit UpdateZkEVMVersion(0, forkID, _version); + emit UpdateZkEVMVersion(0, initializePackedParameters.forkID, _version); } modifier onlyAdmin() { diff --git a/contracts/interfaces/IPolygonZkEVM.sol b/contracts/interfaces/IPolygonZkEVM.sol index 6ce02d880..b21106406 100644 --- a/contracts/interfaces/IPolygonZkEVM.sol +++ b/contracts/interfaces/IPolygonZkEVM.sol @@ -13,6 +13,8 @@ interface IPolygonZkEVM { * @param pendingStateTimeout Pending state timeout * @param trustedAggregator Trusted aggregator * @param trustedAggregatorTimeout Trusted aggregator timeout + * @param _chainID L2 chainID + * @param _forkID Fork Id */ struct InitializePackedParameters { address admin; @@ -20,6 +22,8 @@ interface IPolygonZkEVM { uint64 pendingStateTimeout; address trustedAggregator; uint64 trustedAggregatorTimeout; + uint64 chainID; + uint64 forkID; } function chainID() external view returns (uint64); diff --git a/test/contracts/emergencyManager.test.js b/test/contracts/emergencyManager.test.js index 722cb7203..d8c936f62 100644 --- a/test/contracts/emergencyManager.test.js +++ b/test/contracts/emergencyManager.test.js @@ -117,6 +117,8 @@ describe('Emergency mode test', () => { pendingStateTimeout: pendingStateTimeoutDefault, trustedAggregator: trustedAggregator.address, trustedAggregatorTimeout: trustedAggregatorTimeoutDefault, + chainID, + forkID: 0, }, genesisRoot, urlSequencer, @@ -126,8 +128,6 @@ describe('Emergency mode test', () => { maticTokenContract.address, verifierContract.address, polygonZkEVMBridgeContract.address, - chainID, - 0, ); // fund sequencer address with Matic tokens diff --git a/test/contracts/polygonZkEVM.test.js b/test/contracts/polygonZkEVM.test.js index ec638b2c7..2469e5709 100644 --- a/test/contracts/polygonZkEVM.test.js +++ b/test/contracts/polygonZkEVM.test.js @@ -127,6 +127,8 @@ describe('Polygon ZK-EVM', () => { pendingStateTimeout: pendingStateTimeoutDefault, trustedAggregator: trustedAggregator.address, trustedAggregatorTimeout: trustedAggregatorTimeoutDefault, + chainID, + forkID, }, genesisRoot, urlSequencer, @@ -136,8 +138,6 @@ describe('Polygon ZK-EVM', () => { maticTokenContract.address, verifierContract.address, polygonZkEVMBridgeContract.address, - chainID, - forkID, ); // fund sequencer address with Matic tokens @@ -187,6 +187,8 @@ describe('Polygon ZK-EVM', () => { pendingStateTimeout: HALT_AGGREGATION_TIMEOUT + 1, trustedAggregator: trustedAggregator.address, trustedAggregatorTimeout: trustedAggregatorTimeoutDefault, + chainID, + forkID, }, genesisRoot, urlSequencer, @@ -196,8 +198,7 @@ describe('Polygon ZK-EVM', () => { maticTokenContract.address, verifierContract.address, polygonZkEVMBridgeContract.address, - chainID, - forkID, + )).to.be.revertedWith('PendingStateTimeoutExceedHaltAggregationTimeout'); await expect(polygonZkEVMContractInitialize.initialize( @@ -207,6 +208,8 @@ describe('Polygon ZK-EVM', () => { pendingStateTimeout: pendingStateTimeoutDefault, trustedAggregator: trustedAggregator.address, trustedAggregatorTimeout: HALT_AGGREGATION_TIMEOUT + 1, + chainID, + forkID, }, genesisRoot, urlSequencer, @@ -216,8 +219,7 @@ describe('Polygon ZK-EVM', () => { maticTokenContract.address, verifierContract.address, polygonZkEVMBridgeContract.address, - chainID, - forkID, + )).to.be.revertedWith('TrustedAggregatorTimeoutExceedHaltAggregationTimeout'); await expect( @@ -228,6 +230,8 @@ describe('Polygon ZK-EVM', () => { pendingStateTimeout: pendingStateTimeoutDefault, trustedAggregator: trustedAggregator.address, trustedAggregatorTimeout: trustedAggregatorTimeoutDefault, + chainID, + forkID, }, genesisRoot, urlSequencer, @@ -237,8 +241,6 @@ describe('Polygon ZK-EVM', () => { maticTokenContract.address, verifierContract.address, polygonZkEVMBridgeContract.address, - chainID, - forkID, ), ).to.emit(polygonZkEVMContractInitialize, 'UpdateZkEVMVersion').withArgs(0, forkID, version); }); diff --git a/test/contracts/polygonZkEVMTestnetV2.test.js b/test/contracts/polygonZkEVMTestnetV2.test.js index aa8be2aac..fe0d90c7b 100644 --- a/test/contracts/polygonZkEVMTestnetV2.test.js +++ b/test/contracts/polygonZkEVMTestnetV2.test.js @@ -118,6 +118,8 @@ describe('Polygon ZK-EVM TestnetV2', () => { pendingStateTimeout: pendingStateTimeoutDefault, trustedAggregator: trustedAggregator.address, trustedAggregatorTimeout: trustedAggregatorTimeoutDefault, + chainID, + forkID, }, genesisRoot, urlSequencer, @@ -127,8 +129,7 @@ describe('Polygon ZK-EVM TestnetV2', () => { maticTokenContract.address, verifierContract.address, polygonZkEVMBridgeContract.address, - chainID, - forkID, + ); // fund sequencer address with Matic tokens diff --git a/test/contracts/snark_stark_input.test.js b/test/contracts/snark_stark_input.test.js index 1c1aed626..2c2c05bdc 100644 --- a/test/contracts/snark_stark_input.test.js +++ b/test/contracts/snark_stark_input.test.js @@ -34,6 +34,8 @@ describe('Polygon ZK-EVM snark stark input test', () => { pendingStateTimeout: 0, trustedAggregator: randomSigner.address, trustedAggregatorTimeout: 0, + chainID, + forkID: 0, }, genesisRoot, urlSequencer, @@ -43,8 +45,6 @@ describe('Polygon ZK-EVM snark stark input test', () => { randomSigner.address, randomSigner.address, randomSigner.address, - chainID, - 0, ); await polygonZkEVMContract.deployed(); From 4e029589ba2f873df5074729b501a64584c1811b Mon Sep 17 00:00:00 2001 From: josojo Date: Fri, 14 Jul 2023 12:25:47 +0200 Subject: [PATCH 10/12] Virtual state root appending (#21) --- contracts/PolygonZkEVMWrapper.sol | 43 +++++++++++++++++++ .../inheritedMainContracts/PolygonZkEVM.sol | 6 +-- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/contracts/PolygonZkEVMWrapper.sol b/contracts/PolygonZkEVMWrapper.sol index 113fbc17a..a86905709 100644 --- a/contracts/PolygonZkEVMWrapper.sol +++ b/contracts/PolygonZkEVMWrapper.sol @@ -27,4 +27,47 @@ contract PolygonZkEVMWrapper is PolygonZkEVM{ _bridgeAddress ); } + function verifyBatchesTrustedAggregator( + uint64 pendingStateNum, + uint64 initNumBatch, + uint64 finalNewBatch, + bytes32 newLocalExitRoot, + bytes32 newStateRoot, + bytes calldata proof + ) public override onlyTrustedAggregator { + PolygonZkEVM.verifyBatchesTrustedAggregator( + pendingStateNum, + initNumBatch, + finalNewBatch, + newLocalExitRoot, + newStateRoot, + proof + ); + } + function sequenceBatches( + BatchData[] calldata batches, + address l2Coinbase + ) public override ifNotEmergencyState onlyTrustedSequencer { + PolygonZkEVM.sequenceBatches( + batches, + l2Coinbase + ); + } + function verifyBatches( + uint64 pendingStateNum, + uint64 initNumBatch, + uint64 finalNewBatch, + bytes32 newLocalExitRoot, + bytes32 newStateRoot, + bytes calldata proof + ) public override ifNotEmergencyState { + PolygonZkEVM.verifyBatches( + pendingStateNum, + initNumBatch, + finalNewBatch, + newLocalExitRoot, + newStateRoot, + proof + ); + } } \ No newline at end of file diff --git a/contracts/inheritedMainContracts/PolygonZkEVM.sol b/contracts/inheritedMainContracts/PolygonZkEVM.sol index d52a01d69..b122ce5fe 100644 --- a/contracts/inheritedMainContracts/PolygonZkEVM.sol +++ b/contracts/inheritedMainContracts/PolygonZkEVM.sol @@ -460,7 +460,7 @@ contract PolygonZkEVM is function sequenceBatches( BatchData[] calldata batches, address l2Coinbase - ) external ifNotEmergencyState onlyTrustedSequencer { + ) public virtual ifNotEmergencyState onlyTrustedSequencer { uint256 batchesNum = batches.length; if (batchesNum == 0) { revert SequenceZeroBatches(); @@ -616,7 +616,7 @@ contract PolygonZkEVM is bytes32 newLocalExitRoot, bytes32 newStateRoot, bytes calldata proof - ) external ifNotEmergencyState { + ) public virtual ifNotEmergencyState { // Check if the trusted aggregator timeout expired, // Note that the sequencedBatches struct must exists for this finalNewBatch, if not newAccInputHash will be 0 if ( @@ -689,7 +689,7 @@ contract PolygonZkEVM is bytes32 newLocalExitRoot, bytes32 newStateRoot, bytes calldata proof - ) external onlyTrustedAggregator { + ) public virtual onlyTrustedAggregator { _verifyAndRewardBatches( pendingStateNum, initNumBatch, From 791b09688a07fb7d26d87389560c12725b893e62 Mon Sep 17 00:00:00 2001 From: josojo Date: Thu, 27 Jul 2023 10:18:09 +0200 Subject: [PATCH 11/12] =?UTF-8?q?allows=20to=20override=20function=20overr?= =?UTF-8?q?idePendingState=20and=20consolidatePendi=E2=80=A6=20(#22)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contracts/inheritedMainContracts/PolygonZkEVM.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/inheritedMainContracts/PolygonZkEVM.sol b/contracts/inheritedMainContracts/PolygonZkEVM.sol index b122ce5fe..a7c4ed915 100644 --- a/contracts/inheritedMainContracts/PolygonZkEVM.sol +++ b/contracts/inheritedMainContracts/PolygonZkEVM.sol @@ -832,7 +832,7 @@ contract PolygonZkEVM is * Can be called by the trusted aggregator, which can consolidate any state without the timeout restrictions * @param pendingStateNum Pending state to consolidate */ - function consolidatePendingState(uint64 pendingStateNum) external { + function consolidatePendingState(uint64 pendingStateNum) public virtual { // Check if pending state can be consolidated // If trusted aggregator is the sender, do not check the timeout or the emergency state if (msg.sender != trustedAggregator) { @@ -1329,7 +1329,7 @@ contract PolygonZkEVM is bytes32 newLocalExitRoot, bytes32 newStateRoot, bytes calldata proof - ) external onlyTrustedAggregator { + ) public virtual onlyTrustedAggregator { _proveDistinctPendingState( initPendingStateNum, finalPendingStateNum, From 426ced7cc818c1940647d7aca1a12ba23e6abc09 Mon Sep 17 00:00:00 2001 From: josojo Date: Sun, 30 Jul 2023 15:16:18 +0200 Subject: [PATCH 12/12] making the deposit contract configurable (#23) --- contracts/PolygonZkEVMBridgeWrapper.sol | 6 +++-- .../PolygonZkEVMBridge.sol | 27 ++++++++++++------- contracts/lib/DepositContract.sol | 22 ++++++++++----- contracts/mocks/PolygonZkEVMBridgeMock.sol | 6 ++++- test/contracts/bridge.test.js | 9 +++++++ test/contracts/bridgeMock.test.js | 3 +++ test/contracts/bridge_metadata.test.js | 3 +++ test/contracts/bridge_permit.test.js | 3 +++ test/contracts/emergencyManager.test.js | 3 +++ test/contracts/polygonZkEVM.test.js | 3 +++ test/contracts/polygonZkEVMTestnetV2.test.js | 3 +++ 11 files changed, 70 insertions(+), 18 deletions(-) diff --git a/contracts/PolygonZkEVMBridgeWrapper.sol b/contracts/PolygonZkEVMBridgeWrapper.sol index c1ac52599..c3c0be027 100644 --- a/contracts/PolygonZkEVMBridgeWrapper.sol +++ b/contracts/PolygonZkEVMBridgeWrapper.sol @@ -9,8 +9,10 @@ contract PolygonZkEVMBridgeWrapper is PolygonZkEVMBridge{ IBasePolygonZkEVMGlobalExitRoot _globalExitRootManager, address _polygonZkEVMaddress, address _gasTokenAddress, - bool _isDeployedOnL2 + bool _isDeployedOnL2, + uint32 _lastUpdatedDepositCount, + bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] memory depositBranches ) public virtual override initializer { - PolygonZkEVMBridge.initialize(_networkID, _globalExitRootManager, _polygonZkEVMaddress, _gasTokenAddress, _isDeployedOnL2); + PolygonZkEVMBridge.initialize(_networkID, _globalExitRootManager, _polygonZkEVMaddress, _gasTokenAddress, _isDeployedOnL2, _lastUpdatedDepositCount, depositBranches); } } \ No newline at end of file diff --git a/contracts/inheritedMainContracts/PolygonZkEVMBridge.sol b/contracts/inheritedMainContracts/PolygonZkEVMBridge.sol index 80a5b3517..818aba934 100644 --- a/contracts/inheritedMainContracts/PolygonZkEVMBridge.sol +++ b/contracts/inheritedMainContracts/PolygonZkEVMBridge.sol @@ -83,6 +83,10 @@ contract PolygonZkEVMBridge is * @param _networkID networkID * @param _globalExitRootManager global exit root manager address * @param _polygonZkEVMaddress polygonZkEVM address + * @param _gasTokenAddress gas token address + * @param _isDeployedOnL2 flag to indicate if the contract is deployed on L2 + * @param _lastUpdatedDepositCount last updated deposit count + * @param depositBranches deposit branches * @notice The value of `_polygonZkEVMaddress` on the L2 deployment of the contract will be address(0), so * emergency state is not possible for the L2 deployment of the bridge, intentionally */ @@ -91,14 +95,18 @@ contract PolygonZkEVMBridge is IBasePolygonZkEVMGlobalExitRoot _globalExitRootManager, address _polygonZkEVMaddress, address _gasTokenAddress, - bool _isDeployedOnL2 + bool _isDeployedOnL2, + uint32 _lastUpdatedDepositCount, + bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] memory depositBranches ) public virtual onlyInitializing { networkID = _networkID; globalExitRootManager = _globalExitRootManager; polygonZkEVMaddress = _polygonZkEVMaddress; gasTokenAddress = _gasTokenAddress; isDeployedOnL2 = _isDeployedOnL2; - + lastUpdatedDepositCount = _lastUpdatedDepositCount; + DepositContract.initialize(lastUpdatedDepositCount, depositBranches); + // Initialize OZ contracts __ReentrancyGuard_init(); } @@ -280,7 +288,7 @@ contract PolygonZkEVMBridge is address destinationAddress, bool forceUpdateGlobalExitRoot, bytes calldata metadata - ) external payable ifNotEmergencyState { + ) public virtual payable ifNotEmergencyState { if (msg.value != 0 && !isDeployedOnL2) { revert MsgValueNotZero(); } @@ -344,7 +352,7 @@ contract PolygonZkEVMBridge is address destinationAddress, uint256 amount, bytes calldata metadata - ) external ifNotEmergencyState { + ) public virtual ifNotEmergencyState { // Verify leaf exist and it does not have been claimed _verifyLeaf( smtProof, @@ -478,7 +486,7 @@ contract PolygonZkEVMBridge is address destinationAddress, uint256 amount, bytes calldata metadata - ) external ifNotEmergencyState { + ) public virtual ifNotEmergencyState { // Verify leaf exist and it does not have been claimed _verifyLeaf( smtProof, @@ -667,7 +675,7 @@ contract PolygonZkEVMBridge is * @notice Function to check if an index is claimed or not * @param index Index */ - function isClaimed(uint256 index) external view returns (bool) { + function isClaimed(uint256 index) public virtual view returns (bool) { (uint256 wordPos, uint256 bitPos) = _bitmapPositions(index); uint256 mask = (1 << bitPos); return (claimedBitMap[wordPos] & mask) == mask; @@ -677,7 +685,7 @@ contract PolygonZkEVMBridge is * @notice Function to check that an index is not claimed and set it as claimed * @param index Index */ - function _setAndCheckClaimed(uint256 index) private { + function _setAndCheckClaimed(uint256 index) internal virtual { (uint256 wordPos, uint256 bitPos) = _bitmapPositions(index); uint256 mask = 1 << bitPos; uint256 flipped = claimedBitMap[wordPos] ^= mask; @@ -689,7 +697,7 @@ contract PolygonZkEVMBridge is /** * @notice Function to update the globalExitRoot if the last deposit is not submitted */ - function updateGlobalExitRoot() external { + function updateGlobalExitRoot() public { if (lastUpdatedDepositCount < depositCount) { _updateGlobalExitRoot(); } @@ -709,7 +717,7 @@ contract PolygonZkEVMBridge is */ function _bitmapPositions( uint256 index - ) private pure returns (uint256 wordPos, uint256 bitPos) { + ) internal pure returns (uint256 wordPos, uint256 bitPos) { wordPos = uint248(index >> 8); bitPos = uint8(index); } @@ -898,3 +906,4 @@ contract PolygonZkEVMBridge is } } } + diff --git a/contracts/lib/DepositContract.sol b/contracts/lib/DepositContract.sol index 46ecfcdb8..70fc8f0cb 100644 --- a/contracts/lib/DepositContract.sol +++ b/contracts/lib/DepositContract.sol @@ -22,7 +22,7 @@ contract DepositContract is ReentrancyGuardUpgradeable { // Branch array which contains the necessary sibilings to compute the next root when a new // leaf is inserted - bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] internal _branch; + bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] public branch; // Counter of current deposits uint256 public depositCount; @@ -33,6 +33,16 @@ contract DepositContract is ReentrancyGuardUpgradeable { */ uint256[10] private _gap; + /** + * @dev Initializer that allows to pretend that there have been already previous deposits. + * @param _depositCount Number of deposits already made + * @param _branch Branch array which contains the necessary sibilings to compute the next root when a new + */ + function initialize(uint32 _depositCount, bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] memory _branch) internal onlyInitializing { + depositCount = _depositCount; + branch = _branch; + } + /** * @notice Computes and returns the merkle root */ @@ -47,7 +57,7 @@ contract DepositContract is ReentrancyGuardUpgradeable { height++ ) { if (((size >> height) & 1) == 1) - node = keccak256(abi.encodePacked(_branch[height], node)); + node = keccak256(abi.encodePacked(branch[height], node)); else node = keccak256(abi.encodePacked(node, currentZeroHashHeight)); @@ -65,12 +75,12 @@ contract DepositContract is ReentrancyGuardUpgradeable { function _deposit(bytes32 leafHash) internal { bytes32 node = leafHash; - // Avoid overflowing the Merkle tree (and prevent edge case in computing `_branch`) + // Avoid overflowing the Merkle tree (and prevent edge case in computing `branch`) if (depositCount >= _MAX_DEPOSIT_COUNT) { revert MerkleTreeFull(); } - // Add deposit data root to Merkle tree (update a single `_branch` node) + // Add deposit data root to Merkle tree (update a single `branch` node) uint256 size = ++depositCount; for ( uint256 height = 0; @@ -78,10 +88,10 @@ contract DepositContract is ReentrancyGuardUpgradeable { height++ ) { if (((size >> height) & 1) == 1) { - _branch[height] = node; + branch[height] = node; return; } - node = keccak256(abi.encodePacked(_branch[height], node)); + node = keccak256(abi.encodePacked(branch[height], node)); } // As the loop should always end prematurely with the `return` statement, // this code should be unreachable. We assert `false` just to be safe. diff --git a/contracts/mocks/PolygonZkEVMBridgeMock.sol b/contracts/mocks/PolygonZkEVMBridgeMock.sol index 52a540650..adab79826 100644 --- a/contracts/mocks/PolygonZkEVMBridgeMock.sol +++ b/contracts/mocks/PolygonZkEVMBridgeMock.sol @@ -19,13 +19,17 @@ contract PolygonZkEVMBridgeMock is PolygonZkEVMBridgeWrapper, OwnableUpgradeable IBasePolygonZkEVMGlobalExitRoot _globalExitRootManager, address _polygonZkEVMaddress, address _gasTokenAddress, - bool _isDeployedOnL2 + bool _isDeployedOnL2, + uint32 _lastUpdatedDepositCount, + bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] memory depositBranches ) public override initializer { networkID = _networkID; globalExitRootManager = _globalExitRootManager; polygonZkEVMaddress = _polygonZkEVMaddress; gasTokenAddress = _gasTokenAddress; isDeployedOnL2= _isDeployedOnL2; + lastUpdatedDepositCount = _lastUpdatedDepositCount; + DepositContract.initialize(lastUpdatedDepositCount, depositBranches); maxEtherBridge = 0.25 ether; diff --git a/test/contracts/bridge.test.js b/test/contracts/bridge.test.js index 654c07a81..3736e988e 100644 --- a/test/contracts/bridge.test.js +++ b/test/contracts/bridge.test.js @@ -41,6 +41,7 @@ describe('PolygonZkEVMBridge Contract - for L2', () => { let polygonZkEVMBridgeContract; let tokenContract; let gasTokenContract; + const depositBranches = new Array(32).fill(ethers.constants.HashZero); beforeEach('Deploy contracts', async () => { // load signers @@ -72,6 +73,8 @@ describe('PolygonZkEVMBridge Contract - for L2', () => { polygonZkEVMAddress, gasTokenContract.address, true, + 0, + depositBranches, ); // deploy token @@ -1201,11 +1204,15 @@ describe('PolygonZkEVMBridge Contract - for L1', () => { let polygonZkEVMBridgeContract; let tokenContract; let gasTokenContract; + const depositBranches = new Array(32); beforeEach('Deploy contracts', async () => { // load signers [deployer, rollup, acc1] = await ethers.getSigners(); + for (let i = 0; i < 32; i++) { + depositBranches[i] = ethers.utils.hexlify(ethers.utils.randomBytes(32)); + } // deploy gas token const gasTokenFactory = await ethers.getContractFactory('ERC20PermitMock'); gasTokenContract = await gasTokenFactory.deploy( @@ -1232,6 +1239,8 @@ describe('PolygonZkEVMBridge Contract - for L1', () => { polygonZkEVMAddress, gasTokenContract.address, false, + 0, + depositBranches, ); // deploy token diff --git a/test/contracts/bridgeMock.test.js b/test/contracts/bridgeMock.test.js index 276ca3117..6e516e351 100644 --- a/test/contracts/bridgeMock.test.js +++ b/test/contracts/bridgeMock.test.js @@ -35,6 +35,7 @@ describe('PolygonZkEVMBridge Mock Contract', () => { const LEAF_TYPE_ASSET = 0; const polygonZkEVMAddress = ethers.constants.AddressZero; + const depositBranches = new Array(32).fill(ethers.constants.HashZero); beforeEach('Deploy contracts', async () => { // load signers @@ -65,6 +66,8 @@ describe('PolygonZkEVMBridge Mock Contract', () => { polygonZkEVMAddress, gasTokenContract.address, true, + 0, + depositBranches, ); // deploy token diff --git a/test/contracts/bridge_metadata.test.js b/test/contracts/bridge_metadata.test.js index 80da57170..24542c599 100644 --- a/test/contracts/bridge_metadata.test.js +++ b/test/contracts/bridge_metadata.test.js @@ -24,6 +24,7 @@ describe('PolygonZkEVMBridge Contract werid metadata', () => { const networkIDMainnet = 0; const networkIDRollup = 1; const LEAF_TYPE_ASSET = 0; + const depositBranches = new Array(32).fill(ethers.constants.HashZero); const polygonZkEVMAddress = ethers.constants.AddressZero; @@ -56,6 +57,8 @@ describe('PolygonZkEVMBridge Contract werid metadata', () => { polygonZkEVMAddress, gasTokenContract.address, true, + 0, + depositBranches, ); // deploy token diff --git a/test/contracts/bridge_permit.test.js b/test/contracts/bridge_permit.test.js index 75e898396..f84ffbda9 100644 --- a/test/contracts/bridge_permit.test.js +++ b/test/contracts/bridge_permit.test.js @@ -42,6 +42,7 @@ describe('PolygonZkEVMBridge Contract Permit tests', () => { const networkIDMainnet = 0; const networkIDRollup = 1; const LEAF_TYPE_ASSET = 0; + const depositBranches = new Array(32).fill(ethers.constants.HashZero); const polygonZkEVMAddress = ethers.constants.AddressZero; @@ -74,6 +75,8 @@ describe('PolygonZkEVMBridge Contract Permit tests', () => { polygonZkEVMAddress, gasTokenContract.address, true, + 0, + depositBranches, ); // deploy token diff --git a/test/contracts/emergencyManager.test.js b/test/contracts/emergencyManager.test.js index d8c936f62..521c070d8 100644 --- a/test/contracts/emergencyManager.test.js +++ b/test/contracts/emergencyManager.test.js @@ -32,6 +32,7 @@ describe('Emergency mode test', () => { const pendingStateTimeoutDefault = 10; const trustedAggregatorTimeoutDefault = 10; let firstDeployment = true; + const depositBranches = new Array(32).fill(ethers.constants.HashZero); beforeEach('Deploy contract', async () => { upgrades.silenceWarnings(); @@ -109,6 +110,8 @@ describe('Emergency mode test', () => { polygonZkEVMContract.address, gasTokenContract.address, true, + 0, + depositBranches, ); await polygonZkEVMContract.initialize( { diff --git a/test/contracts/polygonZkEVM.test.js b/test/contracts/polygonZkEVM.test.js index 2469e5709..cbee4c01a 100644 --- a/test/contracts/polygonZkEVM.test.js +++ b/test/contracts/polygonZkEVM.test.js @@ -39,6 +39,7 @@ describe('Polygon ZK-EVM', () => { const pendingStateTimeoutDefault = 100; const trustedAggregatorTimeoutDefault = 10; let firstDeployment = true; + const depositBranches = new Array(32).fill(ethers.constants.HashZero); // PolygonZkEVM Constants const FORCE_BATCH_TIMEOUT = 60 * 60 * 24 * 5; // 5 days @@ -119,6 +120,8 @@ describe('Polygon ZK-EVM', () => { polygonZkEVMContract.address, gasTokenContract.address, true, + 0, + depositBranches, ); await polygonZkEVMContract.initialize( { diff --git a/test/contracts/polygonZkEVMTestnetV2.test.js b/test/contracts/polygonZkEVMTestnetV2.test.js index fe0d90c7b..c66244483 100644 --- a/test/contracts/polygonZkEVMTestnetV2.test.js +++ b/test/contracts/polygonZkEVMTestnetV2.test.js @@ -35,6 +35,7 @@ describe('Polygon ZK-EVM TestnetV2', () => { const trustedAggregatorTimeoutDefault = 10; let firstDeployment = true; let doesNeedImplementationDeployment = true; + const depositBranches = new Array(32).fill(ethers.constants.HashZero); beforeEach('Deploy contract', async () => { upgrades.silenceWarnings(); @@ -110,6 +111,8 @@ describe('Polygon ZK-EVM TestnetV2', () => { polygonZkEVMContract.address, gasTokenContract.address, true, + 0, + depositBranches, ); await polygonZkEVMContract.initialize( {