From 0da67e779cefa58d5d6bbeebdf6462b464545499 Mon Sep 17 00:00:00 2001
From: Mullapudi Pruthvik <mullapudipruthvik@bitgo.com>
Date: Tue, 28 May 2024 15:16:09 +0530
Subject: [PATCH] feat(deploy): v1 factory contracts on holesky

Ticket: COIN-795
---
 .github/workflows/deploy_and_release.yml |   6 +
 .github/workflows/push.yml               |   2 +
 hardhat.config.ts                        |   9 +-
 package.json                             |   2 +-
 scripts/deployV1FactoryContracts.ts      | 185 +++++++++++++++++++++++
 5 files changed, 201 insertions(+), 3 deletions(-)
 create mode 100644 scripts/deployV1FactoryContracts.ts

diff --git a/.github/workflows/deploy_and_release.yml b/.github/workflows/deploy_and_release.yml
index 04a2822..723a4c1 100644
--- a/.github/workflows/deploy_and_release.yml
+++ b/.github/workflows/deploy_and_release.yml
@@ -17,6 +17,8 @@ jobs:
     - run: npm run test
       env:
         MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
+        PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT: ${{ secrets.PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT }}
+        PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP: ${{ secrets.PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP }}
         TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
         QUICKNODE_ETH_MAINNET_API_KEY: ${{ secrets.QUICKNODE_ETH_MAINNET_API_KEY }}
         QUICKNODE_ETH_HOLESKY_API_KEY: ${{ secrets.QUICKNODE_ETH_HOLESKY_API_KEY }}
@@ -59,6 +61,8 @@ jobs:
       - run: npm run deploy-test --network ${{ needs.get-network.outputs.network }}
         env:
           MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
+          PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT: ${{ secrets.PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT }}
+          PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP: ${{ secrets.PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP }}
           TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
           QUICKNODE_ETH_MAINNET_API_KEY: ${{ secrets.QUICKNODE_ETH_MAINNET_API_KEY }}
           QUICKNODE_ETH_HOLESKY_API_KEY: ${{ secrets.QUICKNODE_ETH_HOLESKY_API_KEY }}
@@ -112,6 +116,8 @@ jobs:
       - run: npm run deploy-prod --network ${{ needs.get-network.outputs.network }}
         env:
           MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
+          PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT: ${{ secrets.PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT }}
+          PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP: ${{ secrets.PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP }}
           TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
           QUICKNODE_ETH_MAINNET_API_KEY: ${{ secrets.QUICKNODE_ETH_MAINNET_API_KEY }}
           QUICKNODE_ETH_HOLESKY_API_KEY: ${{ secrets.QUICKNODE_ETH_HOLESKY_API_KEY }}
diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml
index adacd31..a481ca1 100644
--- a/.github/workflows/push.yml
+++ b/.github/workflows/push.yml
@@ -25,6 +25,8 @@ jobs:
     - run: npm run test
       env:
         MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
+        PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT: ${{ secrets.PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT }}
+        PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP: ${{ secrets.PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP }}
         TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
         QUICKNODE_ETH_MAINNET_API_KEY: ${{ secrets.QUICKNODE_ETH_MAINNET_API_KEY }}
         QUICKNODE_ETH_HOLESKY_API_KEY: ${{ secrets.QUICKNODE_ETH_HOLESKY_API_KEY }}
diff --git a/hardhat.config.ts b/hardhat.config.ts
index e45ed1a..9b2eca8 100644
--- a/hardhat.config.ts
+++ b/hardhat.config.ts
@@ -10,6 +10,8 @@ import 'hardhat-gas-reporter';
 import 'solidity-coverage';
 
 const {
+  PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT,
+  PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP,
   MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT,
   TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT,
   QUICKNODE_ETH_MAINNET_API_KEY,
@@ -51,8 +53,11 @@ const config: HardhatUserConfig = {
       accounts: [`${MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT}`]
     },
     hteth: {
-      url: `https://boldest-cosmological-mountain.ethereum-holesky.quiknode.pro/${QUICKNODE_ETH_HOLESKY_API_KEY}`,
-      accounts: [`${TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT}`]
+      url: `https://rpc.holesky.ethpandaops.io/`,
+      accounts: [
+        `${PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT}`,
+        `${PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP}`
+      ]
     },
     matic: {
       url: `https://polygon-mainnet.g.alchemyapi.io/v2/${ALCHEMY_POLYGON_API_KEY}`,
diff --git a/package.json b/package.json
index 96065ce..6deb13e 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,7 @@
   },
   "scripts": {
     "deploy-prod": "hardhat run scripts/deploy.ts --network",
-    "deploy-test": "hardhat run scripts/deploy.ts --network",
+    "deploy-test": "hardhat run scripts/deployV1FactoryContracts.ts --network",
     "test": "hardhat test",
     "coverage": "hardhat coverage",
     "solhint": "./node_modules/.bin/solhint --fix 'contracts/**/*.sol'",
diff --git a/scripts/deployV1FactoryContracts.ts b/scripts/deployV1FactoryContracts.ts
new file mode 100644
index 0000000..4cb820d
--- /dev/null
+++ b/scripts/deployV1FactoryContracts.ts
@@ -0,0 +1,185 @@
+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;
+});