From e6be5f9f104beeaec3362d74a96c769587496fc5 Mon Sep 17 00:00:00 2001 From: AJ Srinivas Date: Fri, 29 Dec 2023 15:22:32 +0530 Subject: [PATCH 1/2] chore(eth-multisig-v4): add zksync config Ticket: WIN-1422 --- .github/workflows/deploy_and_release.yml | 9 +++++--- .github/workflows/push.yml | 1 + hardhat.config.ts | 29 ++++++++++++++++++++++-- scripts/deploy.ts | 6 +++++ 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/.github/workflows/deploy_and_release.yml b/.github/workflows/deploy_and_release.yml index 04a2822..cce42c1 100644 --- a/.github/workflows/deploy_and_release.yml +++ b/.github/workflows/deploy_and_release.yml @@ -26,6 +26,7 @@ jobs: BSCSCAN_API_KEY: ${{ secrets.BSCSCAN_API_KEY }} ARBISCAN_API_KEY: ${{ secrets.ARBISCAN_API_KEY }} OPTIMISTIC_ETHERSCAN_API_KEY: ${{ secrets.OPTIMISTIC_ETHERSCAN_API_KEY }} + ZKSYNC_EXPLORER_API_KEY: ${{ secrets.ZKSYNC_EXPLORER_API_KEY }} get-network: runs-on: ubuntu-latest needs: [lint-and-test] @@ -40,13 +41,13 @@ jobs: result-encoding: string script: | const tag = process.env.GITHUB_REF_NAME; - const regex = /v.*\-(eth|hteth|matic|tmatic|bsc|tbsc|arbeth|tarbeth|opeth|topeth)$/; + const regex = /v.*\-(eth|hteth|matic|tmatic|bsc|tbsc|arbeth|tarbeth|opeth|topeth|zketh|tzketh)$/; const network = tag.match(regex); return network ? network[1] : "hteth"; deploy-to-test: runs-on: ubuntu-latest needs: [lint-and-test, get-network] - if: ${{ (needs.get-network.outputs.network == 'hteth' ) || (needs.get-network.outputs.network == 'tmatic' ) || (needs.get-network.outputs.network == 'tbsc' ) || (needs.get-network.outputs.network == 'tarbeth' ) || (needs.get-network.outputs.network == 'topeth' ) }} + if: ${{ (needs.get-network.outputs.network == 'hteth' ) || (needs.get-network.outputs.network == 'tmatic' ) || (needs.get-network.outputs.network == 'tbsc' ) || (needs.get-network.outputs.network == 'tarbeth' ) || (needs.get-network.outputs.network == 'topeth' ) || (needs.get-network.outputs.network == 'tzketh' ) }} environment: testnet steps: - uses: actions/checkout@v2 @@ -68,6 +69,7 @@ jobs: BSCSCAN_API_KEY: ${{ secrets.BSCSCAN_API_KEY }} ARBISCAN_API_KEY: ${{ secrets.ARBISCAN_API_KEY }} OPTIMISTIC_ETHERSCAN_API_KEY: ${{ secrets.OPTIMISTIC_ETHERSCAN_API_KEY }} + ZKSYNC_EXPLORER_API_KEY: ${{ secrets.ZKSYNC_EXPLORER_API_KEY }} - name: Update release notes uses: actions/github-script@v6 with: @@ -99,7 +101,7 @@ jobs: deploy-to-prod: runs-on: ubuntu-latest needs: [lint-and-test, get-network] - if: ${{ (needs.get-network.outputs.network == 'eth' ) || (needs.get-network.outputs.network == 'matic' ) || (needs.get-network.outputs.network == 'bsc' ) || (needs.get-network.outputs.network == 'arbeth' ) || (needs.get-network.outputs.network == 'opeth' ) }} + if: ${{ (needs.get-network.outputs.network == 'eth' ) || (needs.get-network.outputs.network == 'matic' ) || (needs.get-network.outputs.network == 'bsc' ) || (needs.get-network.outputs.network == 'arbeth' ) || (needs.get-network.outputs.network == 'opeth' ) || (needs.get-network.outputs.network == 'zketh' ) }} environment: mainnet steps: - uses: actions/checkout@v2 @@ -121,6 +123,7 @@ jobs: BSCSCAN_API_KEY: ${{ secrets.BSCSCAN_API_KEY }} ARBISCAN_API_KEY: ${{ secrets.ARBISCAN_API_KEY }} OPTIMISTIC_ETHERSCAN_API_KEY: ${{ secrets.OPTIMISTIC_ETHERSCAN_API_KEY }} + ZKSYNC_EXPLORER_API_KEY: ${{ secrets.ZKSYNC_EXPLORER_API_KEY }} - name: Update release notes uses: actions/github-script@v6 with: diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index adacd31..69cb8e3 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -34,4 +34,5 @@ jobs: BSCSCAN_API_KEY: ${{ secrets.BSCSCAN_API_KEY }} ARBISCAN_API_KEY: ${{ secrets.ARBISCAN_API_KEY }} OPTIMISTIC_ETHERSCAN_API_KEY: ${{ secrets.OPTIMISTIC_ETHERSCAN_API_KEY }} + ZKSYNC_EXPLORER_API_KEY: ${{ secrets.ZKSYNC_EXPLORER_API_KEY }} - run: npm run lint diff --git a/hardhat.config.ts b/hardhat.config.ts index e45ed1a..93d92ee 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -16,12 +16,14 @@ const { QUICKNODE_ETH_HOLESKY_API_KEY, QUICKNODE_ARBITRUM_SEPOLIA_API_KEY, QUICKNODE_OPTIMISM_SEPOLIA_API_KEY, + QUICKNODE_ZKSYNC_SEPOLIA_API_KEY, ETHERSCAN_API_KEY, ALCHEMY_POLYGON_API_KEY, POLYGONSCAN_API_KEY, BSCSCAN_API_KEY, ARBISCAN_API_KEY, - OPTIMISTIC_ETHERSCAN_API_KEY + OPTIMISTIC_ETHERSCAN_API_KEY, + ZKSYNC_EXPLORER_API_KEY } = process.env; const config: HardhatUserConfig = { @@ -78,6 +80,10 @@ const config: HardhatUserConfig = { topeth: { url: `${QUICKNODE_OPTIMISM_SEPOLIA_API_KEY}`, accounts: [`${TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT}`] + }, + tzketh: { + url: `${QUICKNODE_ZKSYNC_SEPOLIA_API_KEY}`, + accounts: [`${TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT}`] } }, gasReporter: { @@ -100,7 +106,10 @@ const config: HardhatUserConfig = { arbitrumSepolia: `${ARBISCAN_API_KEY}`, // optimism optimisticEthereum: `${OPTIMISTIC_ETHERSCAN_API_KEY}`, - optimisticSepolia: `${OPTIMISTIC_ETHERSCAN_API_KEY}` + optimisticSepolia: `${OPTIMISTIC_ETHERSCAN_API_KEY}`, + // zksync + zksync: `${ZKSYNC_EXPLORER_API_KEY}`, + zksyncSepolia: `${ZKSYNC_EXPLORER_API_KEY}` }, customChains: [ { @@ -126,6 +135,22 @@ const config: HardhatUserConfig = { apiURL: 'https://api-sepolia-optimistic.etherscan.io/api', browserURL: 'https://sepolia-optimism.etherscan.io' } + }, + { + network: 'zksync', + chainId: 324, + urls: { + apiURL: 'https://block-explorer-api.mainnet.zksync.io/api', + browserURL: 'https://explorer.zksync.io' + } + }, + { + network: 'zksyncSepolia', + chainId: 300, + urls: { + apiURL: 'https://block-explorer-api.sepolia.zksync.dev/api', + browserURL: 'https://sepolia.explorer.zksync.io' + } } ] }, diff --git a/scripts/deploy.ts b/scripts/deploy.ts index 4f37420..4815e45 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -48,6 +48,12 @@ async function main() { case 11155420: walletImplementationContractName = 'OpethWalletSimple'; break; + // zketh + case 324: + // tzketh + case 300: + walletImplementationContractName = 'ZkethWalletSimple'; + break; } console.log( From 353ad46b5b881f69de7ee4033c215ae7b376ffc7 Mon Sep 17 00:00:00 2001 From: AJ Srinivas Date: Fri, 29 Dec 2023 15:22:37 +0530 Subject: [PATCH 2/2] feat(eth-multisig-v4): add zketh wallet contract Ticket: WIN-1422 --- contracts/coins/ZkethWalletSimple.sol | 57 +++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 contracts/coins/ZkethWalletSimple.sol diff --git a/contracts/coins/ZkethWalletSimple.sol b/contracts/coins/ZkethWalletSimple.sol new file mode 100644 index 0000000..c688cde --- /dev/null +++ b/contracts/coins/ZkethWalletSimple.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.20; +import '../Forwarder.sol'; +import '../ERC20Interface.sol'; +import '../WalletSimple.sol'; + +/** + * + * WalletSimple + * ============ + * + * Basic multi-signer wallet designed for use in a co-signing environment where 2 signatures are required to move funds. + * Typically used in a 2-of-3 signing configuration. Uses ecrecover to allow for 2 signatures in a single transaction. + * + * The first signature is created on the operation hash (see Data Formats) and passed to sendMultiSig/sendMultiSigToken + * The signer is determined by verifyMultiSig(). + * + * The second signature is created by the submitter of the transaction and determined by msg.signer. + * + * Data Formats + * ============ + * + * The signature is created with ethereumjs-util.ecsign(operationHash). + * Like the eth_sign RPC call, it packs the values as a 65-byte array of [r, s, v]. + * Unlike eth_sign, the message is not prefixed. + * + * The operationHash the result of keccak256(prefix, toAddress, value, data, expireTime). + * For ether transactions, `prefix` is chain id of the coin i.e. for zkSync mainnet it is "324" + * For token transaction, `prefix` is "324-ERC20" and `data` is the tokenContractAddress. + * + * + */ +contract ZkethWalletSimple is WalletSimple { + /** + * Get the network identifier that signers must sign over + * This provides protection signatures being replayed on other chains + */ + function getNetworkId() internal view override returns (string memory) { + return Strings.toString(block.chainid); + } + + /** + * Get the network identifier that signers must sign over for token transfers + * This provides protection signatures being replayed on other chains + */ + function getTokenNetworkId() internal view override returns (string memory) { + return string.concat(Strings.toString(block.chainid), '-ERC20'); + } + + /** + * Get the network identifier that signers must sign over for batch transfers + * This provides protection signatures being replayed on other chains + */ + function getBatchNetworkId() internal view override returns (string memory) { + return string.concat(Strings.toString(block.chainid), '-Batch'); + } +}