diff --git a/.github/workflows/deploy_and_release.yml b/.github/workflows/deploy_and_release.yml index 1216f07..154a11c 100644 --- a/.github/workflows/deploy_and_release.yml +++ b/.github/workflows/deploy_and_release.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Use Node.js + - name: Use Node.js uses: actions/setup-node@v3 with: cache: 'npm' @@ -38,20 +38,26 @@ jobs: get-network: runs-on: ubuntu-latest needs: [lint-and-test] - outputs: + outputs: network: ${{steps.network-name.outputs.result}} + version: ${{steps.network-name.outputs.result}} steps: - name: GET NETWORK NAME - id: network-name + id: network-name uses: actions/github-script@v6 with: github-token: ${{secrets.GITHUB_TOKEN}} result-encoding: string script: | const tag = process.env.GITHUB_REF_NAME; - 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"; + const regex = (v[\d\.]+)\-(eth|hteth|matic|tmatic|bsc|tbsc|arbeth|tarbeth|opeth|topeth|zketh|tzketh)$ + const tagArray = tag.match(regex); + const network = tagArray ? tagArray[2] : "hteth"; + const version = tarArray ? tagArray[1] : "v1.0"; + return { + network: network, + version: version + }; deploy-to-test: runs-on: ubuntu-latest needs: [lint-and-test, get-network] @@ -82,7 +88,9 @@ 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 }} + ZKSYNC_EXPLORER_API_KEY: ${{ secrets.ZKSYNC_EXPLORER_API_KEY }}, + VERSION: ${{ needs.get-network.outputs.version }}, + ENV: 'TEST' - name: Update release notes uses: actions/github-script@v6 with: @@ -140,7 +148,9 @@ jobs: OPTIMISTIC_ETHERSCAN_API_KEY: ${{ secrets.OPTIMISTIC_ETHERSCAN_API_KEY }} ZKSYNC_EXPLORER_API_KEY: ${{ secrets.ZKSYNC_EXPLORER_API_KEY }} QUICKNODE_ARBITRUM_ONE_API_KEY: ${{ secrets.QUICKNODE_ARBITRUM_ONE_API_KEY }} - QUICKNODE_OPTIMISM_API_KEY: ${{ secrets.QUICKNODE_OPTIMISM_API_KEY }} + QUICKNODE_OPTIMISM_API_KEY: ${{ secrets.QUICKNODE_OPTIMISM_API_KEY }}, + VERSION: ${{ needs.get-network.outputs.version }}, + ENV: 'PROD' - name: Update release notes uses: actions/github-script@v6 with: diff --git a/hardhat.config.ts b/hardhat.config.ts index 3f8ac7c..2cfaf6e 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -27,9 +27,36 @@ const { BSCSCAN_API_KEY, ARBISCAN_API_KEY, OPTIMISTIC_ETHERSCAN_API_KEY, - ZKSYNC_EXPLORER_API_KEY + ZKSYNC_EXPLORER_API_KEY, + VERSION, + ENV } = process.env; +const version = (VERSION ? VERSION.split(".")[0] : "v1"); + +const privateKey: { [key: string]: string } = { + v4Prod: PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT ?? "", + v4Test: PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT ?? "", + v2Prod: MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT ?? "", + v2Test: TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT ?? "", + v1ProdTestWallet: PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT ?? "", + v1ProdTestForwarder: PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP ?? "", +}; + +function getPrivateKey(version : string) : string[] { + switch (version){ + case "v1": + return [privateKey["v1ProdTestWallet"], privateKey["v1ProdTestForwarder"]] + case "v2": + return ENV === "TEST" ? [privateKey["v2Test"]] : [privateKey["v2Prod"]]; + case "v4": + return ENV === "TEST" ? [privateKey["v4Test"]] : [privateKey["v4Prod"]]; + default: + console.error("Invalid Version Number or Tag"); + process.exit(1); + } +} + const config: HardhatUserConfig = { solidity: { compilers: [ @@ -67,60 +94,48 @@ const config: HardhatUserConfig = { }, eth: { url: `https://ethereum-rpc.publicnode.com`, - accounts: [`${PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT}`] + accounts: getPrivateKey(version) }, hteth: { url: `https://rpc.holesky.ethpandaops.io/`, - accounts: [`${PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP}`] + accounts: getPrivateKey(version) }, matic: { url: `https://polygon-rpc.com/`, - accounts: [`${PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT}`] + accounts: getPrivateKey(version) }, tmatic: { // https://polygon-amoy.g.alchemy.com url: `https://polygon-amoy-bor-rpc.publicnode.com`, - accounts: [`${PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT}`] + accounts: getPrivateKey(version) }, bsc: { url: `https://bsc-dataseed1.binance.org/`, - accounts: [`${PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT}`] + accounts: getPrivateKey(version) }, tbsc: { url: `https://data-seed-prebsc-1-s1.binance.org:8545/`, - accounts: [`${PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT}`] + accounts: getPrivateKey(version) }, tarbeth: { url: `${QUICKNODE_ARBITRUM_SEPOLIA_API_KEY}`, - accounts: [ - `${PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT}`, - `${PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP}` - ] + accounts: getPrivateKey(version) }, arbeth: { url: `${QUICKNODE_ARBITRUM_ONE_API_KEY}`, - accounts: [ - `${PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT}`, - `${PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP}` - ] + accounts: getPrivateKey(version) }, topeth: { url: `${QUICKNODE_OPTIMISM_SEPOLIA_API_KEY}`, - accounts: [ - `${PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT}`, - `${PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP}` - ] + accounts: getPrivateKey(version) }, opeth: { url: `${QUICKNODE_OPTIMISM_API_KEY}`, - accounts: [ - `${PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT}`, - `${PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP}` - ] + accounts: getPrivateKey(version) }, tzketh: { url: `${QUICKNODE_ZKSYNC_SEPOLIA_API_KEY}`, - accounts: [`${PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT}`] + accounts: getPrivateKey(version) } }, gasReporter: { diff --git a/scripts/deploy.ts b/scripts/deploy.ts index 0abb239..2987b73 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -1,164 +1,256 @@ -import { use } from 'chai'; import { ethers } from 'hardhat'; +import {SignerWithAddress} from "@nomiclabs/hardhat-ethers/signers"; +import {BigNumber} from "ethers"; const hre = require('hardhat'); const fs = require('fs'); -async function main() { - const output = { - walletImplementation: '', - walletFactory: '', - forwarderImplementation: '', - forwarderFactory: '' - }; - - const feeData = await ethers.provider.getFeeData(); - const gasParams = { - gasPrice: feeData.gasPrice - }; +async function selfTransferForV1(deployer : SignerWithAddress, gasPrice : BigNumber | null, totalTxnCount : number) { + const txCount = await deployer.getTransactionCount(); + console.log('Tx Count: ', txCount); + const selfTransactions = totalTxnCount - txCount; + for (let i = 0; i < selfTransactions; i++) { + const tx = await deployer.sendTransaction({ + to: deployer.address, + value: ethers.utils.parseEther('0'), + gasPrice: (gasPrice ? gasPrice : 0) + }); + await tx.wait(); + console.log(`Self transaction with nonce: ${i} complete`); + } +} - const [deployer] = await ethers.getSigners(); +function getContractNames(version : string, chainId : number) { + if (version === "v1"){ + return [ + 'WalletSimple', + 'WalletFactory', + 'Forwarder', + 'ForwarderFactory', + 'contracts/WalletSimple.sol:WalletSimple' + ] + } - let walletImplementationContractName = ''; - let walletFactoryContractName = 'WalletFactory'; - let forwarderContractName = 'Forwarder'; - let forwarderFactoryContractName = 'ForwarderFactory'; - let contractPath = `contracts/WalletSimple.sol:WalletSimple`; - const chainId = await deployer.getChainId(); switch (chainId) { - // https://chainlist.org/ - //eth + // https://chainlist.org/ + //eth case 1: - //hteth + //hteth case 17000: - walletImplementationContractName = 'WalletSimple'; - forwarderContractName = 'ForwarderV4'; - forwarderFactoryContractName = 'ForwarderFactoryV4'; - contractPath = `contracts/${walletImplementationContractName}.sol:${walletImplementationContractName}`; - break; - //matic + //matic case 137: - //tmatic + //tmatic case 80002: - walletImplementationContractName = 'WalletSimple'; - forwarderContractName = 'ForwarderV4'; - forwarderFactoryContractName = 'ForwarderFactoryV4'; - contractPath = `contracts/${walletImplementationContractName}.sol:${walletImplementationContractName}`; - break; - // bsc + // bsc case 56: - // tbsc + // tbsc case 97: - walletImplementationContractName = 'WalletSimple'; - forwarderContractName = 'ForwarderV4'; - forwarderFactoryContractName = 'ForwarderFactoryV4'; - contractPath = `contracts/${walletImplementationContractName}.sol:${walletImplementationContractName}`; - break; - // arbeth + return [ + 'WalletSimple', + 'WalletFactory', + 'ForwarderV4', + 'ForwarderFactoryV4', + 'contracts/WalletSimple.sol:WalletSimple' + ] + // arbeth case 42161: - // tarbeth + // tarbeth case 421614: - walletImplementationContractName = 'WalletSimple'; - forwarderContractName = 'Forwarder'; - forwarderFactoryContractName = 'ForwarderFactory'; - contractPath = `contracts/${walletImplementationContractName}.sol:${walletImplementationContractName}`; - break; - // opeth + // opeth case 10: - // topeth + // topeth case 11155420: - walletImplementationContractName = 'WalletSimple'; - forwarderContractName = 'Forwarder'; - forwarderFactoryContractName = 'ForwarderFactory'; - contractPath = `contracts/${walletImplementationContractName}.sol:${walletImplementationContractName}`; - break; - // zketh + return [ + 'WalletSimple', + 'WalletFactory', + 'Forwarder', + 'ForwarderFactory', + 'contracts/WalletSimple.sol:WalletSimple' + ] + // zketh case 324: - // tzketh + // tzketh case 300: - walletImplementationContractName = 'ZkethWalletSimple'; - contractPath = `contracts/coins/${walletImplementationContractName}.sol:${walletImplementationContractName}`; - break; + return [ + 'ZkethWalletSimple', + 'WalletFactory', + 'Forwarder', + 'ForwarderFactory', + 'contracts/ZkethWalletSimple.sol:ZkethWalletSimple' + ] + default: + return ["", "", "", "", ""] } +} - console.log( - 'Deployed wallet contract called: ' + walletImplementationContractName +async function main() { + const output = { + walletImplementation: '', + walletFactory: '', + forwarderImplementation: '', + forwarderFactory: '' + }; + const version = (process.env.VERSION ? process.env.VERSION.split(".")[0] : ""); + + const feeData = await ethers.provider.getFeeData(); + const gasParams = { + gasPrice: (version === "v1" ? feeData.gasPrice!.mul('2') : feeData.gasPrice) + }; + + let walletDeployer, forwarderDeployer; + if(version === "v1"){ + [walletDeployer, forwarderDeployer] = await ethers.getSigners(); + } else{ + [forwarderDeployer] = await ethers.getSigners(); + } + const chainId = 1; + + const [ + walletImplementationContractName, + walletFactoryContractName, + forwarderImplementationContractName, + forwarderFactoryContractName, + contractPath + ] = getContractNames(version, chainId); + + // Wallet Contract Deployment + if (version === "v1" && walletDeployer){ + await selfTransferForV1(walletDeployer, gasParams.gasPrice, 2); + } + const WalletImplementation = ( + version === "v1" ? + await ethers.getContractFactory( + walletImplementationContractName, + walletDeployer + ) : + await ethers.getContractFactory( + walletImplementationContractName + ) ); - const WalletSimple = await ethers.getContractFactory( - walletImplementationContractName + console.log(`Deploying ${walletImplementationContractName}....`); + const walletImplementation = await WalletImplementation.deploy(gasParams); + await walletImplementation.deployed(); + output.walletImplementation = walletImplementation.address; + console.log( + `${walletImplementationContractName} deployed at ` + + walletImplementation.address ); - const walletSimple = await WalletSimple.deploy(gasParams); - await walletSimple.deployed(); - output.walletImplementation = walletSimple.address; - console.log('WalletSimple deployed at ' + walletSimple.address); - const WalletFactory = await ethers.getContractFactory( - walletFactoryContractName + // Wallet Factory Contract Deployment + const WalletFactory = ( + version === "v1" ? + await ethers.getContractFactory( + walletFactoryContractName, + walletDeployer + ) : + await ethers.getContractFactory( + walletFactoryContractName + ) ); + console.log(`Deploying ${walletFactoryContractName}....`); const walletFactory = await WalletFactory.deploy( - walletSimple.address, - gasParams + walletImplementation.address, + gasParams ); await walletFactory.deployed(); output.walletFactory = walletFactory.address; - console.log('WalletFactory deployed at ' + walletFactory.address); - - // In case of new coins like arbeth, opeth, zketh, we need to deploy new forwarder and forwarder factory i.e. - // ForwarderV4 and ForwarderFactoryV4. - // If we have to deploy contracts for the older coins like eth, avax, polygon, we need to deploy Forwarder and ForwarderFactory - const Forwarder = await ethers.getContractFactory(forwarderContractName); - const forwarder = await Forwarder.deploy(gasParams); - await forwarder.deployed(); - output.forwarderImplementation = forwarder.address; - console.log(`${forwarderContractName} deployed at ` + forwarder.address); - - const ForwarderFactory = await ethers.getContractFactory( - forwarderFactoryContractName + console.log( + `${walletFactoryContractName} deployed at ` + walletFactory.address + ); + + //Forwarder Contract Deployment + if (version === "v1" && forwarderDeployer){ + await selfTransferForV1(forwarderDeployer, gasParams.gasPrice, 234 ); + } + const ForwarderImplementation = ( + version === "v1" ? + await ethers.getContractFactory( + forwarderImplementationContractName, + forwarderDeployer + ) : + await ethers.getContractFactory( + forwarderImplementationContractName + ) + ); + console.log(`Deploying ${forwarderImplementationContractName}....`); + const forwarderImplementation = await ForwarderImplementation.deploy( + gasParams ); + await forwarderImplementation.deployed(); + output.forwarderImplementation = forwarderImplementation.address; + console.log( + `${forwarderImplementationContractName} deployed at ` + + forwarderImplementation.address + ); + + //Forwarder Factory Contract Deployment + const ForwarderFactory = ( + version === "v1" ? + await ethers.getContractFactory( + forwarderFactoryContractName, + forwarderDeployer + ) : + await ethers.getContractFactory( + forwarderFactoryContractName + ) + ); + console.log(`Deploying ${forwarderFactoryContractName}....`); const forwarderFactory = await ForwarderFactory.deploy( - forwarder.address, - gasParams + forwarderImplementation.address, + gasParams ); await forwarderFactory.deployed(); output.forwarderFactory = forwarderFactory.address; console.log( - `${forwarderFactoryContractName} deployed at ` + forwarderFactory.address + `${forwarderFactoryContractName} deployed at ` + forwarderFactory.address ); fs.writeFileSync('output.json', JSON.stringify(output)); // Wait 5 minutes. It takes some time for the etherscan backend to index the transaction and store the contract. - console.log('Waiting for 5 minutes before verifying.....'); + console.log('Waiting for 5 minutes before verifying....'); await new Promise((r) => setTimeout(r, 1000 * 300)); // We have to wait for a minimum of 10 block confirmations before we can call the etherscan api to verify - await walletSimple.deployTransaction.wait(10); + await walletImplementation.deployTransaction.wait(10); await walletFactory.deployTransaction.wait(10); - await forwarder.deployTransaction.wait(10); + await forwarderImplementation.deployTransaction.wait(10); await forwarderFactory.deployTransaction.wait(10); console.log('Done waiting, verifying'); + // Verify Wallet Contract await verifyContract( - walletImplementationContractName, - walletSimple.address, - [], - contractPath + walletImplementationContractName, + walletImplementation.address, + [], + contractPath ); - await verifyContract('WalletFactory', walletFactory.address, [ - walletSimple.address + + // Verify Wallet Factory Contract + await verifyContract(walletFactoryContractName, walletFactory.address, [ + walletImplementation.address ]); - await verifyContract(forwarderContractName, forwarder.address, []); + + // Verify Forwarder Contract + await verifyContract( + forwarderImplementationContractName, + forwarderImplementation.address, + [] + ); + + // Verify Forwarder Factory Contract await verifyContract(forwarderFactoryContractName, forwarderFactory.address, [ - forwarder.address + forwarderImplementation.address ]); + console.log('Contracts verified'); } async function verifyContract( - contractName: string, - contractAddress: string, - constructorArguments: string[], - contract?: string + contractName: string, + contractAddress: string, + constructorArguments: string[], + contract?: string ) { try { const verifyContractArgs: { @@ -189,3 +281,4 @@ main().catch((error) => { console.error(error); process.exitCode = 1; }); + diff --git a/scripts/deployV1FactoryContracts.ts b/scripts/deployV1FactoryContracts.ts deleted file mode 100644 index 4cb820d..0000000 --- a/scripts/deployV1FactoryContracts.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { ethers } from 'hardhat'; -const hre = require('hardhat'); -const fs = require('fs'); - -async function main() { - const output = { - walletImplementation: '', - walletFactory: '', - forwarderImplementation: '', - forwarderFactory: '' - }; - - const feeData = await ethers.provider.getFeeData(); - - const [walletDeployer, forwarderDeployer] = await ethers.getSigners(); - - const gasParams = { - gasPrice: feeData.gasPrice!.mul('2') - }; - const walletTxCount = await walletDeployer.getTransactionCount(); - - console.log('Deploying wallet contracts....'); - console.log('Wallet Tx Count: ', walletTxCount); - const walletSelfTransactions = 2 - walletTxCount; - for (let i = 0; i < walletSelfTransactions; i++) { - const tx = await walletDeployer.sendTransaction({ - to: walletDeployer.address, - value: ethers.utils.parseEther('0'), - gasPrice: gasParams.gasPrice - }); - await tx.wait(); - console.log(`Self transaction with nonce: ${i} complete`); - } - - const walletImplementationContractName = 'WalletSimple'; - const walletFactoryContractName = 'WalletFactory'; - - const WalletImplementation = await ethers.getContractFactory( - walletImplementationContractName, - walletDeployer - ); - const walletImplementation = await WalletImplementation.deploy(gasParams); - await walletImplementation.deployed(); - output.walletImplementation = walletImplementation.address; - console.log( - `${walletImplementationContractName} deployed at ` + - walletImplementation.address - ); - - const WalletFactory = await ethers.getContractFactory( - walletFactoryContractName, - walletDeployer - ); - const walletFactory = await WalletFactory.deploy( - walletImplementation.address, - gasParams - ); - await walletFactory.deployed(); - output.walletFactory = walletFactory.address; - console.log( - `${walletFactoryContractName} deployed at ` + walletFactory.address - ); - - const forwarderTxCount = await forwarderDeployer.getTransactionCount(); - - console.log('Deploying forwarder contracts....'); - console.log('Forwarder Tx Count: ', forwarderTxCount); - const forwarderSelfTransactions = 234 - forwarderTxCount; - - for (let i = 0; i < forwarderSelfTransactions; i++) { - const tx = await forwarderDeployer.sendTransaction({ - to: forwarderDeployer.address, - value: ethers.utils.parseEther('0'), - gasPrice: gasParams.gasPrice - }); - await tx.wait(); - console.log(`Self transaction with nonce: ${i} complete`); - } - - const forwarderImplementationContractName = 'Forwarder'; - const forwarderFactoryContractName = 'ForwarderFactory'; - - const ForwarderImplementation = await ethers.getContractFactory( - forwarderImplementationContractName, - forwarderDeployer - ); - - const forwarderImplementation = await ForwarderImplementation.deploy( - gasParams - ); - await forwarderImplementation.deployed(); - output.forwarderImplementation = forwarderImplementation.address; - - console.log( - `${forwarderImplementationContractName} deployed at ` + - forwarderImplementation.address - ); - - const ForwarderFactory = await ethers.getContractFactory( - forwarderFactoryContractName, - forwarderDeployer - ); - - const forwarderFactory = await ForwarderFactory.deploy( - forwarderImplementation.address, - gasParams - ); - - await forwarderFactory.deployed(); - output.forwarderFactory = forwarderFactory.address; - console.log( - `${forwarderFactoryContractName} deployed at ` + forwarderFactory.address - ); - - fs.writeFileSync('output.json', JSON.stringify(output)); - - // Wait 5 minutes. It takes some time for the etherscan backend to index the transaction and store the contract. - console.log('Waiting for 5 minutes before verifying....'); - await new Promise((r) => setTimeout(r, 1000 * 300)); - - // We have to wait for a minimum of 10 block confirmations before we can call the etherscan api to verify - - await walletImplementation.deployTransaction.wait(10); - await walletFactory.deployTransaction.wait(10); - await forwarderImplementation.deployTransaction.wait(10); - await forwarderFactory.deployTransaction.wait(10); - - console.log('Done waiting, verifying'); - await verifyContract( - walletImplementationContractName, - walletImplementation.address, - [] - ); - await verifyContract('WalletFactory', walletFactory.address, [ - walletImplementation.address - ]); - - await verifyContract( - forwarderImplementationContractName, - forwarderImplementation.address, - [] - ); - - await verifyContract('ForwarderFactory', forwarderFactory.address, [ - forwarderImplementation.address - ]); - - console.log('Contracts verified'); -} - -async function verifyContract( - contractName: string, - contractAddress: string, - constructorArguments: string[], - contract?: string -) { - try { - const verifyContractArgs: { - address: string; - constructorArguments: string[]; - contract?: string; - } = { - address: contractAddress, - constructorArguments: constructorArguments - }; - - if (contract) { - verifyContractArgs.contract = contract; - } - - await hre.run('verify:verify', verifyContractArgs); - } catch (e) { - // @ts-ignore - // We get a failure API response if the source code has already been uploaded, don't throw in this case. - if (!e.message.includes('Reason: Already Verified')) { - throw e; - } - } - console.log(`Verified ${contractName} on Etherscan!`); -} - -main().catch((error) => { - console.error(error); - process.exitCode = 1; -});