From 48c1051cea668b2f1d18b79fdff14ec2a77b657f Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Mon, 27 Nov 2023 13:53:12 +1000 Subject: [PATCH 01/40] Support ledger & switch to ts --- package.json | 2 + scripts/bootstrap/.env.example | 8 + ...loyer_funding.js => 1_deployer_funding.ts} | 40 +-- scripts/bootstrap/2_deployment_validation.js | 60 ---- scripts/bootstrap/2_deployment_validation.ts | 62 ++++ ...ld_deployment.js => 3_child_deployment.ts} | 6 +- ...oot_deployment.js => 4_root_deployment.ts} | 6 +- ...alisation.js => 5_child_initialisation.ts} | 6 +- .../{6_imx_burning.js => 6_imx_burning.ts} | 38 +-- ...mx_rebalancing.js => 7_imx_rebalancing.ts} | 38 +-- ...ialisation.js => 8_root_initialisation.ts} | 6 +- scripts/bootstrap/README.md | 26 +- scripts/deploy/.env.example | 6 + scripts/deploy/README.md | 8 +- ...hild_deployment.js => child_deployment.ts} | 80 ++--- ...tialisation.js => child_initialisation.ts} | 66 ++-- scripts/deploy/deployAndInit.js | 14 - scripts/deploy/deployAndInit.ts | 14 + ...{root_deployment.js => root_deployment.ts} | 69 ++-- ...itialisation.js => root_initialisation.ts} | 126 ++++---- scripts/e2e/{e2e.js => e2e.ts} | 146 ++++----- scripts/helpers/helpers.js | 66 ---- scripts/helpers/helpers.ts | 90 ++++++ scripts/helpers/ledger_signer.ts | 145 +++++++++ .../{axelar_setup.js => axelar_setup.ts} | 46 +-- ...ldchain.config.js => childchain.config.ts} | 7 +- ...hildchain_setup.js => childchain_setup.ts} | 16 +- scripts/localdev/ci.sh | 2 +- scripts/localdev/deploy.sh | 14 +- ...ootchain.config.js => rootchain.config.ts} | 9 +- ...{rootchain_setup.js => rootchain_setup.ts} | 51 ++- scripts/localdev/start.sh | 14 +- tsconfig.json | 11 + yarn.lock | 302 +++++++++++++++++- 34 files changed, 1041 insertions(+), 559 deletions(-) rename scripts/bootstrap/{1_deployer_funding.js => 1_deployer_funding.ts} (62%) delete mode 100644 scripts/bootstrap/2_deployment_validation.js create mode 100644 scripts/bootstrap/2_deployment_validation.ts rename scripts/bootstrap/{3_child_deployment.js => 3_child_deployment.ts} (55%) rename scripts/bootstrap/{4_root_deployment.js => 4_root_deployment.ts} (55%) rename scripts/bootstrap/{5_child_initialisation.js => 5_child_initialisation.ts} (56%) rename scripts/bootstrap/{6_imx_burning.js => 6_imx_burning.ts} (74%) rename scripts/bootstrap/{7_imx_rebalancing.js => 7_imx_rebalancing.ts} (88%) rename scripts/bootstrap/{8_root_initialisation.js => 8_root_initialisation.ts} (56%) rename scripts/deploy/{child_deployment.js => child_deployment.ts} (52%) rename scripts/deploy/{child_initialisation.js => child_initialisation.ts} (52%) delete mode 100644 scripts/deploy/deployAndInit.js create mode 100644 scripts/deploy/deployAndInit.ts rename scripts/deploy/{root_deployment.js => root_deployment.ts} (51%) rename scripts/deploy/{root_initialisation.js => root_initialisation.ts} (54%) rename scripts/e2e/{e2e.js => e2e.ts} (80%) delete mode 100644 scripts/helpers/helpers.js create mode 100644 scripts/helpers/helpers.ts create mode 100644 scripts/helpers/ledger_signer.ts rename scripts/localdev/{axelar_setup.js => axelar_setup.ts} (80%) rename scripts/localdev/{childchain.config.js => childchain.config.ts} (60%) rename scripts/localdev/{childchain_setup.js => childchain_setup.ts} (62%) rename scripts/localdev/{rootchain.config.js => rootchain.config.ts} (60%) rename scripts/localdev/{rootchain_setup.js => rootchain_setup.ts} (63%) create mode 100644 tsconfig.json diff --git a/package.json b/package.json index 98ec7265..b4e5ca0a 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,8 @@ "@axelar-network/axelar-local-dev": "^2.1.1-alpha.2", "@axelar-network/axelarjs-sdk": "^0.12.8", "@ethersproject/hardware-wallets": "^5.7.0", + "@ledgerhq/hw-app-eth": "^6.35.0", + "@ledgerhq/hw-transport-node-hid": "^6.28.0", "@openzeppelin/contracts": "^4.5.0", "axios": "^0.27.2", "bip39": "^3.0.4", diff --git a/scripts/bootstrap/.env.example b/scripts/bootstrap/.env.example index ad143850..1806dcaf 100644 --- a/scripts/bootstrap/.env.example +++ b/scripts/bootstrap/.env.example @@ -9,18 +9,26 @@ ROOT_CHAIN_ID= CHILD_ADMIN_ADDR= ## The private key for the admin EOA or "ledger" if using hardware wallet. CHILD_ADMIN_EOA_SECRET= +## The ledger index for the admin EOA, required if using ledger. +CHILD_ADMIN_EOA_LEDGER_INDEX= ## The deployer address on child chain. CHILD_DEPLOYER_ADDR= ## The private key for the deployer on child chain or "ledger" if using hardware wallet. CHILD_DEPLOYER_SECRET= +## The ledger index for the deployer on child chain, required if using ledger. +CHILD_DEPLOYER_LEDGER_INDEX= ## The amount of fund deployer required on L2, unit is in IMX or 10^18 Wei. CHILD_DEPLOYER_FUND= ## The deployer address on root chain. ROOT_DEPLOYER_ADDR= ## The private key for the deployer on root chain or "ledger" if using hardware wallet. ROOT_DEPLOYER_SECRET= +## The ledger index for the deployer on root chain, required if using ledger. +ROOT_DEPLOYER_LEDGER_INDEX= ## The private key for rate admin or "ledger" if using hardware wallet. ROOT_BRIDGE_RATE_ADMIN_SECRET= +## The ledger index for the rate admin, required if using ledger. +ROOT_BRIDGE_RATE_ADMIN_LEDGER_INDEX= ## The IMX token address on root chain. ROOT_IMX_ADDR= ## The Wrapped ETH token address on the root chain. diff --git a/scripts/bootstrap/1_deployer_funding.js b/scripts/bootstrap/1_deployer_funding.ts similarity index 62% rename from scripts/bootstrap/1_deployer_funding.js rename to scripts/bootstrap/1_deployer_funding.ts index 7266624f..8e3b1341 100644 --- a/scripts/bootstrap/1_deployer_funding.js +++ b/scripts/bootstrap/1_deployer_funding.ts @@ -1,27 +1,29 @@ // Deployer funding -'use strict'; -require('dotenv').config(); -const { ethers } = require("ethers"); -const helper = require("../helpers/helpers.js"); -const { LedgerSigner } = require('@ethersproject/hardware-wallets') +import * as dotenv from "dotenv"; +dotenv.config(); +import { ethers } from "ethers"; +import { requireEnv, waitForConfirmation, waitForReceipt, getFee, hasDuplicates } from "../helpers/helpers"; +import { LedgerSigner } from "../helpers/ledger_signer"; async function run() { console.log("=======Start Deployer Funding======="); // Check environment variables - let childRPCURL = helper.requireEnv("CHILD_RPC_URL"); - let childChainID = helper.requireEnv("CHILD_CHAIN_ID"); - let adminEOASecret = helper.requireEnv("CHILD_ADMIN_EOA_SECRET"); - let axelarEOA = helper.requireEnv("AXELAR_EOA"); - let axelarFund = helper.requireEnv("AXELAR_FUND"); - let deployerEOA = helper.requireEnv("CHILD_DEPLOYER_ADDR"); - let deployerFund = helper.requireEnv("CHILD_DEPLOYER_FUND"); + let childRPCURL = requireEnv("CHILD_RPC_URL"); + let childChainID = requireEnv("CHILD_CHAIN_ID"); + let adminEOASecret = requireEnv("CHILD_ADMIN_EOA_SECRET"); + let axelarEOA = requireEnv("AXELAR_EOA"); + let axelarFund = requireEnv("AXELAR_FUND"); + let deployerEOA = requireEnv("CHILD_DEPLOYER_ADDR"); + let deployerFund = requireEnv("CHILD_DEPLOYER_FUND"); // Get admin EOA address const childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); let adminWallet; if (adminEOASecret == "ledger") { - adminWallet = new LedgerSigner(childProvider); + let index = requireEnv("CHILD_ADMIN_EOA_LEDGER_INDEX"); + const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; + adminWallet = new LedgerSigner(childProvider, derivationPath); } else { adminWallet = new ethers.Wallet(adminEOASecret, childProvider); } @@ -29,7 +31,7 @@ async function run() { console.log("Admin address is: ", adminAddr); // Check duplicates - if (helper.hasDuplicates([adminAddr, axelarEOA, deployerEOA])) { + if (hasDuplicates([adminAddr, axelarEOA, deployerEOA])) { throw("Duplicate address detected!"); } @@ -37,9 +39,9 @@ async function run() { console.log("Axelar EOA now has: ", ethers.utils.formatEther(await childProvider.getBalance(axelarEOA))); console.log("Deployer EOA now has: ", ethers.utils.formatEther(await childProvider.getBalance(deployerEOA))); console.log("Fund Axelar and deployer on child chain in..."); - await helper.waitForConfirmation(); + await waitForConfirmation(); - let [priorityFee, maxFee] = await helper.getFee(adminWallet); + let [priorityFee, maxFee] = await getFee(childProvider); console.log("Transfer value to axelar..."); let resp = await adminWallet.sendTransaction({ to: axelarEOA, @@ -48,9 +50,9 @@ async function run() { maxFeePerGas: maxFee, }) console.log("Transaction submitted: " + JSON.stringify(resp, null, 2)); - await helper.waitForReceipt(resp.hash, childProvider); + await waitForReceipt(resp.hash, childProvider); - [priorityFee, maxFee] = await helper.getFee(adminWallet); + [priorityFee, maxFee] = await getFee(childProvider); console.log("Transfer value to deployer..."); resp = await adminWallet.sendTransaction({ to: deployerEOA, @@ -59,7 +61,7 @@ async function run() { maxFeePerGas: maxFee, }) console.log("Transaction submitted: " + JSON.stringify(resp, null, 2)); - await helper.waitForReceipt(resp.hash, childProvider); + await waitForReceipt(resp.hash, childProvider); // Print target balance console.log("Axelar EOA now has: ", ethers.utils.formatEther(await childProvider.getBalance(axelarEOA))); diff --git a/scripts/bootstrap/2_deployment_validation.js b/scripts/bootstrap/2_deployment_validation.js deleted file mode 100644 index ea763836..00000000 --- a/scripts/bootstrap/2_deployment_validation.js +++ /dev/null @@ -1,60 +0,0 @@ -// Deployment validation -'use strict'; -require('dotenv').config(); -const { ethers } = require("ethers"); -const helper = require("../helpers/helpers.js"); - -async function run() { - console.log("=======Start Deployment Validation======="); - - // Check environment variables - let childRPCURL = helper.requireEnv("CHILD_RPC_URL"); - let childChainID = helper.requireEnv("CHILD_CHAIN_ID"); - let rootRPCURL = helper.requireEnv("ROOT_RPC_URL"); - let rootChainID = helper.requireEnv("ROOT_CHAIN_ID"); - let childGatewayAddr = helper.requireEnv("CHILD_GATEWAY_ADDRESS"); - let childGasServiceAddr = helper.requireEnv("CHILD_GAS_SERVICE_ADDRESS"); - let multisigAddr = helper.requireEnv("MULTISIG_CONTRACT_ADDRESS"); - let rootGatewayAddr = helper.requireEnv("ROOT_GATEWAY_ADDRESS"); - let rootGasService = helper.requireEnv("ROOT_GAS_SERVICE_ADDRESS"); - - // Check duplicates - if (helper.hasDuplicates([childGatewayAddr, childGasServiceAddr, multisigAddr])) { - throw("Duplicate address detected!"); - } - if (helper.hasDuplicates([rootGatewayAddr, rootGasService])) { - throw("Duplicate address detected!"); - } - - const childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); - const rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); - - // Check child chain. - console.log("Check contracts on child chain..."); - console.log("Check gateway contract...") - await helper.requireNonEmptyCode(childProvider, childGatewayAddr); - console.log("Succeed."); - console.log("Check gas service contract...") - await helper.requireNonEmptyCode(childProvider, childGasServiceAddr); - console.log("Succeed."); - if (process.env["SKIP_MULTISIG_CHECK"] != null) { - console.log("Skip multisig contract check..."); - } else { - console.log("Check multisig contract..."); - await helper.requireNonEmptyCode(childProvider, multisigAddr); - console.log("Succeed."); - } - - // Check root chain. - console.log("Check contracts on root chain..."); - console.log("Check gateway contract..."); - await helper.requireNonEmptyCode(rootProvider, rootGatewayAddr); - console.log("Succeed."); - console.log("Check gas service contract..."); - await helper.requireNonEmptyCode(rootProvider, rootGasService); - console.log("Succeed."); - - console.log("=======End Deployment Validation======="); -} - -run(); \ No newline at end of file diff --git a/scripts/bootstrap/2_deployment_validation.ts b/scripts/bootstrap/2_deployment_validation.ts new file mode 100644 index 00000000..b6b2835b --- /dev/null +++ b/scripts/bootstrap/2_deployment_validation.ts @@ -0,0 +1,62 @@ +// Deployment validation +import * as dotenv from "dotenv"; +dotenv.config(); +import { ethers } from "ethers"; +import { requireEnv, hasDuplicates, requireNonEmptyCode } from "../helpers/helpers"; +import { LedgerSigner } from "../helpers/ledger_signer"; +import * as fs from "fs"; + +async function run() { + console.log("=======Start Deployment Validation======="); + + // Check environment variables + let childRPCURL = requireEnv("CHILD_RPC_URL"); + let childChainID = requireEnv("CHILD_CHAIN_ID"); + let rootRPCURL = requireEnv("ROOT_RPC_URL"); + let rootChainID = requireEnv("ROOT_CHAIN_ID"); + let childGatewayAddr = requireEnv("CHILD_GATEWAY_ADDRESS"); + let childGasServiceAddr = requireEnv("CHILD_GAS_SERVICE_ADDRESS"); + let multisigAddr = requireEnv("MULTISIG_CONTRACT_ADDRESS"); + let rootGatewayAddr = requireEnv("ROOT_GATEWAY_ADDRESS"); + let rootGasService = requireEnv("ROOT_GAS_SERVICE_ADDRESS"); + + // Check duplicates + if (hasDuplicates([childGatewayAddr, childGasServiceAddr, multisigAddr])) { + throw("Duplicate address detected!"); + } + if (hasDuplicates([rootGatewayAddr, rootGasService])) { + throw("Duplicate address detected!"); + } + + const childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); + const rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); + + // Check child chain. + console.log("Check contracts on child chain..."); + console.log("Check gateway contract...") + await requireNonEmptyCode(childProvider, childGatewayAddr); + console.log("Succeed."); + console.log("Check gas service contract...") + await requireNonEmptyCode(childProvider, childGasServiceAddr); + console.log("Succeed."); + if (process.env["SKIP_MULTISIG_CHECK"] != null) { + console.log("Skip multisig contract check..."); + } else { + console.log("Check multisig contract..."); + await requireNonEmptyCode(childProvider, multisigAddr); + console.log("Succeed."); + } + + // Check root chain. + console.log("Check contracts on root chain..."); + console.log("Check gateway contract..."); + await requireNonEmptyCode(rootProvider, rootGatewayAddr); + console.log("Succeed."); + console.log("Check gas service contract..."); + await requireNonEmptyCode(rootProvider, rootGasService); + console.log("Succeed."); + + console.log("=======End Deployment Validation======="); +} + +run(); \ No newline at end of file diff --git a/scripts/bootstrap/3_child_deployment.js b/scripts/bootstrap/3_child_deployment.ts similarity index 55% rename from scripts/bootstrap/3_child_deployment.js rename to scripts/bootstrap/3_child_deployment.ts index 509ff107..37af73e2 100644 --- a/scripts/bootstrap/3_child_deployment.js +++ b/scripts/bootstrap/3_child_deployment.ts @@ -1,12 +1,10 @@ // Deploy child contracts -'use strict'; -require('dotenv').config(); -const deploy = require("../deploy/child_deployment.js"); +import { deployChildContracts } from "../deploy/child_deployment"; async function run() { console.log("=======Start Child Deployment======="); - await deploy.deployChildContracts(); + await deployChildContracts(); console.log("=======End Child Deployment======="); } diff --git a/scripts/bootstrap/4_root_deployment.js b/scripts/bootstrap/4_root_deployment.ts similarity index 55% rename from scripts/bootstrap/4_root_deployment.js rename to scripts/bootstrap/4_root_deployment.ts index 48ea2d4f..546aaf11 100644 --- a/scripts/bootstrap/4_root_deployment.js +++ b/scripts/bootstrap/4_root_deployment.ts @@ -1,12 +1,10 @@ // Deploy root contracts -'use strict'; -require('dotenv').config(); -const deploy = require("../deploy/root_deployment.js"); +import { deployRootContracts } from "../deploy/root_deployment"; async function run() { console.log("=======Start Root Deployment======="); - await deploy.deployRootContracts(); + await deployRootContracts(); console.log("=======End Root Deployment======="); } diff --git a/scripts/bootstrap/5_child_initialisation.js b/scripts/bootstrap/5_child_initialisation.ts similarity index 56% rename from scripts/bootstrap/5_child_initialisation.js rename to scripts/bootstrap/5_child_initialisation.ts index 09fc59e6..47fa9c60 100644 --- a/scripts/bootstrap/5_child_initialisation.js +++ b/scripts/bootstrap/5_child_initialisation.ts @@ -1,12 +1,10 @@ // Initialise child contracts -'use strict'; -require('dotenv').config(); -const init = require("../deploy/child_initialisation.js"); +import { initialiseChildContracts } from "../deploy/child_initialisation"; async function run() { console.log("=======Start Child Initialisation======="); - await init.initialiseChildContracts(); + await initialiseChildContracts(); console.log("=======End Child Initialisation======="); } diff --git a/scripts/bootstrap/6_imx_burning.js b/scripts/bootstrap/6_imx_burning.ts similarity index 74% rename from scripts/bootstrap/6_imx_burning.js rename to scripts/bootstrap/6_imx_burning.ts index 57e0bfb0..d67ddbd4 100644 --- a/scripts/bootstrap/6_imx_burning.js +++ b/scripts/bootstrap/6_imx_burning.ts @@ -1,20 +1,20 @@ // IMX burning -'use strict'; -require('dotenv').config(); -const { ethers } = require("ethers"); -const helper = require("../helpers/helpers.js"); -const { LedgerSigner } = require('@ethersproject/hardware-wallets') -const fs = require('fs'); +import * as dotenv from "dotenv"; +dotenv.config(); +import { ethers } from "ethers"; +import { requireEnv, waitForConfirmation, hasDuplicates, waitForReceipt, getFee } from "../helpers/helpers"; +import { LedgerSigner } from "../helpers/ledger_signer"; +import * as fs from "fs"; async function run() { console.log("=======Start IMX Burning======="); // Check environment variables - let childRPCURL = helper.requireEnv("CHILD_RPC_URL"); - let childChainID = helper.requireEnv("CHILD_CHAIN_ID"); - let adminEOASecret = helper.requireEnv("CHILD_ADMIN_EOA_SECRET"); - let multisigAddr = helper.requireEnv("MULTISIG_CONTRACT_ADDRESS"); - let imxDepositLimit = helper.requireEnv("IMX_DEPOSIT_LIMIT"); + let childRPCURL = requireEnv("CHILD_RPC_URL"); + let childChainID = requireEnv("CHILD_CHAIN_ID"); + let adminEOASecret = requireEnv("CHILD_ADMIN_EOA_SECRET"); + let multisigAddr = requireEnv("MULTISIG_CONTRACT_ADDRESS"); + let imxDepositLimit = requireEnv("IMX_DEPOSIT_LIMIT"); // Read from contract file. let data = fs.readFileSync(".child.bridge.contracts.json", 'utf-8'); @@ -25,7 +25,9 @@ async function run() { const childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); let adminWallet; if (adminEOASecret == "ledger") { - adminWallet = new LedgerSigner(childProvider); + let index = requireEnv("CHILD_ADMIN_EOA_LEDGER_INDEX"); + const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; + adminWallet = new LedgerSigner(childProvider, derivationPath); } else { adminWallet = new ethers.Wallet(adminEOASecret, childProvider); } @@ -33,7 +35,7 @@ async function run() { console.log("Admin address is: ", adminAddr); // Check duplicates - if (helper.hasDuplicates([adminAddr, childBridgeAddr, multisigAddr])) { + if (hasDuplicates([adminAddr, childBridgeAddr, multisigAddr])) { throw("Duplicate address detected!"); } @@ -51,20 +53,20 @@ async function run() { } console.log("Burn IMX in..."); - await helper.waitForConfirmation(); + await waitForConfirmation(); let childBridgeObj = JSON.parse(fs.readFileSync('../../out/ChildERC20Bridge.sol/ChildERC20Bridge.json', 'utf8')); let childBridge = new ethers.Contract(childBridgeAddr, childBridgeObj.abi, childProvider); console.log("Transfer " + imxDepositLimit + " IMX to child bridge..."); - let [priorityFee, maxFee] = await helper.getFee(adminWallet); + let [priorityFee, maxFee] = await getFee(childProvider); let resp = await childBridge.connect(adminWallet).privilegedDeposit({ value: ethers.utils.parseEther(imxDepositLimit), maxPriorityFeePerGas: priorityFee, maxFeePerGas: maxFee, }) console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)) - await helper.waitForReceipt(resp.hash, childProvider); + await waitForReceipt(resp.hash, childProvider); adminBal = await childProvider.getBalance(adminAddr); bridgeBal = await childProvider.getBalance(childBridgeAddr); @@ -75,7 +77,7 @@ async function run() { // Transfer to multisig console.log("Transfer remaining to multisig..."); - [priorityFee, maxFee] = await helper.getFee(adminWallet); + [priorityFee, maxFee] = await getFee(childProvider); resp = await adminWallet.sendTransaction({ to: multisigAddr, value: adminBal.sub(ethers.utils.parseEther("0.01")), @@ -83,7 +85,7 @@ async function run() { maxFeePerGas: maxFee, }); console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)) - await helper.waitForReceipt(resp.hash, childProvider); + await waitForReceipt(resp.hash, childProvider); adminBal = await childProvider.getBalance(adminAddr); bridgeBal = await childProvider.getBalance(childBridgeAddr); diff --git a/scripts/bootstrap/7_imx_rebalancing.js b/scripts/bootstrap/7_imx_rebalancing.ts similarity index 88% rename from scripts/bootstrap/7_imx_rebalancing.js rename to scripts/bootstrap/7_imx_rebalancing.ts index e2bf463c..01f7d805 100644 --- a/scripts/bootstrap/7_imx_rebalancing.js +++ b/scripts/bootstrap/7_imx_rebalancing.ts @@ -1,10 +1,10 @@ // IMX rebalancing -'use strict'; -require('dotenv').config(); -const { ethers } = require("ethers"); -const helper = require("../helpers/helpers.js"); -const { LedgerSigner } = require('@ethersproject/hardware-wallets') -const fs = require('fs'); +import * as dotenv from "dotenv"; +dotenv.config(); +import { ethers } from "ethers"; +import { requireEnv, waitForConfirmation, waitForReceipt, getFee, hasDuplicates } from "../helpers/helpers"; +import { LedgerSigner } from "../helpers/ledger_signer"; +import * as fs from "fs"; // The total supply of IMX const TOTAL_SUPPLY = "2000000000"; @@ -16,13 +16,13 @@ async function run() { console.log("=======Start IMX Rebalancing======="); // Check environment variables - let childRPCURL = helper.requireEnv("CHILD_RPC_URL"); - let childChainID = helper.requireEnv("CHILD_CHAIN_ID"); - let rootRPCURL = helper.requireEnv("ROOT_RPC_URL"); - let rootChainID = helper.requireEnv("ROOT_CHAIN_ID"); - let rootDeployerSecret = helper.requireEnv("ROOT_DEPLOYER_SECRET"); - let multisigAddr = helper.requireEnv("MULTISIG_CONTRACT_ADDRESS"); - let rootIMXAddr = helper.requireEnv("ROOT_IMX_ADDR"); + let childRPCURL = requireEnv("CHILD_RPC_URL"); + let childChainID = requireEnv("CHILD_CHAIN_ID"); + let rootRPCURL = requireEnv("ROOT_RPC_URL"); + let rootChainID = requireEnv("ROOT_CHAIN_ID"); + let rootDeployerSecret = requireEnv("ROOT_DEPLOYER_SECRET"); + let multisigAddr = requireEnv("MULTISIG_CONTRACT_ADDRESS"); + let rootIMXAddr = requireEnv("ROOT_IMX_ADDR"); // Read from contract file. let data = fs.readFileSync(".child.bridge.contracts.json", 'utf-8'); @@ -37,7 +37,9 @@ async function run() { const rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); let adminWallet; if (rootDeployerSecret == "ledger") { - adminWallet = new LedgerSigner(rootProvider); + let index = requireEnv("ROOT_DEPLOYER_LEDGER_INDEX"); + const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; + adminWallet = new LedgerSigner(rootProvider, derivationPath); } else { adminWallet = new ethers.Wallet(rootDeployerSecret, rootProvider); } @@ -45,10 +47,10 @@ async function run() { console.log("Deployer address is: ", adminAddr); // Check duplicates - if (helper.hasDuplicates([adminAddr, childBridgeAddr, multisigAddr])) { + if (hasDuplicates([adminAddr, childBridgeAddr, multisigAddr])) { throw("Duplicate address detected!"); } - if (helper.hasDuplicates([adminAddr, rootBridgeAddr, rootIMXAddr])) { + if (hasDuplicates([adminAddr, rootBridgeAddr, rootIMXAddr])) { throw("Duplicate address detected!"); } @@ -72,13 +74,13 @@ async function run() { } console.log("Rebalance in..."); - await helper.waitForConfirmation(); + await waitForConfirmation(); // Rebalancing console.log("Transfer...") let resp = await IMX.connect(adminWallet).transfer(rootBridgeAddr, balanceAmt); console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)) - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); adminL1Balance = await IMX.balanceOf(adminAddr); rootBridgeBalance = await IMX.balanceOf(rootBridgeAddr); diff --git a/scripts/bootstrap/8_root_initialisation.js b/scripts/bootstrap/8_root_initialisation.ts similarity index 56% rename from scripts/bootstrap/8_root_initialisation.js rename to scripts/bootstrap/8_root_initialisation.ts index a193b67c..3693345b 100644 --- a/scripts/bootstrap/8_root_initialisation.js +++ b/scripts/bootstrap/8_root_initialisation.ts @@ -1,12 +1,10 @@ // Initialise root contracts -'use strict'; -require('dotenv').config(); -const init = require("../deploy/root_initialisation.js"); +import { initialiseRootContracts } from "../deploy/root_initialisation"; async function run() { console.log("=======Start Root Initialisation======="); - await init.initialiseRootContracts(); + await initialiseRootContracts(); console.log("=======End Root Initialisation======="); } diff --git a/scripts/bootstrap/README.md b/scripts/bootstrap/README.md index e5ca044e..2dda4bc7 100644 --- a/scripts/bootstrap/README.md +++ b/scripts/bootstrap/README.md @@ -34,18 +34,26 @@ ROOT_CHAIN_ID= CHILD_ADMIN_ADDR= ## The private key for the admin EOA or "ledger" if using hardware wallet. CHILD_ADMIN_EOA_SECRET= +## The ledger index for the admin EOA, required if using ledger. +CHILD_ADMIN_EOA_LEDGER_INDEX= ## The deployer address on child chain. CHILD_DEPLOYER_ADDR= ## The private key for the deployer on child chain or "ledger" if using hardware wallet. CHILD_DEPLOYER_SECRET= +## The ledger index for the deployer on child chain, required if using ledger. +CHILD_DEPLOYER_LEDGER_INDEX= ## The amount of fund deployer required on L2, unit is in IMX or 10^18 Wei. CHILD_DEPLOYER_FUND= ## The deployer address on root chain. ROOT_DEPLOYER_ADDR= ## The private key for the deployer on root chain or "ledger" if using hardware wallet. ROOT_DEPLOYER_SECRET= +## The ledger index for the deployer on root chain, required if using ledger. +ROOT_DEPLOYER_LEDGER_INDEX= ## The private key for rate admin or "ledger" if using hardware wallet. ROOT_BRIDGE_RATE_ADMIN_SECRET= +## The ledger index for the rate admin, required if using ledger. +ROOT_BRIDGE_RATE_ADMIN_LEDGER_INDEX= ## The IMX token address on root chain. ROOT_IMX_ADDR= ## The Wrapped ETH token address on the root chain. @@ -141,7 +149,7 @@ RATE_LIMIT_GOG_LARGE_THRESHOLD= ``` 3. Fund deployer ``` -node 1_deployer_funding.js 2>&1 | tee bootstrap.out +npx ts-node 1_deployer_funding.ts 2>&1 | tee bootstrap.out ``` 4. Wait for Axelar to deploy & setup their system and Security team to deploy & setup multisig wallet. 5. Set the following environment variables @@ -156,32 +164,32 @@ ROOT_GAS_SERVICE_ADDRESS= If multisig is deployed: ``` -node 2_deployment_validation.js 2>&1 | tee -a bootstrap.out +npx ts-node 2_deployment_validation.ts 2>&1 | tee -a bootstrap.out ``` If multisig isn't deployed: ``` -SKIP_MULTISIG_CHECK=true node 2_deployment_validation.js 2>&1 | tee -a bootstrap.out +SKIP_MULTISIG_CHECK=true npx ts-node 2_deployment_validation.ts 2>&1 | tee -a bootstrap.out ``` 7. Deploy bridge contracts on child and root chain. ``` -node 3_child_deployment.js 2>&1 | tee -a bootstrap.out -node 4_root_deployment.js 2>&1 | tee -a bootstrap.out +npx ts-node 3_child_deployment.ts 2>&1 | tee -a bootstrap.out +npx ts-node 4_root_deployment.ts 2>&1 | tee -a bootstrap.out ``` 8. Initialise bridge contracts on child chain. ``` -node 5_child_initialisation.js 2>&1 | tee -a bootstrap.out +npx ts-node 5_child_initialisation.ts 2>&1 | tee -a bootstrap.out ``` 9. IMX Burning ``` -node 6_imx_burning.js 2>&1 | tee -a bootstrap.out +npx ts-node 6_imx_burning.ts 2>&1 | tee -a bootstrap.out ``` 10. IMX Rebalancing ``` -node 7_imx_rebalancing.js 2>&1 | tee -a bootstrap.out +npx ts-node 7_imx_rebalancing.ts 2>&1 | tee -a bootstrap.out ``` 11. Initialise bridge contracts on root chain. ``` -node 8_root_initialisation.js 2>&1 | tee -a bootstrap.out +npx ts-node 8_root_initialisation.ts 2>&1 | tee -a bootstrap.out ``` 12. Set the following environment variable ``` diff --git a/scripts/deploy/.env.example b/scripts/deploy/.env.example index dd94dd60..0fcc7293 100644 --- a/scripts/deploy/.env.example +++ b/scripts/deploy/.env.example @@ -11,10 +11,16 @@ CHILD_ADMIN_ADDR= MULTISIG_CONTRACT_ADDRESS= ## The private key for the deployer on child chain or "ledger" if using hardware wallet. CHILD_DEPLOYER_SECRET= +## The ledger index for the deployer on child chain, required if using ledger. +CHILD_DEPLOYER_LEDGER_INDEX= ## The private key for the deployer on root chain or "ledger" if using hardware wallet. ROOT_DEPLOYER_SECRET= +## The ledger index for the deployer on root chain, required if using ledger. +ROOT_DEPLOYER_LEDGER_INDEX= ## The private key for rate admin or "ledger" if using hardware wallet. ROOT_BRIDGE_RATE_ADMIN_SECRET= +## The ledger index for the rate admin, required if using ledger. +ROOT_BRIDGE_RATE_ADMIN_LEDGER_INDEX= ## The IMX token address on root chain. ROOT_IMX_ADDR= ## The Wrapped ETH token address on the root chain. diff --git a/scripts/deploy/README.md b/scripts/deploy/README.md index 6b1e2d8e..0251cc9f 100644 --- a/scripts/deploy/README.md +++ b/scripts/deploy/README.md @@ -24,10 +24,16 @@ CHILD_ADMIN_ADDR= MULTISIG_CONTRACT_ADDRESS= ## The private key for the deployer on child chain or "ledger" if using hardware wallet. CHILD_DEPLOYER_SECRET= +## The ledger index for the deployer on child chain, required if using ledger. +CHILD_DEPLOYER_LEDGER_INDEX= ## The private key for the deployer on root chain or "ledger" if using hardware wallet. ROOT_DEPLOYER_SECRET= +## The ledger index for the deployer on root chain, required if using ledger. +ROOT_DEPLOYER_LEDGER_INDEX= ## The private key for rate admin or "ledger" if using hardware wallet. ROOT_BRIDGE_RATE_ADMIN_SECRET= +## The ledger index for the rate admin, required if using ledger. +ROOT_BRIDGE_RATE_ADMIN_LEDGER_INDEX= ## The IMX token address on root chain. ROOT_IMX_ADDR= ## The Wrapped ETH token address on the root chain. @@ -128,5 +134,5 @@ ROOT_GAS_SERVICE_ADDRESS= 3. Deploy and setup contracts: ``` -node deployAndInit.js +npx ts-node deployAndInit.ts ``` \ No newline at end of file diff --git a/scripts/deploy/child_deployment.js b/scripts/deploy/child_deployment.ts similarity index 52% rename from scripts/deploy/child_deployment.js rename to scripts/deploy/child_deployment.ts index 45f47ad4..2ac569a0 100644 --- a/scripts/deploy/child_deployment.js +++ b/scripts/deploy/child_deployment.ts @@ -1,24 +1,26 @@ // Deploy child contracts -'use strict'; -require('dotenv').config(); -const { ethers } = require("ethers"); -const helper = require("../helpers/helpers.js"); -const { LedgerSigner } = require('@ethersproject/hardware-wallets') -const fs = require('fs'); +import * as dotenv from "dotenv"; +dotenv.config(); +import { ethers } from "ethers"; +import { requireEnv, waitForConfirmation, deployChildContract, waitForReceipt, getFee } from "../helpers/helpers"; +import { LedgerSigner } from "../helpers/ledger_signer"; +import * as fs from "fs"; -exports.deployChildContracts = async () => { +export async function deployChildContracts() { // Check environment variables - let childRPCURL = helper.requireEnv("CHILD_RPC_URL"); - let childChainID = helper.requireEnv("CHILD_CHAIN_ID"); - let childDeployerSecret = helper.requireEnv("CHILD_DEPLOYER_SECRET"); - let childGatewayAddr = helper.requireEnv("CHILD_GATEWAY_ADDRESS"); - let childProxyAdmin = helper.requireEnv("CHILD_PROXY_ADMIN"); + let childRPCURL = requireEnv("CHILD_RPC_URL"); + let childChainID = requireEnv("CHILD_CHAIN_ID"); + let childDeployerSecret = requireEnv("CHILD_DEPLOYER_SECRET"); + let childGatewayAddr = requireEnv("CHILD_GATEWAY_ADDRESS"); + let childProxyAdmin = requireEnv("CHILD_PROXY_ADMIN"); // Get admin address const childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); let adminWallet; if (childDeployerSecret == "ledger") { - adminWallet = new LedgerSigner(childProvider); + let index = requireEnv("CHILD_DEPLOYER_LEDGER_INDEX"); + const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; + adminWallet = new LedgerSigner(childProvider, derivationPath); } else { adminWallet = new ethers.Wallet(childDeployerSecret, childProvider); } @@ -27,87 +29,85 @@ exports.deployChildContracts = async () => { // Execute console.log("Deploy child contracts in..."); - await helper.waitForConfirmation(); + await waitForConfirmation(); // Deploy child token template - let childTokenTemplateObj = JSON.parse(fs.readFileSync('../../out/ChildERC20.sol/ChildERC20.json', 'utf8')); console.log("Deploy child token template..."); - let childTokenTemplate = await helper.deployChildContract(childTokenTemplateObj, adminWallet); + let childTokenTemplate = await deployChildContract("ChildERC20", adminWallet); console.log("Transaction submitted: ", JSON.stringify(childTokenTemplate.deployTransaction, null, 2)); - await helper.waitForReceipt(childTokenTemplate.deployTransaction.hash, childProvider); + await waitForReceipt(childTokenTemplate.deployTransaction.hash, childProvider); // Initialise template console.log("Initialise child token template..."); - let [priorityFee, maxFee] = await helper.getFee(adminWallet); + let [priorityFee, maxFee] = await getFee(childProvider); let resp = await childTokenTemplate.connect(adminWallet).initialize("000000000000000000000000000000000000007B", "TEMPLATE", "TPT", 18, { maxPriorityFeePerGas: priorityFee, maxFeePerGas: maxFee, }); console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await helper.waitForReceipt(resp.hash, childProvider); + await waitForReceipt(resp.hash, childProvider); console.log("Deployed to CHILD_TOKEN_TEMPLATE: ", childTokenTemplate.address); // Deploy wrapped IMX - let wrappedIMXObj = JSON.parse(fs.readFileSync('../../out/WIMX.sol/WIMX.json', 'utf8')); console.log("Deploy wrapped IMX..."); - let wrappedIMX = await helper.deployChildContract(wrappedIMXObj, adminWallet); + let wrappedIMX = await deployChildContract("WIMX", adminWallet); console.log("Transaction submitted: ", JSON.stringify(wrappedIMX.deployTransaction, null, 2)); - await helper.waitForReceipt(wrappedIMX.deployTransaction.hash, childProvider); + await waitForReceipt(wrappedIMX.deployTransaction.hash, childProvider); console.log("Deployed to WRAPPED_IMX_ADDRESS: ", wrappedIMX.address); // Deploy proxy admin - let proxyAdminObj = JSON.parse(fs.readFileSync('../../out/ProxyAdmin.sol/ProxyAdmin.json', 'utf8')); console.log("Deploy proxy admin..."); - let proxyAdmin = await helper.deployChildContract(proxyAdminObj, adminWallet); + let proxyAdmin = await deployChildContract("ProxyAdmin", adminWallet); console.log("Transaction submitted: ", JSON.stringify(proxyAdmin.deployTransaction, null, 2)); - await helper.waitForReceipt(proxyAdmin.deployTransaction.hash, childProvider); + await waitForReceipt(proxyAdmin.deployTransaction.hash, childProvider); // Change owner console.log("Change ownership..."); - [priorityFee, maxFee] = await helper.getFee(adminWallet); + [priorityFee, maxFee] = await getFee(childProvider); resp = await proxyAdmin.connect(adminWallet).transferOwnership(childProxyAdmin, { maxPriorityFeePerGas: priorityFee, maxFeePerGas: maxFee, }); console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await helper.waitForReceipt(resp.hash, childProvider); + await waitForReceipt(resp.hash, childProvider); console.log("Deployed to CHILD_PROXY_ADMIN: ", proxyAdmin.address); // Deploy child bridge impl - let childBridgeImplObj = JSON.parse(fs.readFileSync('../../out/ChildERC20Bridge.sol/ChildERC20Bridge.json', 'utf8')); console.log("Deploy child bridge impl..."); - let childBridgeImpl = await helper.deployChildContract(childBridgeImplObj, adminWallet); + let childBridgeImpl = await deployChildContract("ChildERC20Bridge", adminWallet); console.log("Transaction submitted: ", JSON.stringify(childBridgeImpl.deployTransaction, null, 2)); - await helper.waitForReceipt(childBridgeImpl.deployTransaction.hash, childProvider); + await waitForReceipt(childBridgeImpl.deployTransaction.hash, childProvider); console.log("Deployed to CHILD_BRIDGE_IMPL_ADDRESS: ", childBridgeImpl.address); // Deploy child bridge proxy - let childBridgeProxyObj = JSON.parse(fs.readFileSync('../../out/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json', 'utf8')); console.log("Deploy child bridge proxy..."); - let childBridgeProxy = await helper.deployChildContract(childBridgeProxyObj, adminWallet, childBridgeImpl.address, proxyAdmin.address, []); + let childBridgeProxy = await deployChildContract("TransparentUpgradeableProxy", adminWallet, childBridgeImpl.address, proxyAdmin.address, []); console.log("Transaction submitted: ", JSON.stringify(childBridgeProxy.deployTransaction, null, 2)); - await helper.waitForReceipt(childBridgeProxy.deployTransaction.hash, childProvider); + await waitForReceipt(childBridgeProxy.deployTransaction.hash, childProvider); console.log("Deployed to CHILD_BRIDGE_PROXY_ADDRESS: ", childBridgeProxy.address); // Deploy child adaptor impl - let childAdaptorImplObj = JSON.parse(fs.readFileSync('../../out/ChildAxelarBridgeAdaptor.sol/ChildAxelarBridgeAdaptor.json', 'utf8')); console.log("Deploy child adaptor impl..."); - let childAdaptorImpl = await helper.deployChildContract(childAdaptorImplObj, adminWallet, childGatewayAddr); + let childAdaptorImpl = await deployChildContract("ChildAxelarBridgeAdaptor", adminWallet, childGatewayAddr); console.log("Transaction submitted: ", JSON.stringify(childAdaptorImpl.deployTransaction, null, 2)); - await helper.waitForReceipt(childAdaptorImpl.deployTransaction.hash, childProvider); + await waitForReceipt(childAdaptorImpl.deployTransaction.hash, childProvider); console.log("Deployed to CHILD_ADAPTOR_IMPL_ADDRESS: ", childAdaptorImpl.address); // Deploy child adaptor proxy - let childAdaptorProxyObj = JSON.parse(fs.readFileSync('../../out/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json', 'utf8')); console.log("Deploy child adaptor proxy..."); - let childAdaptorProxy = await helper.deployChildContract(childAdaptorProxyObj, adminWallet, childAdaptorImpl.address, proxyAdmin.address, []); + let childAdaptorProxy = await deployChildContract("TransparentUpgradeableProxy", adminWallet, childAdaptorImpl.address, proxyAdmin.address, []); console.log("Transaction submitted: ", JSON.stringify(childAdaptorProxy.deployTransaction, null, 2)); - await helper.waitForReceipt(childAdaptorProxy.deployTransaction.hash, childProvider); + await waitForReceipt(childAdaptorProxy.deployTransaction.hash, childProvider); console.log("Deployed to CHILD_ADAPTOR_PROXY_ADDRESS: ", childAdaptorProxy.address); let contractData = { + CHILD_PROXY_ADMIN: proxyAdmin.address, + CHILD_BRIDGE_IMPL_ADDRESS: childBridgeImpl.address, + CHILD_BRIDGE_PROXY_ADDRESS: childBridgeProxy.address, CHILD_BRIDGE_ADDRESS: childBridgeProxy.address, + CHILD_ADAPTOR_IMPL_ADDRESS: childAdaptorImpl.address, + CHILD_ADAPTOR_PROXY_ADDRESS: childAdaptorProxy.address, CHILD_ADAPTOR_ADDRESS: childAdaptorProxy.address, - WRAPPED_IMX_ADDRESS: wrappedIMX.address, CHILD_TOKEN_TEMPLATE: childTokenTemplate.address, + WRAPPED_IMX_ADDRESS: wrappedIMX.address, }; fs.writeFileSync(".child.bridge.contracts.json", JSON.stringify(contractData, null, 2)); } \ No newline at end of file diff --git a/scripts/deploy/child_initialisation.js b/scripts/deploy/child_initialisation.ts similarity index 52% rename from scripts/deploy/child_initialisation.js rename to scripts/deploy/child_initialisation.ts index 2aed4ce7..996e50a7 100644 --- a/scripts/deploy/child_initialisation.js +++ b/scripts/deploy/child_initialisation.ts @@ -1,28 +1,29 @@ // Initialise child contracts 'use strict'; -require('dotenv').config(); -const { ethers } = require("ethers"); -const helper = require("../helpers/helpers.js"); -const { LedgerSigner } = require('@ethersproject/hardware-wallets') -const fs = require('fs'); +import * as dotenv from "dotenv"; +dotenv.config(); +import { ethers } from "ethers"; +import { requireEnv, waitForConfirmation, waitForReceipt, getFee, getContract } from "../helpers/helpers"; +import { LedgerSigner } from "../helpers/ledger_signer"; +import * as fs from "fs"; -exports.initialiseChildContracts = async () => { - let rootChainName = helper.requireEnv("ROOT_CHAIN_NAME"); - let childRPCURL = helper.requireEnv("CHILD_RPC_URL"); - let childChainID = helper.requireEnv("CHILD_CHAIN_ID"); - let adminEOAAddr = helper.requireEnv("CHILD_ADMIN_ADDR"); - let childBridgeDefaultAdmin = helper.requireEnv("CHILD_BRIDGE_DEFAULT_ADMIN"); - let childBridgePauser = helper.requireEnv("CHILD_BRIDGE_PAUSER"); - let childBridgeUnpauser = helper.requireEnv("CHILD_BRIDGE_UNPAUSER"); - let childBridgeAdaptorManager = helper.requireEnv("CHILD_BRIDGE_ADAPTOR_MANAGER"); - let childAdaptorDefaultAdmin = helper.requireEnv("CHILD_ADAPTOR_DEFAULT_ADMIN"); - let childAdaptorBridgeManager = helper.requireEnv("CHILD_ADAPTOR_BRIDGE_MANAGER"); - let childAdaptorGasServiceManager = helper.requireEnv("CHILD_ADAPTOR_GAS_SERVICE_MANAGER"); - let childAdaptorTargetManager = helper.requireEnv("CHILD_ADAPTOR_TARGET_MANAGER"); - let childDeployerSecret = helper.requireEnv("CHILD_DEPLOYER_SECRET"); - let childGasServiceAddr = helper.requireEnv("CHILD_GAS_SERVICE_ADDRESS"); - let multisigAddr = helper.requireEnv("MULTISIG_CONTRACT_ADDRESS"); - let rootIMXAddr = helper.requireEnv("ROOT_IMX_ADDR"); +export async function initialiseChildContracts() { + let rootChainName = requireEnv("ROOT_CHAIN_NAME"); + let childRPCURL = requireEnv("CHILD_RPC_URL"); + let childChainID = requireEnv("CHILD_CHAIN_ID"); + let adminEOAAddr = requireEnv("CHILD_ADMIN_ADDR"); + let childBridgeDefaultAdmin = requireEnv("CHILD_BRIDGE_DEFAULT_ADMIN"); + let childBridgePauser = requireEnv("CHILD_BRIDGE_PAUSER"); + let childBridgeUnpauser = requireEnv("CHILD_BRIDGE_UNPAUSER"); + let childBridgeAdaptorManager = requireEnv("CHILD_BRIDGE_ADAPTOR_MANAGER"); + let childAdaptorDefaultAdmin = requireEnv("CHILD_ADAPTOR_DEFAULT_ADMIN"); + let childAdaptorBridgeManager = requireEnv("CHILD_ADAPTOR_BRIDGE_MANAGER"); + let childAdaptorGasServiceManager = requireEnv("CHILD_ADAPTOR_GAS_SERVICE_MANAGER"); + let childAdaptorTargetManager = requireEnv("CHILD_ADAPTOR_TARGET_MANAGER"); + let childDeployerSecret = requireEnv("CHILD_DEPLOYER_SECRET"); + let childGasServiceAddr = requireEnv("CHILD_GAS_SERVICE_ADDRESS"); + let multisigAddr = requireEnv("MULTISIG_CONTRACT_ADDRESS"); + let rootIMXAddr = requireEnv("ROOT_IMX_ADDR"); // Read from contract file. let data = fs.readFileSync(".child.bridge.contracts.json", 'utf-8'); @@ -39,7 +40,9 @@ exports.initialiseChildContracts = async () => { const childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); let adminWallet; if (childDeployerSecret == "ledger") { - adminWallet = new LedgerSigner(childProvider); + let index = requireEnv("CHILD_DEPLOYER_LEDGER_INDEX"); + const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; + adminWallet = new LedgerSigner(childProvider, derivationPath); } else { adminWallet = new ethers.Wallet(childDeployerSecret, childProvider); } @@ -48,13 +51,12 @@ exports.initialiseChildContracts = async () => { // Execute console.log("Initialise child contracts in..."); - await helper.waitForConfirmation(); + await waitForConfirmation(); // Initialise child bridge - let childBridgeObj = JSON.parse(fs.readFileSync('../../out/ChildERC20Bridge.sol/ChildERC20Bridge.json', 'utf8')); console.log("Initialise child bridge..."); - let childBridge = new ethers.Contract(childBridgeAddr, childBridgeObj.abi, childProvider); - let [priorityFee, maxFee] = await helper.getFee(adminWallet); + let childBridge = getContract("ChildERC20Bridge", childBridgeAddr, childProvider); + let [priorityFee, maxFee] = await getFee(childProvider); let resp = await childBridge.connect(adminWallet).initialize( { defaultAdmin: childBridgeDefaultAdmin, @@ -73,13 +75,13 @@ exports.initialiseChildContracts = async () => { maxFeePerGas: maxFee, }); console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await helper.waitForReceipt(resp.hash, childProvider); + await waitForReceipt(resp.hash, childProvider); // Initialise child adaptor - let childAdaptorObj = JSON.parse(fs.readFileSync('../../out/ChildAxelarBridgeAdaptor.sol/ChildAxelarBridgeAdaptor.json', 'utf8')); console.log("Initialise child adaptor..."); - let childAdaptor = new ethers.Contract(childAdaptorAddr, childAdaptorObj.abi, childProvider); - [priorityFee, maxFee] = await helper.getFee(adminWallet); + let childAdaptor = getContract("ChildAxelarBridgeAdaptor", childAdaptorAddr, childProvider); + // let childAdaptor = new ethers.Contract(childAdaptorAddr, childAdaptorObj.abi, childProvider); + [priorityFee, maxFee] = await getFee(childProvider); resp = await childAdaptor.connect(adminWallet).initialize( { defaultAdmin: childAdaptorDefaultAdmin, @@ -96,5 +98,5 @@ exports.initialiseChildContracts = async () => { maxFeePerGas: maxFee, }); console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await helper.waitForReceipt(resp.hash, childProvider); + await waitForReceipt(resp.hash, childProvider); } \ No newline at end of file diff --git a/scripts/deploy/deployAndInit.js b/scripts/deploy/deployAndInit.js deleted file mode 100644 index 78243245..00000000 --- a/scripts/deploy/deployAndInit.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; -require('dotenv').config(); -const deployChild = require("./child_deployment.js"); -const initChild = require("./child_initialisation.js"); -const deployRoot = require("./root_deployment.js"); -const initRoot = require("./root_initialisation.js"); - -async function run() { - await deployChild.deployChildContracts(); - await deployRoot.deployRootContracts(); - await initChild.initialiseChildContracts(); - await initRoot.initialiseRootContracts(); -} -run(); \ No newline at end of file diff --git a/scripts/deploy/deployAndInit.ts b/scripts/deploy/deployAndInit.ts new file mode 100644 index 00000000..b34a0cd8 --- /dev/null +++ b/scripts/deploy/deployAndInit.ts @@ -0,0 +1,14 @@ +'use strict'; +require('dotenv').config(); +import { deployChildContracts } from "./child_deployment"; +import { initialiseChildContracts } from "./child_initialisation"; +import { deployRootContracts } from "./root_deployment"; +import { initialiseRootContracts } from "./root_initialisation"; + +async function run() { + await deployChildContracts(); + await deployRootContracts(); + await initialiseChildContracts(); + await initialiseRootContracts(); +} +run(); \ No newline at end of file diff --git a/scripts/deploy/root_deployment.js b/scripts/deploy/root_deployment.ts similarity index 51% rename from scripts/deploy/root_deployment.js rename to scripts/deploy/root_deployment.ts index 6363d2be..11cca6a7 100644 --- a/scripts/deploy/root_deployment.js +++ b/scripts/deploy/root_deployment.ts @@ -1,24 +1,26 @@ // Deploy root contracts -'use strict'; -require('dotenv').config(); -const { ethers } = require("ethers"); -const helper = require("../helpers/helpers.js"); -const { LedgerSigner } = require('@ethersproject/hardware-wallets') -const fs = require('fs'); +import * as dotenv from "dotenv"; +dotenv.config(); +import { ethers } from "ethers"; +import { requireEnv, waitForConfirmation, deployRootContract, waitForReceipt } from "../helpers/helpers"; +import { LedgerSigner } from "../helpers/ledger_signer"; +import * as fs from "fs"; -exports.deployRootContracts = async () => { +export async function deployRootContracts() { // Check environment variables - let rootRPCURL = helper.requireEnv("ROOT_RPC_URL"); - let rootChainID = helper.requireEnv("ROOT_CHAIN_ID"); - let rootDeployerSecret = helper.requireEnv("ROOT_DEPLOYER_SECRET"); - let rootProxyAdmin = helper.requireEnv("ROOT_PROXY_ADMIN"); - let rootGatewayAddr = helper.requireEnv("ROOT_GATEWAY_ADDRESS"); + let rootRPCURL = requireEnv("ROOT_RPC_URL"); + let rootChainID = requireEnv("ROOT_CHAIN_ID"); + let rootDeployerSecret = requireEnv("ROOT_DEPLOYER_SECRET"); + let rootProxyAdmin = requireEnv("ROOT_PROXY_ADMIN"); + let rootGatewayAddr = requireEnv("ROOT_GATEWAY_ADDRESS"); // Get admin address const rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); let adminWallet; if (rootDeployerSecret == "ledger") { - adminWallet = new LedgerSigner(rootProvider); + let index = requireEnv("ROOT_DEPLOYER_LEDGER_INDEX"); + const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; + adminWallet = new LedgerSigner(rootProvider, derivationPath); } else { adminWallet = new ethers.Wallet(rootDeployerSecret, rootProvider); } @@ -27,68 +29,67 @@ exports.deployRootContracts = async () => { // Execute console.log("Deploy root contracts in..."); - await helper.waitForConfirmation(); + await waitForConfirmation(); // Deploy root token template - let rootTokenTemplateObj = JSON.parse(fs.readFileSync('../../out/ChildERC20.sol/ChildERC20.json', 'utf8')); console.log("Deploy root token template..."); - let rootTokenTemplate = await helper.deployRootContract(rootTokenTemplateObj, adminWallet); + let rootTokenTemplate = await deployRootContract("ChildERC20", adminWallet); console.log("Transaction submitted: ", JSON.stringify(rootTokenTemplate.deployTransaction, null, 2)); - await helper.waitForReceipt(rootTokenTemplate.deployTransaction.hash, rootProvider); + await waitForReceipt(rootTokenTemplate.deployTransaction.hash, rootProvider); // Initialise template console.log("Initialise root token template..."); let resp = await rootTokenTemplate.connect(adminWallet).initialize("000000000000000000000000000000000000007B", "TEMPLATE", "TPT", 18); console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); console.log("Deployed to ROOT_TOKEN_TEMPLATE: ", rootTokenTemplate.address); // Deploy proxy admin - let proxyAdminObj = JSON.parse(fs.readFileSync('../../out/ProxyAdmin.sol/ProxyAdmin.json', 'utf8')); console.log("Deploy proxy admin..."); - let proxyAdmin = await helper.deployRootContract(proxyAdminObj, adminWallet); + let proxyAdmin = await deployRootContract("ProxyAdmin", adminWallet); console.log("Transaction submitted: ", JSON.stringify(proxyAdmin.deployTransaction, null, 2)); - await helper.waitForReceipt(proxyAdmin.deployTransaction.hash, rootProvider); + await waitForReceipt(proxyAdmin.deployTransaction.hash, rootProvider); // Change owner console.log("Change ownership...") resp = await proxyAdmin.connect(adminWallet).transferOwnership(rootProxyAdmin); console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); console.log("Deployed to ROOT_PROXY_ADMIN: ", proxyAdmin.address); // Deploy root bridge impl - let rootBridgeImplObj = JSON.parse(fs.readFileSync('../../out/RootERC20BridgeFlowRate.sol/RootERC20BridgeFlowRate.json', 'utf8')); console.log("Deploy root bridge impl..."); - let rootBridgeImpl = await helper.deployRootContract(rootBridgeImplObj, adminWallet); + let rootBridgeImpl = await deployRootContract("RootERC20BridgeFlowRate", adminWallet); console.log("Transaction submitted: ", JSON.stringify(rootBridgeImpl.deployTransaction, null, 2)); - await helper.waitForReceipt(rootBridgeImpl.deployTransaction.hash, rootProvider); + await waitForReceipt(rootBridgeImpl.deployTransaction.hash, rootProvider); console.log("Deployed to ROOT_BRIDGE_IMPL_ADDRESS: ", rootBridgeImpl.address); // Deploy root bridge proxy - let rootBridgeProxyObj = JSON.parse(fs.readFileSync('../../out/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json', 'utf8')); console.log("Deploy root bridge proxy..."); - let rootBridgeProxy = await helper.deployRootContract(rootBridgeProxyObj, adminWallet, rootBridgeImpl.address, proxyAdmin.address, []); + let rootBridgeProxy = await deployRootContract("TransparentUpgradeableProxy", adminWallet, rootBridgeImpl.address, proxyAdmin.address, []); console.log("Transaction submitted: ", JSON.stringify(rootBridgeProxy.deployTransaction, null, 2)); - await helper.waitForReceipt(rootBridgeProxy.deployTransaction.hash, rootProvider); + await waitForReceipt(rootBridgeProxy.deployTransaction.hash, rootProvider); console.log("Deployed to ROOT_BRIDGE_PROXY_ADDRESS: ", rootBridgeProxy.address); // Deploy root adaptor impl - let rootAdaptorImplObj = JSON.parse(fs.readFileSync('../../out/RootAxelarBridgeAdaptor.sol/RootAxelarBridgeAdaptor.json', 'utf8')); console.log("Deploy root adaptor impl..."); - let rootAdaptorImpl = await helper.deployRootContract(rootAdaptorImplObj, adminWallet, rootGatewayAddr); + let rootAdaptorImpl = await deployRootContract("RootAxelarBridgeAdaptor", adminWallet, rootGatewayAddr); console.log("Transaction submitted: ", JSON.stringify(rootAdaptorImpl.deployTransaction, null, 2)); - await helper.waitForReceipt(rootAdaptorImpl.deployTransaction.hash, rootProvider); + await waitForReceipt(rootAdaptorImpl.deployTransaction.hash, rootProvider); console.log("Deployed to ROOT_ADAPTOR_IMPL_ADDRESS: ", rootAdaptorImpl.address); // Deploy root adaptor proxy - let rootAdaptorProxyObj = JSON.parse(fs.readFileSync('../../out/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json', 'utf8')); console.log("Deploy root adaptor proxy..."); - let rootAdaptorProxy = await helper.deployRootContract(rootAdaptorProxyObj, adminWallet, rootAdaptorImpl.address, proxyAdmin.address, []); + let rootAdaptorProxy = await deployRootContract("TransparentUpgradeableProxy", adminWallet, rootAdaptorImpl.address, proxyAdmin.address, []); console.log("Transaction submitted: ", JSON.stringify(rootAdaptorProxy.deployTransaction, null, 2)); - await helper.waitForReceipt(rootAdaptorProxy.deployTransaction.hash, rootProvider); + await waitForReceipt(rootAdaptorProxy.deployTransaction.hash, rootProvider); console.log("Deployed to ROOT_ADAPTOR_PROXY_ADDRESS: ", rootAdaptorProxy.address); let contractData = { + ROOT_PROXY_ADMIN: rootBridgeImpl.address, + ROOT_BRIDGE_IMPL_ADDRESS: rootBridgeImpl.address, + ROOT_BRIDGE_PROXY_ADDRESS: rootBridgeProxy.address, ROOT_BRIDGE_ADDRESS: rootBridgeProxy.address, + ROOT_ADAPTOR_IMPL_ADDRESS: rootAdaptorImpl.address, + ROOT_ADAPTOR_PROXY_ADDRESS: rootAdaptorProxy.address, ROOT_ADAPTOR_ADDRESS: rootAdaptorProxy.address, ROOT_TOKEN_TEMPLATE: rootTokenTemplate.address, }; diff --git a/scripts/deploy/root_initialisation.js b/scripts/deploy/root_initialisation.ts similarity index 54% rename from scripts/deploy/root_initialisation.js rename to scripts/deploy/root_initialisation.ts index bf49cc6d..1f61002f 100644 --- a/scripts/deploy/root_initialisation.js +++ b/scripts/deploy/root_initialisation.ts @@ -1,53 +1,53 @@ // Initialise root contracts -'use strict'; -require('dotenv').config(); -const { ethers } = require("ethers"); -const helper = require("../helpers/helpers.js"); -const { LedgerSigner } = require('@ethersproject/hardware-wallets') -const fs = require('fs'); +import * as dotenv from "dotenv"; +dotenv.config(); +import { ethers } from "ethers"; +import { requireEnv, waitForConfirmation, waitForReceipt, getContract } from "../helpers/helpers"; +import { LedgerSigner } from "../helpers/ledger_signer"; +import * as fs from "fs"; -exports.initialiseRootContracts = async() => { +export async function initialiseRootContracts() { // Check environment variables - let childChainName = helper.requireEnv("CHILD_CHAIN_NAME"); - let rootRPCURL = helper.requireEnv("ROOT_RPC_URL"); - let rootChainID = helper.requireEnv("ROOT_CHAIN_ID"); - let rootDeployerSecret = helper.requireEnv("ROOT_DEPLOYER_SECRET"); - let rootRateAdminSecret = helper.requireEnv("ROOT_BRIDGE_RATE_ADMIN_SECRET"); - let rootBridgeDefaultAdmin = helper.requireEnv("ROOT_BRIDGE_DEFAULT_ADMIN"); - let rootBridgePauser = helper.requireEnv("ROOT_BRIDGE_PAUSER"); - let rootBridgeUnpauser = helper.requireEnv("ROOT_BRIDGE_UNPAUSER"); - let rootBridgeVariableManager = helper.requireEnv("ROOT_BRIDGE_VARIABLE_MANAGER"); - let rootBridgeAdaptorManager = helper.requireEnv("ROOT_BRIDGE_ADAPTOR_MANAGER"); - let rootAdaptorDefaultAdmin = helper.requireEnv("ROOT_ADAPTOR_DEFAULT_ADMIN"); - let rootAdaptorBridgeManager = helper.requireEnv("ROOT_ADAPTOR_BRIDGE_MANAGER"); - let rootAdaptorGasServiceManager = helper.requireEnv("ROOT_ADAPTOR_GAS_SERVICE_MANAGER"); - let rootAdaptorTargetManager = helper.requireEnv("ROOT_ADAPTOR_TARGET_MANAGER"); - let rootGasServiceAddr = helper.requireEnv("ROOT_GAS_SERVICE_ADDRESS"); - let rootIMXAddr = helper.requireEnv("ROOT_IMX_ADDR"); - let rootWETHAddr = helper.requireEnv("ROOT_WETH_ADDR"); - let imxDepositLimit = helper.requireEnv("IMX_DEPOSIT_LIMIT"); - let rateLimitIMXCap = helper.requireEnv("RATE_LIMIT_IMX_CAPACITY"); - let rateLimitIMXRefill = helper.requireEnv("RATE_LIMIT_IMX_REFILL_RATE"); - let rateLimitIMXLargeThreshold = helper.requireEnv("RATE_LIMIT_IMX_LARGE_THRESHOLD"); - let rateLimitETHCap = helper.requireEnv("RATE_LIMIT_ETH_CAPACITY"); - let rateLimitETHRefill = helper.requireEnv("RATE_LIMIT_ETH_REFILL_RATE"); - let rateLimitETHLargeThreshold = helper.requireEnv("RATE_LIMIT_ETH_LARGE_THRESHOLD"); - let rateLimitUSDCAddr = helper.requireEnv("RATE_LIMIT_USDC_ADDR"); - let rateLimitUSDCCap = helper.requireEnv("RATE_LIMIT_USDC_CAPACITY"); - let rateLimitUSDCRefill = helper.requireEnv("RATE_LIMIT_USDC_REFILL_RATE"); - let rateLimitUSDCLargeThreshold = helper.requireEnv("RATE_LIMIT_USDC_LARGE_THRESHOLD"); - let rateLimitGUAddr = helper.requireEnv("RATE_LIMIT_GU_ADDR"); - let rateLimitGUCap = helper.requireEnv("RATE_LIMIT_GU_CAPACITY"); - let rateLimitGURefill = helper.requireEnv("RATE_LIMIT_GU_REFILL_RATE"); - let rateLimitGULargeThreshold = helper.requireEnv("RATE_LIMIT_GU_LARGE_THRESHOLD"); - let rateLimitCheckMateAddr = helper.requireEnv("RATE_LIMIT_CHECKMATE_ADDR"); - let rateLimitCheckMateCap = helper.requireEnv("RATE_LIMIT_CHECKMATE_CAPACITY"); - let rateLimitCheckMateRefill = helper.requireEnv("RATE_LIMIT_CHECKMATE_REFILL_RATE"); - let rateLimitCheckMateLargeThreshold = helper.requireEnv("RATE_LIMIT_CHECKMATE_LARGE_THRESHOLD"); - let rateLimitGOGAddr = helper.requireEnv("RATE_LIMIT_GOG_ADDR"); - let rateLimitGOGCap = helper.requireEnv("RATE_LIMIT_GOG_CAPACITY"); - let rateLimitGOGRefill = helper.requireEnv("RATE_LIMIT_GOG_REFILL_RATE"); - let rateLimitGOGLargeThreshold = helper.requireEnv("RATE_LIMIT_GOG_LARGE_THRESHOLD"); + let childChainName = requireEnv("CHILD_CHAIN_NAME"); + let rootRPCURL = requireEnv("ROOT_RPC_URL"); + let rootChainID = requireEnv("ROOT_CHAIN_ID"); + let rootDeployerSecret = requireEnv("ROOT_DEPLOYER_SECRET"); + let rootRateAdminSecret = requireEnv("ROOT_BRIDGE_RATE_ADMIN_SECRET"); + let rootBridgeDefaultAdmin = requireEnv("ROOT_BRIDGE_DEFAULT_ADMIN"); + let rootBridgePauser = requireEnv("ROOT_BRIDGE_PAUSER"); + let rootBridgeUnpauser = requireEnv("ROOT_BRIDGE_UNPAUSER"); + let rootBridgeVariableManager = requireEnv("ROOT_BRIDGE_VARIABLE_MANAGER"); + let rootBridgeAdaptorManager = requireEnv("ROOT_BRIDGE_ADAPTOR_MANAGER"); + let rootAdaptorDefaultAdmin = requireEnv("ROOT_ADAPTOR_DEFAULT_ADMIN"); + let rootAdaptorBridgeManager = requireEnv("ROOT_ADAPTOR_BRIDGE_MANAGER"); + let rootAdaptorGasServiceManager = requireEnv("ROOT_ADAPTOR_GAS_SERVICE_MANAGER"); + let rootAdaptorTargetManager = requireEnv("ROOT_ADAPTOR_TARGET_MANAGER"); + let rootGasServiceAddr = requireEnv("ROOT_GAS_SERVICE_ADDRESS"); + let rootIMXAddr = requireEnv("ROOT_IMX_ADDR"); + let rootWETHAddr = requireEnv("ROOT_WETH_ADDR"); + let imxDepositLimit = requireEnv("IMX_DEPOSIT_LIMIT"); + let rateLimitIMXCap = requireEnv("RATE_LIMIT_IMX_CAPACITY"); + let rateLimitIMXRefill = requireEnv("RATE_LIMIT_IMX_REFILL_RATE"); + let rateLimitIMXLargeThreshold = requireEnv("RATE_LIMIT_IMX_LARGE_THRESHOLD"); + let rateLimitETHCap = requireEnv("RATE_LIMIT_ETH_CAPACITY"); + let rateLimitETHRefill = requireEnv("RATE_LIMIT_ETH_REFILL_RATE"); + let rateLimitETHLargeThreshold = requireEnv("RATE_LIMIT_ETH_LARGE_THRESHOLD"); + let rateLimitUSDCAddr = requireEnv("RATE_LIMIT_USDC_ADDR"); + let rateLimitUSDCCap = requireEnv("RATE_LIMIT_USDC_CAPACITY"); + let rateLimitUSDCRefill = requireEnv("RATE_LIMIT_USDC_REFILL_RATE"); + let rateLimitUSDCLargeThreshold = requireEnv("RATE_LIMIT_USDC_LARGE_THRESHOLD"); + let rateLimitGUAddr = requireEnv("RATE_LIMIT_GU_ADDR"); + let rateLimitGUCap = requireEnv("RATE_LIMIT_GU_CAPACITY"); + let rateLimitGURefill = requireEnv("RATE_LIMIT_GU_REFILL_RATE"); + let rateLimitGULargeThreshold = requireEnv("RATE_LIMIT_GU_LARGE_THRESHOLD"); + let rateLimitCheckMateAddr = requireEnv("RATE_LIMIT_CHECKMATE_ADDR"); + let rateLimitCheckMateCap = requireEnv("RATE_LIMIT_CHECKMATE_CAPACITY"); + let rateLimitCheckMateRefill = requireEnv("RATE_LIMIT_CHECKMATE_REFILL_RATE"); + let rateLimitCheckMateLargeThreshold = requireEnv("RATE_LIMIT_CHECKMATE_LARGE_THRESHOLD"); + let rateLimitGOGAddr = requireEnv("RATE_LIMIT_GOG_ADDR"); + let rateLimitGOGCap = requireEnv("RATE_LIMIT_GOG_CAPACITY"); + let rateLimitGOGRefill = requireEnv("RATE_LIMIT_GOG_REFILL_RATE"); + let rateLimitGOGLargeThreshold = requireEnv("RATE_LIMIT_GOG_LARGE_THRESHOLD"); // Read from contract file. let data = fs.readFileSync(".child.bridge.contracts.json", 'utf-8'); @@ -64,7 +64,9 @@ exports.initialiseRootContracts = async() => { const rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); let adminWallet; if (rootDeployerSecret == "ledger") { - adminWallet = new LedgerSigner(rootProvider); + let index = requireEnv("ROOT_DEPLOYER_LEDGER_INDEX"); + const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; + adminWallet = new LedgerSigner(rootProvider, derivationPath); } else { adminWallet = new ethers.Wallet(rootDeployerSecret, rootProvider); } @@ -74,7 +76,9 @@ exports.initialiseRootContracts = async() => { // Get rate admin address let rateAdminWallet; if (rootRateAdminSecret == "ledger") { - rateAdminWallet = new LedgerSigner(rateAdminWallet); + let index = requireEnv("ROOT_BRIDGE_RATE_ADMIN_LEDGER_INDEX"); + const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; + rateAdminWallet = new LedgerSigner(rootProvider, derivationPath); } else { rateAdminWallet = new ethers.Wallet(rootRateAdminSecret, rootProvider); } @@ -84,12 +88,11 @@ exports.initialiseRootContracts = async() => { // Execute console.log("Initialise root contracts in..."); - await helper.waitForConfirmation(); + await waitForConfirmation(); // Initialise root bridge - let rootBridgeObj = JSON.parse(fs.readFileSync('../../out/RootERC20BridgeFlowRate.sol/RootERC20BridgeFlowRate.json', 'utf8')); console.log("Initialise root bridge..."); - let rootBridge = new ethers.Contract(rootBridgeAddr, rootBridgeObj.abi, rootProvider); + let rootBridge = getContract("RootERC20BridgeFlowRate", rootBridgeAddr, rootProvider); let resp = await rootBridge.connect(adminWallet)["initialize((address,address,address,address,address),address,address,address,address,address,uint256,address)"]( { defaultAdmin: rootBridgeDefaultAdmin, @@ -106,7 +109,7 @@ exports.initialiseRootContracts = async() => { ethers.utils.parseEther(imxDepositLimit), rateAdminAddr); console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); // Configure rate // IMX @@ -118,7 +121,7 @@ exports.initialiseRootContracts = async() => { ethers.utils.parseEther(rateLimitIMXLargeThreshold) ); console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); // ETH console.log("Configure rate limiting for ETH...") @@ -129,7 +132,7 @@ exports.initialiseRootContracts = async() => { ethers.utils.parseEther(rateLimitETHLargeThreshold) ); console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); // USDC console.log("Configure rate limiting for USDC...") @@ -140,7 +143,7 @@ exports.initialiseRootContracts = async() => { ethers.utils.parseEther(rateLimitUSDCLargeThreshold) ); console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); // GU console.log("Configure rate limiting for GU...") @@ -151,7 +154,7 @@ exports.initialiseRootContracts = async() => { ethers.utils.parseEther(rateLimitGULargeThreshold) ); console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); // Checkmate console.log("Configure rate limiting for CheckMate...") @@ -162,7 +165,7 @@ exports.initialiseRootContracts = async() => { ethers.utils.parseEther(rateLimitCheckMateLargeThreshold) ); console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); // GOG console.log("Configure rate limiting for GOG...") @@ -173,12 +176,11 @@ exports.initialiseRootContracts = async() => { ethers.utils.parseEther(rateLimitGOGLargeThreshold) ); console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); // Initialise root adaptor - let rootAdaptorObj = JSON.parse(fs.readFileSync('../../out/RootAxelarBridgeAdaptor.sol/RootAxelarBridgeAdaptor.json', 'utf8')); console.log("Initialise root adaptor..."); - let rootAdaptor = new ethers.Contract(rootAdaptorAddr, rootAdaptorObj.abi, rootProvider); + let rootAdaptor = getContract("RootAxelarBridgeAdaptor", rootAdaptorAddr, rootProvider); resp = await rootAdaptor.connect(adminWallet).initialize( { defaultAdmin: rootAdaptorDefaultAdmin, @@ -191,5 +193,5 @@ exports.initialiseRootContracts = async() => { childAdaptorAddr, rootGasServiceAddr); console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); } \ No newline at end of file diff --git a/scripts/e2e/e2e.js b/scripts/e2e/e2e.ts similarity index 80% rename from scripts/e2e/e2e.js rename to scripts/e2e/e2e.ts index 964432e2..d62e0f49 100644 --- a/scripts/e2e/e2e.js +++ b/scripts/e2e/e2e.ts @@ -1,38 +1,38 @@ // End to end tests -'use strict'; -require('dotenv').config(); -const { ethers, ContractFactory } = require("ethers"); -const helper = require("../helpers/helpers.js"); -const fs = require('fs'); -const { expect } = require("chai"); +import * as dotenv from "dotenv"; +dotenv.config(); +import { ethers, providers } from "ethers"; +import { requireEnv, waitForReceipt, getFee, getContract, deployRootContract, delay } from "../helpers/helpers"; +import * as fs from "fs"; +import { expect } from "chai"; // The contract ABI of IMX on L1. const IMX_ABI = `[{"inputs":[{"internalType":"address","name":"minter","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]`; describe("Bridge e2e test", () => { - let rootProvider; - let childProvider; - let rootTestWallet; - let childTestWallet; - let rootBridge; - let rootWETH; - let rootIMX; - let childBridge; - let childETH; - let childWIMX; - let rootCustomToken; - let childCustomToken; + let rootProvider: providers.JsonRpcProvider; + let childProvider: providers.JsonRpcProvider; + let rootTestWallet: ethers.Wallet; + let childTestWallet: ethers.Wallet; + let rootBridge: ethers.Contract; + let rootWETH: ethers.Contract; + let rootIMX: ethers.Contract; + let childBridge: ethers.Contract; + let childETH: ethers.Contract; + let childWIMX: ethers.Contract; + let rootCustomToken: ethers.Contract; + let childCustomToken: ethers.Contract; before(async function () { this.timeout(30000); - let rootRPCURL = helper.requireEnv("ROOT_RPC_URL"); - let rootChainID = helper.requireEnv("ROOT_CHAIN_ID"); - let childRPCURL = helper.requireEnv("CHILD_RPC_URL"); - let childChainID = helper.requireEnv("CHILD_CHAIN_ID"); - let rootRateAdminSecret = helper.requireEnv("ROOT_BRIDGE_RATE_ADMIN_SECRET"); - let testAccountKey = helper.requireEnv("TEST_ACCOUNT_SECRET"); - let rootIMXAddr = helper.requireEnv("ROOT_IMX_ADDR"); - let rootWETHAddr = helper.requireEnv("ROOT_WETH_ADDR"); + let rootRPCURL = requireEnv("ROOT_RPC_URL"); + let rootChainID = requireEnv("ROOT_CHAIN_ID"); + let childRPCURL = requireEnv("CHILD_RPC_URL"); + let childChainID = requireEnv("CHILD_CHAIN_ID"); + let rootRateAdminSecret = requireEnv("ROOT_BRIDGE_RATE_ADMIN_SECRET"); + let testAccountKey = requireEnv("TEST_ACCOUNT_SECRET"); + let rootIMXAddr = requireEnv("ROOT_IMX_ADDR"); + let rootWETHAddr = requireEnv("ROOT_WETH_ADDR"); // Read from contract file. let data = fs.readFileSync(".child.bridge.contracts.json", 'utf-8'); @@ -49,32 +49,19 @@ describe("Bridge e2e test", () => { childTestWallet = new ethers.Wallet(testAccountKey, childProvider); let rootRateAdminWallet = new ethers.Wallet(rootRateAdminSecret, rootProvider); - let rootBridgeObj = JSON.parse(fs.readFileSync('../../out/RootERC20BridgeFlowRate.sol/RootERC20BridgeFlowRate.json', 'utf8')); - rootBridge = new ethers.Contract(rootBridgeAddr, rootBridgeObj.abi, rootProvider); - - let WETHObj = JSON.parse(fs.readFileSync('../../out/WETH.sol/WETH.json', 'utf8')) - rootWETH = new ethers.Contract(rootWETHAddr, WETHObj.abi, rootProvider); - + rootBridge = getContract("RootERC20BridgeFlowRate", rootBridgeAddr, rootProvider); + rootWETH = getContract("WETH", rootWETHAddr, rootProvider); rootIMX = new ethers.Contract(rootIMXAddr, IMX_ABI, rootProvider); - - let childBridgeObj = JSON.parse(fs.readFileSync('../../out/ChildERC20Bridge.sol/ChildERC20Bridge.json', 'utf8')); - childBridge = new ethers.Contract(childBridgeAddr, childBridgeObj.abi, childProvider); - - let childEthTokenAddr = await childBridge.childETHToken(); - let childTokenTemplateObj = JSON.parse(fs.readFileSync('../../out/ChildERC20.sol/ChildERC20.json', 'utf8')); - childETH = new ethers.Contract(childEthTokenAddr, childTokenTemplateObj.abi, childProvider); - - let wrappedIMXObj = JSON.parse(fs.readFileSync('../../out/WIMX.sol/WIMX.json', 'utf8')); - childWIMX = new ethers.Contract(childWIMXAddr, wrappedIMXObj.abi, childProvider); + childBridge = getContract("ChildERC20Bridge", childBridgeAddr, childProvider); + childETH = getContract("ChildERC20", await childBridge.childETHToken(), childProvider); + childWIMX = getContract("WIMX", childWIMXAddr, childProvider); // Deploy a custom token - let customTokenObj = JSON.parse(fs.readFileSync('../../out/ERC20PresetMinterPauser.sol/ERC20PresetMinterPauser.json', 'utf8')); - let factory = new ContractFactory(customTokenObj.abi, customTokenObj.bytecode, rootTestWallet); - rootCustomToken = await factory.deploy("Custom Token", "CTK"); - await helper.waitForReceipt(rootCustomToken.deployTransaction.hash, rootProvider); + rootCustomToken = await deployRootContract("ERC20PresetMinterPauser", rootTestWallet, "Custom Token", "CTK"); + await waitForReceipt(rootCustomToken.deployTransaction.hash, rootProvider); // Mint tokens let resp = await rootCustomToken.connect(rootTestWallet).mint(rootTestWallet.address, ethers.utils.parseEther("1000.0").toBigInt()); - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); // Set rate control resp = await rootBridge.connect(rootRateAdminWallet).setRateControlThreshold( rootCustomToken.address, @@ -82,7 +69,7 @@ describe("Bridge e2e test", () => { ethers.utils.parseEther("5.56"), ethers.utils.parseEther("10008.0") ); - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); }) it("should successfully deposit IMX to self from L1 to L2", async() => { @@ -95,20 +82,20 @@ describe("Bridge e2e test", () => { // Approve let resp = await rootIMX.connect(rootTestWallet).approve(rootBridge.address, amt); - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); // IMX deposit L1 to L2 resp = await rootBridge.connect(rootTestWallet).deposit(rootIMX.address, amt, { value: bridgeFee, }); - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); let postBalL1 = await rootIMX.balanceOf(rootTestWallet.address); let postBalL2 = preBalL2; while (postBalL2.eq(preBalL2)) { postBalL2 = await childProvider.getBalance(childTestWallet.address); - await helper.delay(1000); + await delay(1000); } // Verify @@ -127,20 +114,20 @@ describe("Bridge e2e test", () => { let bridgeFee = ethers.utils.parseEther("1.0"); // IMX withdraw L2 to L1 - let [priorityFee, maxFee] = await helper.getFee(childTestWallet); + let [priorityFee, maxFee] = await getFee(childProvider); let resp = await childBridge.connect(childTestWallet).withdrawIMX(amt, { value: amt.add(bridgeFee), maxPriorityFeePerGas: priorityFee, maxFeePerGas: maxFee, }); - await helper.waitForReceipt(resp.hash, childProvider); + await waitForReceipt(resp.hash, childProvider); let postBalL1 = preBalL1; let postBalL2 = await childProvider.getBalance(childTestWallet.address); while (postBalL1.eq(preBalL1)) { postBalL1 = await rootIMX.balanceOf(rootTestWallet.address); - await helper.delay(1000); + await delay(1000); } // Verify @@ -154,13 +141,13 @@ describe("Bridge e2e test", () => { it("should successfully withdraw wIMX to self from L2 to L1", async() => { // Wrap 1 IMX - let [priorityFee, maxFee] = await helper.getFee(childTestWallet); + let [priorityFee, maxFee] = await getFee(childProvider); let resp = await childWIMX.connect(childTestWallet).deposit({ value: ethers.utils.parseEther("1.0"), maxPriorityFeePerGas: priorityFee, maxFeePerGas: maxFee, }); - await helper.waitForReceipt(resp.hash, childProvider); + await waitForReceipt(resp.hash, childProvider); // Get IMX balance on root & child chains before withdraw let preBalL1 = await rootIMX.balanceOf(rootTestWallet.address); @@ -170,28 +157,28 @@ describe("Bridge e2e test", () => { let bridgeFee = ethers.utils.parseEther("1.0"); // Approve - [priorityFee, maxFee] = await helper.getFee(childTestWallet); + [priorityFee, maxFee] = await getFee(childProvider); resp = await childWIMX.connect(childTestWallet).approve(childBridge.address, amt, { maxPriorityFeePerGas: priorityFee, maxFeePerGas: maxFee, }); - await helper.waitForReceipt(resp.hash, childProvider); + await waitForReceipt(resp.hash, childProvider); // wIMX withdraw L2 to L1 - [priorityFee, maxFee] = await helper.getFee(childTestWallet); + [priorityFee, maxFee] = await getFee(childProvider); resp = await childBridge.connect(childTestWallet).withdrawWIMX(amt, { value: bridgeFee, maxPriorityFeePerGas: priorityFee, maxFeePerGas: maxFee, }); - await helper.waitForReceipt(resp.hash, childProvider); + await waitForReceipt(resp.hash, childProvider); let postBalL1 = preBalL1; let postBalL2 = await childWIMX.balanceOf(childTestWallet.address); while (postBalL1.eq(preBalL1)) { postBalL1 = await rootIMX.balanceOf(rootTestWallet.address); - await helper.delay(1000); + await delay(1000); } // Verify @@ -213,14 +200,14 @@ describe("Bridge e2e test", () => { let resp = await rootBridge.connect(rootTestWallet).depositETH(amt, { value: amt.add(bridgeFee), }); - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); let postBalL1 = await rootProvider.getBalance(rootTestWallet.address); let postBalL2 = preBalL2; while (postBalL2.eq(preBalL2)) { postBalL2 = await childETH.balanceOf(childTestWallet.address); - await helper.delay(1000); + await delay(1000); } // Verify @@ -237,7 +224,7 @@ describe("Bridge e2e test", () => { let resp = await rootWETH.connect(rootTestWallet).deposit({ value: ethers.utils.parseEther("0.01"), }) - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); // Get ETH balance on root & child chains before withdraw let preBalL1 = await rootWETH.balanceOf(rootTestWallet.address); @@ -248,20 +235,20 @@ describe("Bridge e2e test", () => { // Approve resp = await rootWETH.connect(rootTestWallet).approve(rootBridge.address, amt); - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); // wETH deposit L1 to L2 resp = await rootBridge.connect(rootTestWallet).deposit(rootWETH.address, amt, { value: bridgeFee, }) - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); let postBalL1 = await rootWETH.balanceOf(rootTestWallet.address); let postBalL2 = preBalL2; while (postBalL2.eq(preBalL2)) { postBalL2 = await childETH.balanceOf(childTestWallet.address); - await helper.delay(1000); + await delay(1000); } // Verify @@ -280,20 +267,20 @@ describe("Bridge e2e test", () => { let bridgeFee = ethers.utils.parseEther("1.0"); // ETH withdraw L2 to L1 - let [priorityFee, maxFee] = await helper.getFee(childTestWallet); + let [priorityFee, maxFee] = await getFee(childProvider); let resp = await childBridge.connect(childTestWallet).withdrawETH(amt, { value: bridgeFee, maxPriorityFeePerGas: priorityFee, maxFeePerGas: maxFee, }); - await helper.waitForReceipt(resp.hash, childProvider); + await waitForReceipt(resp.hash, childProvider); let postBalL1 = preBalL1; let postBalL2 = await childETH.balanceOf(childTestWallet.address); while (postBalL1.eq(preBalL1)) { postBalL1 = await rootProvider.getBalance(rootTestWallet.address); - await helper.delay(1000); + await delay(1000); } // Verify @@ -312,15 +299,14 @@ describe("Bridge e2e test", () => { let resp = await rootBridge.connect(rootTestWallet).mapToken(rootCustomToken.address, { value: bridgeFee, }) - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); let childTokenAddr = await childBridge.rootTokenToChildToken(rootCustomToken.address); while (childTokenAddr == ethers.constants.AddressZero) { childTokenAddr = await childBridge.rootTokenToChildToken(rootCustomToken.address); - await helper.delay(1000); + await delay(1000); } - let childTokenTemplateObj = JSON.parse(fs.readFileSync('../../out/ChildERC20.sol/ChildERC20.json', 'utf8')); - childCustomToken = new ethers.Contract(childTokenAddr, childTokenTemplateObj.abi, childProvider); + childCustomToken = getContract("ChildERC20", childTokenAddr, childProvider); // Verify expect(childTokenAddr).to.equal(expectedChildTokenAddr); @@ -336,19 +322,19 @@ describe("Bridge e2e test", () => { // Approve let resp = await rootCustomToken.connect(rootTestWallet).approve(rootBridge.address, amt); - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); // Token deposit L1 to L2 resp = await rootBridge.connect(rootTestWallet).deposit(rootCustomToken.address, amt, { value: bridgeFee, }) - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); let postBalL1 = await rootCustomToken.balanceOf(rootTestWallet.address); let postBalL2 = preBalL2; while (postBalL2.eq(preBalL2)) { postBalL2 = await childCustomToken.balanceOf(childTestWallet.address); - await helper.delay(1000); + await delay(1000); } // Verify @@ -367,20 +353,20 @@ describe("Bridge e2e test", () => { let bridgeFee = ethers.utils.parseEther("1.0"); // Token withdraw L2 to L1 - let [priorityFee, maxFee] = await helper.getFee(childTestWallet); + let [priorityFee, maxFee] = await getFee(childProvider); let resp = await childBridge.connect(childTestWallet).withdraw(childCustomToken.address, amt, { value: bridgeFee, maxPriorityFeePerGas: priorityFee, maxFeePerGas: maxFee, }) - await helper.waitForReceipt(resp.hash, childProvider); + await waitForReceipt(resp.hash, childProvider); let postBalL1 = preBalL1; let postBalL2 = await childCustomToken.balanceOf(childTestWallet.address); while (postBalL1.eq(preBalL1)) { postBalL1 = await rootCustomToken.balanceOf(rootTestWallet.address); - await helper.delay(1000); + await delay(1000); } // Verify diff --git a/scripts/helpers/helpers.js b/scripts/helpers/helpers.js deleted file mode 100644 index 776846cc..00000000 --- a/scripts/helpers/helpers.js +++ /dev/null @@ -1,66 +0,0 @@ -const { ContractFactory } = require("ethers"); - -exports.delay = (time) => { - return new Promise(resolve => setTimeout(resolve, time)); -} -exports.requireEnv = (envName) => { - let val = process.env[envName]; - if (val == null || val == "") { - throw(envName + " not set!"); - } - if (!envName.includes("SECRET")) { - console.log(envName + ": ", val); - } else { - console.log(envName + " is set."); - } - return val -} -exports.waitForReceipt = async (txHash, provider) => { - let receipt; - while (receipt == null) { - receipt = await provider.getTransactionReceipt(txHash) - await exports.delay(1000); - } - console.log("Receipt: " + JSON.stringify(receipt, null, 2)); - if (receipt.status != 1) { - throw("Fail to execute: " + txHash); - } - console.log("Tx " + txHash + " succeed."); -} -exports.waitForConfirmation = async () => { - if (process.env["SKIP_WAIT_FOR_CONFIRMATION"] == null) { - for (let i = 10; i >= 0; i--) { - console.log(i) - await exports.delay(1000); - } - } -} -exports.getFee = async (wallet) => { - let feeData = await wallet.getFeeData(); - let baseFee = feeData.lastBaseFeePerGas; - let gasPrice = feeData.gasPrice; - let priorityFee = Math.round(gasPrice * 150 / 100); - let maxFee = Math.round(1.13 * baseFee + priorityFee); - return [priorityFee, maxFee]; -} -exports.requireNonEmptyCode = async (provider, addr) => { - if (await provider.getCode(addr) == "0x") { - throw(addr + " has empty code!"); - } - console.log(addr + " has code."); -} -exports.hasDuplicates = (array) => { - return (new Set(array)).size !== array.length; -} -exports.deployChildContract = async (contractObj, adminWallet, ...args) => { - let [priorityFee, maxFee] = await exports.getFee(adminWallet); - let factory = new ContractFactory(contractObj.abi, contractObj.bytecode, adminWallet); - return await factory.deploy(...args, { - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); -} -exports.deployRootContract = async (contractObj, adminWallet, ...args) => { - let factory = new ContractFactory(contractObj.abi, contractObj.bytecode, adminWallet); - return await factory.deploy(...args); -} \ No newline at end of file diff --git a/scripts/helpers/helpers.ts b/scripts/helpers/helpers.ts new file mode 100644 index 00000000..678a4be1 --- /dev/null +++ b/scripts/helpers/helpers.ts @@ -0,0 +1,90 @@ +import { ContractFactory, providers, ethers } from "ethers"; +import { LedgerSigner } from "./ledger_signer"; +import * as fs from "fs"; + +export function delay(time: number) { + return new Promise(resolve => setTimeout(resolve, time)); +} + +export function requireEnv(envName: string) { + let val = process.env[envName]; + if (val == null || val == "") { + throw(envName + " not set!"); + } + if (!envName.includes("SECRET")) { + console.log(envName + ": ", val); + } else { + console.log(envName + " is set."); + } + return val +} + +export async function waitForReceipt(txHash: string, provider: providers.JsonRpcProvider) { + let receipt; + while (receipt == null) { + receipt = await provider.getTransactionReceipt(txHash) + await exports.delay(1000); + } + console.log("Receipt: " + JSON.stringify(receipt, null, 2)); + if (receipt.status != 1) { + throw("Fail to execute: " + txHash); + } + console.log("Tx " + txHash + " succeed."); +} + +export async function waitForConfirmation() { + if (process.env["SKIP_WAIT_FOR_CONFIRMATION"] == null) { + for (let i = 10; i >= 0; i--) { + console.log(i) + await exports.delay(1000); + } + } +} + +export async function getFee(provider: providers.JsonRpcProvider) { + let feeData = await provider.getFeeData(); + let baseFee = feeData.lastBaseFeePerGas; + let gasPrice = feeData.gasPrice; + let priorityFee; + let maxFee; + if (gasPrice && baseFee) { + priorityFee = gasPrice.mul(150).div(100); + maxFee = baseFee.mul(113).div(100).add(priorityFee); + } else { + priorityFee = ethers.utils.parseUnits("110", "gwei"); + maxFee = ethers.utils.parseUnits("120", "gwei"); + } + return [priorityFee, maxFee]; +} + +export async function requireNonEmptyCode(provider: providers.JsonRpcProvider, addr: string) { + if (await provider.getCode(addr) == "0x") { + throw(addr + " has empty code!"); + } + console.log(addr + " has code."); +} + +export function hasDuplicates(array: string[]) { + return (new Set(array)).size !== array.length; +} + +export async function deployChildContract(contract: string, adminWallet: ethers.Wallet | LedgerSigner, ...args: any) { + let contractObj = JSON.parse(fs.readFileSync(`../../out/${contract}.sol/${contract}.json`, 'utf8')); + let [priorityFee, maxFee] = await exports.getFee(adminWallet); + let factory = new ContractFactory(contractObj.abi, contractObj.bytecode, adminWallet); + return await factory.deploy(...args, { + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); +} + +export async function deployRootContract(contract: string, adminWallet: ethers.Wallet | LedgerSigner, ...args: any) { + let contractObj = JSON.parse(fs.readFileSync(`../../out/${contract}.sol/${contract}.json`, 'utf8')); + let factory = new ContractFactory(contractObj.abi, contractObj.bytecode, adminWallet); + return await factory.deploy(...args); +} + +export function getContract(contract: string, contractAddr: string, provider: providers.JsonRpcProvider) { + let contractObj = JSON.parse(fs.readFileSync(`../../out/${contract}.sol/${contract}.json`, 'utf8')); + return new ethers.Contract(contractAddr, contractObj.abi, provider); +} \ No newline at end of file diff --git a/scripts/helpers/ledger_signer.ts b/scripts/helpers/ledger_signer.ts new file mode 100644 index 00000000..cb81315f --- /dev/null +++ b/scripts/helpers/ledger_signer.ts @@ -0,0 +1,145 @@ +// Copied from https://github.com/immutable/imx-engine/blob/77b8a62e6ac0baf033519e0ed533316eead3bc23/services/order-book-mr/e2e/scripts/ledger-signer.ts +import { ethers } from "ethers"; +import Eth from "@ledgerhq/hw-app-eth"; +import TransportNodeHid from "@ledgerhq/hw-transport-node-hid"; +import { + defineReadOnly, + hexlify, + resolveProperties, + serializeTransaction, + toUtf8Bytes, + UnsignedTransaction, +} from "ethers/lib/utils"; +import { toBuffer, toRpcSig } from "@nomicfoundation/ethereumjs-util"; +import ledgerService from "@ledgerhq/hw-app-eth/lib/services/ledger"; + +const DEFAULT_LEDGER_PATH = "m/44'/60'/0'/0/0"; + +function toHex(value: string | Buffer): string { + const stringValue = typeof value === "string" ? value : value.toString("hex"); + return stringValue.startsWith("0x") ? stringValue : `0x${stringValue}`; +} + +// Simple LedgerSigner that wraps @ledgerhq/hw-transport-node-hid to deploy +// contracts using hardware wallet. +export class LedgerSigner extends ethers.Signer { + readonly path: string; + readonly _eth: Promise | undefined; + + constructor( + provider?: ethers.providers.Provider, + path: string = DEFAULT_LEDGER_PATH + ) { + super(); + this.path = path || DEFAULT_LEDGER_PATH; + + defineReadOnly(this, "path", path); + defineReadOnly(this, "provider", provider || undefined); + defineReadOnly( + this, + "_eth", + TransportNodeHid.create().then(async (transport) => { + try { + const eth = new Eth(transport); + await eth.getAppConfiguration(); + return eth; + } catch (error) { + throw "LedgerSigner: unable to initialize TransportNodeHid: " + error; + } + }) + ); + } + + private async _withConfirmation any>( + func: T + ): Promise> { + try { + const result = await func(); + + return result; + } catch (error) { + throw new Error("LedgerSigner: confirmation_failure: " + error); + } + } + + public async getAddress(): Promise { + const eth = await this._eth; + + const MAX_RETRY_COUNT = 50; + const WAIT_INTERVAL = 100; + + for (let i = 0; i < MAX_RETRY_COUNT; i++) { + try { + const account = await eth!.getAddress(this.path); + return ethers.utils.getAddress(account.address); + } catch (error) { + if ((error as any).id !== "TransportLocked") { + throw error; + } + } + await new Promise((resolve) => setTimeout(resolve, WAIT_INTERVAL)); + } + + throw new Error("LedgerSigner: getAddress timed out"); + } + + public async signMessage( + message: ethers.utils.Bytes | string + ): Promise { + const resolvedMessage = + typeof message === "string" ? toUtf8Bytes(message) : message; + + const eth = await this._eth; + const signature = await this._withConfirmation(() => + eth!.signPersonalMessage(this.path, hexlify(resolvedMessage)) + ); + + return toRpcSig( + BigInt(signature.v - 27), + toBuffer(toHex(signature.r)), + toBuffer(toHex(signature.s)) + ); + } + + async signTransaction( + transaction: ethers.providers.TransactionRequest + ): Promise { + const txRequest = await resolveProperties(transaction); + + const baseTx: UnsignedTransaction = { + type: txRequest.type, + data: txRequest.data, + chainId: txRequest.chainId, + gasLimit: txRequest.gasLimit, + gasPrice: txRequest.gasPrice, + nonce: Number(txRequest.nonce), + value: txRequest.value, + to: txRequest.to, + }; + + // Type-2 transaction, with tip + if (txRequest.type === 2) { + baseTx.maxFeePerGas = txRequest.maxFeePerGas; + baseTx.maxPriorityFeePerGas = txRequest.maxPriorityFeePerGas; + } + + const txToSign = serializeTransaction(baseTx).substring(2); + + const resolution = await ledgerService.resolveTransaction(txToSign, {}, {}); + + const eth = await this._eth; + const signature = await this._withConfirmation(() => + eth!.signTransaction(this.path, txToSign, resolution) + ); + + return serializeTransaction(baseTx, { + v: Number(signature.v), + r: toHex(signature.r), + s: toHex(signature.s), + }); + } + + connect(provider: ethers.providers.Provider): ethers.Signer { + return new LedgerSigner(provider, this.path); + } +} \ No newline at end of file diff --git a/scripts/localdev/axelar_setup.js b/scripts/localdev/axelar_setup.ts similarity index 80% rename from scripts/localdev/axelar_setup.js rename to scripts/localdev/axelar_setup.ts index 81a467af..b8b0c6f1 100644 --- a/scripts/localdev/axelar_setup.js +++ b/scripts/localdev/axelar_setup.ts @@ -1,23 +1,23 @@ -'use strict'; -const { Network, networks, EvmRelayer, relay } = require('@axelar-network/axelar-local-dev'); -const helper = require("../helpers/helpers.js"); -const { ethers } = require("ethers"); -const fs = require('fs'); -require('dotenv').config(); +import * as dotenv from "dotenv"; +dotenv.config(); +import { Network, networks, EvmRelayer, relay } from '@axelar-network/axelar-local-dev'; +import { requireEnv, waitForReceipt } from "../helpers/helpers"; +import { ethers } from "ethers"; +import * as fs from "fs"; let relaying = false; const defaultEvmRelayer = new EvmRelayer(); async function main() { - let rootChainName = helper.requireEnv("ROOT_CHAIN_NAME"); - let rootRPCURL = helper.requireEnv("ROOT_RPC_URL"); - let rootChainID = helper.requireEnv("ROOT_CHAIN_ID"); - let childChainName = helper.requireEnv("CHILD_CHAIN_NAME"); - let childRPCURL = helper.requireEnv("CHILD_RPC_URL"); - let childChainID = helper.requireEnv("CHILD_CHAIN_ID"); - let axelarRootEOAKey = helper.requireEnv("AXELAR_ROOT_EOA_SECRET"); - let axelarChildEOAKey = helper.requireEnv("AXELAR_CHILD_EOA_SECRET"); - let axelarDeployerKey = helper.requireEnv("AXELAR_DEPLOYER_SECRET"); + let rootChainName = requireEnv("ROOT_CHAIN_NAME"); + let rootRPCURL = requireEnv("ROOT_RPC_URL"); + let rootChainID = requireEnv("ROOT_CHAIN_ID"); + let childChainName = requireEnv("CHILD_CHAIN_NAME"); + let childRPCURL = requireEnv("CHILD_RPC_URL"); + let childChainID = requireEnv("CHILD_CHAIN_ID"); + let axelarRootEOAKey = requireEnv("AXELAR_ROOT_EOA_SECRET"); + let axelarChildEOAKey = requireEnv("AXELAR_CHILD_EOA_SECRET"); + let axelarDeployerKey = requireEnv("AXELAR_DEPLOYER_SECRET"); // Create root chain. let rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); @@ -75,23 +75,23 @@ async function main() { to: childChain.ownerWallet.address, value: ethers.utils.parseEther("35.0"), }) - await helper.waitForReceipt(resp.hash, childProvider); + await waitForReceipt(resp.hash, childProvider); resp = await axelarChildEOA.sendTransaction({ to: childChain.operatorWallet.address, value: ethers.utils.parseEther("35.0"), }) - await helper.waitForReceipt(resp.hash, childProvider); + await waitForReceipt(resp.hash, childProvider); resp = await axelarChildEOA.sendTransaction({ to: childChain.relayerWallet.address, value: ethers.utils.parseEther("35.0"), }) - await helper.waitForReceipt(resp.hash, childProvider); + await waitForReceipt(resp.hash, childProvider); for (let i = 0; i < 10; i++) { resp = await axelarChildEOA.sendTransaction({ to: childChain.adminWallets[i].address, value: ethers.utils.parseEther("35.0"), }) - await helper.waitForReceipt(resp.hash, childProvider); + await waitForReceipt(resp.hash, childProvider); } // Deploy child contracts. await childChain.deployConstAddressDeployer(); @@ -106,23 +106,23 @@ async function main() { to: rootChain.ownerWallet.address, value: ethers.utils.parseEther("35.0"), }) - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); resp = await axelarRootEOA.sendTransaction({ to: rootChain.operatorWallet.address, value: ethers.utils.parseEther("35.0"), }) - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); resp = await axelarRootEOA.sendTransaction({ to: rootChain.relayerWallet.address, value: ethers.utils.parseEther("35.0"), }) - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); for (let i = 0; i < 10; i++) { resp = await axelarRootEOA.sendTransaction({ to: rootChain.adminWallets[i].address, value: ethers.utils.parseEther("35.0"), }) - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); } // Deploy root contracts. await rootChain.deployConstAddressDeployer(); diff --git a/scripts/localdev/childchain.config.js b/scripts/localdev/childchain.config.ts similarity index 60% rename from scripts/localdev/childchain.config.js rename to scripts/localdev/childchain.config.ts index 9d968f23..b7df4ed5 100644 --- a/scripts/localdev/childchain.config.js +++ b/scripts/localdev/childchain.config.ts @@ -1,7 +1,7 @@ -/** @type import('hardhat/config').HardhatUserConfig */ -require("@nomicfoundation/hardhat-toolbox"); +import { HardhatUserConfig } from "hardhat/config"; +import "@nomicfoundation/hardhat-toolbox"; -module.exports = { +const config: HardhatUserConfig = { networks: { hardhat: { mining: { @@ -17,3 +17,4 @@ module.exports = { }, solidity: "0.8.19", }; +export default config; \ No newline at end of file diff --git a/scripts/localdev/childchain_setup.js b/scripts/localdev/childchain_setup.ts similarity index 62% rename from scripts/localdev/childchain_setup.js rename to scripts/localdev/childchain_setup.ts index e1f74557..889ffbd5 100644 --- a/scripts/localdev/childchain_setup.js +++ b/scripts/localdev/childchain_setup.ts @@ -1,13 +1,13 @@ -'use strict'; -const { ethers: hardhat } = require("hardhat"); -const { ethers } = require("ethers"); -const helper = require("../helpers/helpers.js"); -require('dotenv').config(); +import * as dotenv from "dotenv"; +dotenv.config(); +import { ethers as hardhat } from "hardhat"; +import { ethers } from "ethers"; +import { requireEnv } from "../helpers/helpers"; async function main() { - let childRPCURL = helper.requireEnv("CHILD_RPC_URL"); - let childChainID = helper.requireEnv("CHILD_CHAIN_ID"); - let childEOAKey = helper.requireEnv("CHILD_ADMIN_EOA_SECRET"); + let childRPCURL = requireEnv("CHILD_RPC_URL"); + let childChainID = requireEnv("CHILD_CHAIN_ID"); + let childEOAKey = requireEnv("CHILD_ADMIN_EOA_SECRET"); // Get child provider. let childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); diff --git a/scripts/localdev/ci.sh b/scripts/localdev/ci.sh index 003ea77a..b510e099 100755 --- a/scripts/localdev/ci.sh +++ b/scripts/localdev/ci.sh @@ -5,7 +5,7 @@ counter=1 while [ $counter -le 300 ] do echo "Waiting for chain and axelar setup... ${counter}" - SKIP_WAIT_FOR_CONFIRMATION=true SKIP_MULTISIG_CHECK=true node ../bootstrap/2_deployment_validation.js > /dev/null 2>&1 + SKIP_WAIT_FOR_CONFIRMATION=true SKIP_MULTISIG_CHECK=true npx ts-node ../bootstrap/2_deployment_validation.ts > /dev/null 2>&1 if [ $? -ne 0 ]; then sleep 1 ((counter++)) diff --git a/scripts/localdev/deploy.sh b/scripts/localdev/deploy.sh index 3fef152b..a770cc33 100755 --- a/scripts/localdev/deploy.sh +++ b/scripts/localdev/deploy.sh @@ -3,22 +3,22 @@ set -ex set -o pipefail # Verify deployment -SKIP_WAIT_FOR_CONFIRMATION=true SKIP_MULTISIG_CHECK=true node ../bootstrap/2_deployment_validation.js 2>&1 | tee -a bootstrap.out +SKIP_WAIT_FOR_CONFIRMATION=true SKIP_MULTISIG_CHECK=true npx ts-node ../bootstrap/2_deployment_validation.ts 2>&1 | tee -a bootstrap.out # Deploy child contracts -SKIP_WAIT_FOR_CONFIRMATION=true node ../bootstrap/3_child_deployment.js 2>&1 | tee -a bootstrap.out +SKIP_WAIT_FOR_CONFIRMATION=true npx ts-node ../bootstrap/3_child_deployment.ts 2>&1 | tee -a bootstrap.out # Deploy root contracts -SKIP_WAIT_FOR_CONFIRMATION=true node ../bootstrap/4_root_deployment.js 2>&1 | tee -a bootstrap.out +SKIP_WAIT_FOR_CONFIRMATION=true npx ts-node ../bootstrap/4_root_deployment.ts 2>&1 | tee -a bootstrap.out # Initialise child contracts -SKIP_WAIT_FOR_CONFIRMATION=true node ../bootstrap/5_child_initialisation.js 2>&1 | tee -a bootstrap.out +SKIP_WAIT_FOR_CONFIRMATION=true npx ts-node ../bootstrap/5_child_initialisation.ts 2>&1 | tee -a bootstrap.out # IMX Burning -SKIP_WAIT_FOR_CONFIRMATION=true node ../bootstrap/6_imx_burning.js 2>&1 | tee -a bootstrap.out +SKIP_WAIT_FOR_CONFIRMATION=true npx ts-node ../bootstrap/6_imx_burning.ts 2>&1 | tee -a bootstrap.out # IMX Rebalancing -SKIP_WAIT_FOR_CONFIRMATION=true node ../bootstrap/7_imx_rebalancing.js 2>&1 | tee -a bootstrap.out +SKIP_WAIT_FOR_CONFIRMATION=true npx ts-node ../bootstrap/7_imx_rebalancing.ts 2>&1 | tee -a bootstrap.out # Initialise root contracts -SKIP_WAIT_FOR_CONFIRMATION=true node ../bootstrap/8_root_initialisation.js 2>&1 | tee -a bootstrap.out \ No newline at end of file +SKIP_WAIT_FOR_CONFIRMATION=true npx ts-node ../bootstrap/8_root_initialisation.ts 2>&1 | tee -a bootstrap.out \ No newline at end of file diff --git a/scripts/localdev/rootchain.config.js b/scripts/localdev/rootchain.config.ts similarity index 60% rename from scripts/localdev/rootchain.config.js rename to scripts/localdev/rootchain.config.ts index 1177b63f..db622ae6 100644 --- a/scripts/localdev/rootchain.config.js +++ b/scripts/localdev/rootchain.config.ts @@ -1,7 +1,7 @@ -/** @type import('hardhat/config').HardhatUserConfig */ -require("@nomicfoundation/hardhat-toolbox"); +import { HardhatUserConfig } from "hardhat/config"; +import "@nomicfoundation/hardhat-toolbox"; -module.exports = { +const config: HardhatUserConfig = { networks: { hardhat: { mining: { @@ -16,4 +16,5 @@ module.exports = { } }, solidity: "0.8.19", -}; \ No newline at end of file +}; +export default config; \ No newline at end of file diff --git a/scripts/localdev/rootchain_setup.js b/scripts/localdev/rootchain_setup.ts similarity index 63% rename from scripts/localdev/rootchain_setup.js rename to scripts/localdev/rootchain_setup.ts index cc7e5b35..0bd6f26d 100644 --- a/scripts/localdev/rootchain_setup.js +++ b/scripts/localdev/rootchain_setup.ts @@ -1,18 +1,18 @@ -'use strict'; -const { ethers: hardhat } = require("hardhat"); -const { ethers, ContractFactory } = require("ethers"); -const helper = require("../helpers/helpers.js"); -const fs = require('fs'); -require('dotenv').config(); +import * as dotenv from "dotenv"; +dotenv.config(); +import { ethers as hardhat } from "hardhat"; +import { ethers } from "ethers"; +import { requireEnv, deployRootContract, waitForReceipt } from "../helpers/helpers"; +import * as fs from "fs"; async function main() { - let rootRPCURL = helper.requireEnv("ROOT_RPC_URL"); - let rootChainID = helper.requireEnv("ROOT_CHAIN_ID"); - let rootAdminKey = helper.requireEnv("ROOT_EOA_SECRET"); - let rootDeployerKey = helper.requireEnv("ROOT_DEPLOYER_SECRET"); - let axelarDeployerKey = helper.requireEnv("AXELAR_ROOT_EOA_SECRET"); - let rootTestKey = helper.requireEnv("TEST_ACCOUNT_SECRET"); - let rootRateAdminKey = helper.requireEnv("ROOT_BRIDGE_RATE_ADMIN_SECRET"); + let rootRPCURL = requireEnv("ROOT_RPC_URL"); + let rootChainID = requireEnv("ROOT_CHAIN_ID"); + let rootAdminKey = requireEnv("ROOT_EOA_SECRET"); + let rootDeployerKey = requireEnv("ROOT_DEPLOYER_SECRET"); + let axelarDeployerKey = requireEnv("AXELAR_ROOT_EOA_SECRET"); + let rootTestKey = requireEnv("TEST_ACCOUNT_SECRET"); + let rootRateAdminKey = requireEnv("ROOT_BRIDGE_RATE_ADMIN_SECRET"); // Get root provider. let rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); @@ -39,38 +39,31 @@ async function main() { ]); // Deploy IMX contract - let IMXObj = JSON.parse(fs.readFileSync('../../out/ERC20PresetMinterPauser.sol/ERC20PresetMinterPauser.json', 'utf8')); console.log("Deploy IMX contract on root chain..."); - - let IMXFactory = new ContractFactory(IMXObj.abi, IMXObj.bytecode, admin); - let IMX = await IMXFactory.deploy("IMX Token", "IMX"); - let txn = IMX.deployTransaction; - await helper.waitForReceipt(txn.hash, rootProvider); + let IMX = await deployRootContract("ERC20PresetMinterPauser", admin, "IMX Token", "IMX"); + await waitForReceipt(IMX.deployTransaction.hash, rootProvider); console.log("IMX deployed at: " + IMX.address); // Deploy WETH contract - let WETHObj = JSON.parse(fs.readFileSync('../../out/WETH.sol/WETH.json', 'utf8')) console.log("Deploy WETH contract on root chain..."); - let WETHFactory = new ContractFactory(WETHObj.abi, WETHObj.bytecode, admin); - let WETH = await WETHFactory.deploy(); - txn = WETH.deployTransaction; - await helper.waitForReceipt(txn.hash, rootProvider); + let WETH = await deployRootContract("WETH", admin); + await waitForReceipt(WETH.deployTransaction.hash, rootProvider); console.log("WETH deployed at: " + WETH.address); // Mint 1100 IMX to root deployer let resp = await IMX.connect(admin).mint(rootDeployer.address, ethers.utils.parseEther("1100.0")); - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); // Transfer 1000 IMX to test wallet resp = await IMX.connect(admin).mint(testWallet.address, ethers.utils.parseEther("1000.0")) - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); // Transfer 0.1 ETH to root deployer resp = await admin.sendTransaction({ to: rootDeployer.address, value: ethers.utils.parseEther("0.1"), }) - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); // Transfer 500 ETH to axelar deployer resp = await admin.sendTransaction({ @@ -83,14 +76,14 @@ async function main() { to: testWallet.address, value: ethers.utils.parseEther("10.0"), }) - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); // Transfer 0.1 ETH to rate admin resp = await admin.sendTransaction({ to: rateAdminWallet.address, value: ethers.utils.parseEther("10.0"), }) - await helper.waitForReceipt(resp.hash, rootProvider); + await waitForReceipt(resp.hash, rootProvider); console.log("Root deployer now has " + ethers.utils.formatEther(await IMX.balanceOf(rootDeployer.address)) + " IMX."); console.log("Root deployer now has " + ethers.utils.formatEther(await rootProvider.getBalance(rootDeployer.address)) + " ETH."); diff --git a/scripts/localdev/start.sh b/scripts/localdev/start.sh index 47f7b972..74205d77 100755 --- a/scripts/localdev/start.sh +++ b/scripts/localdev/start.sh @@ -8,8 +8,8 @@ set -o pipefail cp .env.local .env # Start root & child chain. -npx hardhat node --config ./rootchain.config.js --port 8500 > /dev/null 2>&1 & -npx hardhat node --config ./childchain.config.js --port 8501 > /dev/null 2>&1 & +npx hardhat node --config ./rootchain.config.ts --port 8500 > /dev/null 2>&1 & +npx hardhat node --config ./childchain.config.ts --port 8501 > /dev/null 2>&1 & sleep 10 # trap ctrl-c and call ctrl_c() @@ -20,18 +20,18 @@ function ctrl_c() { } # Setup root & child chain. -npx hardhat run ./rootchain_setup.js --config ./rootchain.config.js --network localhost -npx hardhat run ./childchain_setup.js --config ./childchain.config.js --network localhost +npx hardhat run ./rootchain_setup.ts --config ./rootchain.config.ts --network localhost +npx hardhat run ./childchain_setup.ts --config ./childchain.config.ts --network localhost echo "Successfully setup root chain and child chain..." if [ -z ${LOCAL_CHAIN_ONLY+x} ]; then # Fund accounts - SKIP_WAIT_FOR_CONFIRMATION=true node ../bootstrap/1_deployer_funding.js 2>&1 | tee bootstrap.out - echo "Successfully run 1_deployer_funding.js..." + SKIP_WAIT_FOR_CONFIRMATION=true npx ts-node ../bootstrap/1_deployer_funding.ts 2>&1 | tee bootstrap.out + echo "Successfully run 1_deployer_funding.ts..." # Setup axelar - node axelar_setup.js + npx ts-node axelar_setup.ts ./stop.sh fi \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..0b9e4592 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "es2018", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "outDir": "dist", + "declaration": true, + "resolveJsonModule": true + } +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 130cde6f..e8988aaf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -336,7 +336,7 @@ ethereum-cryptography "^2.0.0" micro-ftch "^0.3.1" -"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.7.0": +"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.5.0", "@ethersproject/abi@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== @@ -573,7 +573,7 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.7.0": +"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.5.0", "@ethersproject/rlp@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== @@ -713,6 +713,15 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@ledgerhq/cryptoassets@^11.2.0": + version "11.2.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/cryptoassets/-/cryptoassets-11.2.0.tgz#5594e262fc0aab6e02c1c2bdd950c019712fdaf2" + integrity sha512-O5fVIxzlwyR3YNEJJKUcGNyZW5Nf2lJtm0CPDWIfPaKERwvLPLfuJ5yUSHYBqpvYMGCCFldykiPdZ9XS3+fRaA== + dependencies: + axios "^1.6.0" + bs58check "^2.1.2" + invariant "2" + "@ledgerhq/cryptoassets@^5.27.2": version "5.53.0" resolved "https://registry.yarnpkg.com/@ledgerhq/cryptoassets/-/cryptoassets-5.53.0.tgz#11dcc93211960c6fd6620392e4dd91896aaabe58" @@ -730,11 +739,50 @@ rxjs "6" semver "^7.3.5" +"@ledgerhq/devices@^8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-8.1.0.tgz#39b12feabe1c7a99b86667bedf2eafbd125cf217" + integrity sha512-Vsdv84Nwzee0qhObdwVzhkxW1+h2cFoD1AWuU8N1V/2OJKiVS35A1qloSCF0oHapg+KTJvim8tr5rRvlkCYyzQ== + dependencies: + "@ledgerhq/errors" "^6.16.0" + "@ledgerhq/logs" "^6.12.0" + rxjs "^7.8.1" + semver "^7.3.5" + +"@ledgerhq/domain-service@^1.1.15": + version "1.1.15" + resolved "https://registry.yarnpkg.com/@ledgerhq/domain-service/-/domain-service-1.1.15.tgz#fb7664c61c83c0230f8aec35861ac81bdb605c0b" + integrity sha512-1X4MvNhVDTXCfOQckaUHsq/Qzn8xhFMcHjnLKOuCR5zNB8hYuTyg9e7JXURZ8W7/Qcn41rvIPxXBHwvMjWQBMA== + dependencies: + "@ledgerhq/errors" "^6.16.0" + "@ledgerhq/logs" "^6.12.0" + "@ledgerhq/types-live" "^6.43.0" + axios "^1.3.4" + eip55 "^2.1.1" + react "^18.2.0" + react-dom "^18.2.0" + "@ledgerhq/errors@^5.26.0", "@ledgerhq/errors@^5.50.0": version "5.50.0" resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-5.50.0.tgz#e3a6834cb8c19346efca214c1af84ed28e69dad9" integrity sha512-gu6aJ/BHuRlpU7kgVpy2vcYk6atjB4iauP2ymF7Gk0ez0Y/6VSMVSJvubeEQN+IV60+OBK0JgeIZG7OiHaw8ow== +"@ledgerhq/errors@^6.16.0": + version "6.16.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-6.16.0.tgz#0aaf16bbf649a3b43867746781b2e3adebf7fe3a" + integrity sha512-vnew6lf4jN6E+WI0DFhD4WY0uM8LYL8HCumtUr86hNwvmEfebi7LxxpJGmYfVQD5TgEC7NibYnQ+2q9XWAc02A== + +"@ledgerhq/evm-tools@^1.0.11": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@ledgerhq/evm-tools/-/evm-tools-1.0.11.tgz#d1352dcd3a8e971c5808f6a7e65439277bd1ee66" + integrity sha512-XfOQvEAzT3iD0hd7zNg8kioRXHnWdeLgs2/bwHeI9/pttzE+kTCjLhvIipYAeYVHg0gKaqecoygKdsuh6kS1fw== + dependencies: + "@ledgerhq/cryptoassets" "^11.2.0" + "@ledgerhq/live-env" "^0.7.0" + "@ledgerhq/live-network" "^1.1.9" + crypto-js "4.2.0" + ethers "5.7.2" + "@ledgerhq/hw-app-eth@5.27.2": version "5.27.2" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-eth/-/hw-app-eth-5.27.2.tgz#65a2ed613a69340e0cd69c942147455ec513d006" @@ -746,6 +794,33 @@ bignumber.js "^9.0.1" rlp "^2.2.6" +"@ledgerhq/hw-app-eth@^6.35.0": + version "6.35.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-eth/-/hw-app-eth-6.35.0.tgz#30486e0e9221de92653985af08a6208fde770a9c" + integrity sha512-BJ39+biwuTXmiKuO2c5PbjJBdGMOSl7nHncuLFCwBXi0hYlHiELHQgEOjjPon418ltuCQyuDBiNMyIFOLikIRQ== + dependencies: + "@ethersproject/abi" "^5.5.0" + "@ethersproject/rlp" "^5.5.0" + "@ledgerhq/cryptoassets" "^11.2.0" + "@ledgerhq/domain-service" "^1.1.15" + "@ledgerhq/errors" "^6.16.0" + "@ledgerhq/evm-tools" "^1.0.11" + "@ledgerhq/hw-transport" "^6.30.0" + "@ledgerhq/hw-transport-mocker" "^6.28.0" + "@ledgerhq/logs" "^6.12.0" + "@ledgerhq/types-live" "^6.43.0" + axios "^1.3.4" + bignumber.js "^9.1.2" + +"@ledgerhq/hw-transport-mocker@^6.28.0": + version "6.28.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-mocker/-/hw-transport-mocker-6.28.0.tgz#a664220338b56d62c8aca0a10c98e28484e4a889" + integrity sha512-svUgIRdoc69b49MHncKikoRgWIqn7ZR3IHP+nq4TCTYn2nm5LILJYyf8osnCg8brsXdEY68z++fr++GyF9vUIw== + dependencies: + "@ledgerhq/hw-transport" "^6.30.0" + "@ledgerhq/logs" "^6.12.0" + rxjs "^7.8.1" + "@ledgerhq/hw-transport-node-hid-noevents@^5.26.0": version "5.51.1" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid-noevents/-/hw-transport-node-hid-noevents-5.51.1.tgz#71f37f812e448178ad0bcc2258982150d211c1ab" @@ -757,6 +832,17 @@ "@ledgerhq/logs" "^5.50.0" node-hid "2.1.1" +"@ledgerhq/hw-transport-node-hid-noevents@^6.29.0": + version "6.29.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid-noevents/-/hw-transport-node-hid-noevents-6.29.0.tgz#10cbb260d9af3e961675ca88695d521dbc8c5964" + integrity sha512-JJM0NGOmFxCJ0IvbGlCo3KHYhkckn7QPNgBlGTrV/UDoMZdtDfp3R971jGUVInUmqYmHRDCGXRpjwgZRI7MJhg== + dependencies: + "@ledgerhq/devices" "^8.1.0" + "@ledgerhq/errors" "^6.16.0" + "@ledgerhq/hw-transport" "^6.30.0" + "@ledgerhq/logs" "^6.12.0" + node-hid "^2.1.2" + "@ledgerhq/hw-transport-node-hid@5.26.0": version "5.26.0" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-5.26.0.tgz#69bc4f8067cdd9c09ef4aed0e0b3c58328936e4b" @@ -771,6 +857,20 @@ node-hid "1.3.0" usb "^1.6.3" +"@ledgerhq/hw-transport-node-hid@^6.28.0": + version "6.28.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-6.28.0.tgz#d8e67b1b8acd1110bcecff11326eb37a7f1f2475" + integrity sha512-kRGsT9YkudP8TbiaBWOtpgMje3gp7CbNHgAA4gdGM5Xri5Li0foEoIFqYZfWCS44NrPbDrsalWqj03HmQ2LDpg== + dependencies: + "@ledgerhq/devices" "^8.1.0" + "@ledgerhq/errors" "^6.16.0" + "@ledgerhq/hw-transport" "^6.30.0" + "@ledgerhq/hw-transport-node-hid-noevents" "^6.29.0" + "@ledgerhq/logs" "^6.12.0" + lodash "^4.17.21" + node-hid "^2.1.2" + usb "2.9.0" + "@ledgerhq/hw-transport-u2f@5.26.0": version "5.26.0" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-u2f/-/hw-transport-u2f-5.26.0.tgz#b7d9d13193eb82b051fd7a838cd652372f907ec5" @@ -799,11 +899,62 @@ "@ledgerhq/errors" "^5.50.0" events "^3.3.0" +"@ledgerhq/hw-transport@^6.30.0": + version "6.30.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.30.0.tgz#9c8a8f2c8281fbc4a3db1d1f3ac44a456b38281a" + integrity sha512-wrAwn/wCAaGP2Yuy78cLyqmQNzbuDvUv4gJYF/UO4djvUz0jjvD2w5kxRWxF/W93vyKT+/RplRtFk3CJzD3e3A== + dependencies: + "@ledgerhq/devices" "^8.1.0" + "@ledgerhq/errors" "^6.16.0" + "@ledgerhq/logs" "^6.12.0" + events "^3.3.0" + +"@ledgerhq/live-env@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/live-env/-/live-env-0.7.0.tgz#b1fd3922111cb9c9410ffbed2010e506adacd093" + integrity sha512-Q77gmJLafjKmc23CbRgBD1Bm1MVatISo0JEWDX/nWZnWUK3IVwp8VxxJDHW4P7TlpsuCKCgCtd0C1gxZDWI/RA== + dependencies: + rxjs "^7.8.1" + utility-types "^3.10.0" + +"@ledgerhq/live-network@^1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@ledgerhq/live-network/-/live-network-1.1.9.tgz#ecf6b14cb382384665b29ffb6d8541044515b8e2" + integrity sha512-uwtVSzL88VtClmfkUTW5plEgdBqXnmT1vhTC7k/bCOf3CPpvkPQ2NLuutT1GHPkHUu+BjAweM6uUKl9JDwGs1g== + dependencies: + "@ledgerhq/errors" "^6.16.0" + "@ledgerhq/live-env" "^0.7.0" + "@ledgerhq/live-promise" "^0.0.3" + "@ledgerhq/logs" "^6.12.0" + axios "0.26.1" + invariant "^2.2.2" + lru-cache "^7.14.1" + +"@ledgerhq/live-promise@^0.0.3": + version "0.0.3" + resolved "https://registry.yarnpkg.com/@ledgerhq/live-promise/-/live-promise-0.0.3.tgz#432693468ddd48f94a24437c01791d59d393adbc" + integrity sha512-/49dRz5XoxUw4TFq0kytU2Vz9w+FoGgG28U8RH9nuUWVPjVhAPvhY/QXUQA+7qqaorEIAYPHF0Rappalawhr+g== + dependencies: + "@ledgerhq/logs" "^6.12.0" + "@ledgerhq/logs@^5.26.0", "@ledgerhq/logs@^5.50.0": version "5.50.0" resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-5.50.0.tgz#29c6419e8379d496ab6d0426eadf3c4d100cd186" integrity sha512-swKHYCOZUGyVt4ge0u8a7AwNcA//h4nx5wIi0sruGye1IJ5Cva0GyK9L2/WdX+kWVTKp92ZiEo1df31lrWGPgA== +"@ledgerhq/logs@^6.12.0": + version "6.12.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-6.12.0.tgz#ad903528bf3687a44da435d7b2479d724d374f5d" + integrity sha512-ExDoj1QV5eC6TEbMdLUMMk9cfvNKhhv5gXol4SmULRVCx/3iyCPhJ74nsb3S0Vb+/f+XujBEj3vQn5+cwS0fNA== + +"@ledgerhq/types-live@^6.43.0": + version "6.43.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/types-live/-/types-live-6.43.0.tgz#48491fb6f1a24b012e0b78188de7ad1cd1814bab" + integrity sha512-NvSWPefZ54BLTTMdljO2eS3j1Jbj4O+j/2OWZfyt6T1qMrU1OwORkIn7weuyqR0Y01mTos0sjST7r10MqtauJg== + dependencies: + bignumber.js "^9.1.2" + rxjs "^7.8.1" + "@metamask/eth-sig-util@^4.0.0": version "4.0.1" resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz#3ad61f6ea9ad73ba5b19db780d40d9aae5157088" @@ -1626,6 +1777,11 @@ resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== +"@types/w3c-web-usb@^1.0.6": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@types/w3c-web-usb/-/w3c-web-usb-1.0.10.tgz#cf89cccd2d93b6245e784c19afe0a9f5038d4528" + integrity sha512-CHgUI5kTc/QLMP8hODUHhge0D4vx+9UiAwIGiT0sTy/B2XpdX1U5rJt6JSISgr6ikRT7vxV9EVAFeYZqUnl1gQ== + abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -1968,6 +2124,13 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== +axios@0.26.1: + version "0.26.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9" + integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA== + dependencies: + follow-redirects "^1.14.8" + axios@0.27.2, axios@^0.27.2: version "0.27.2" resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" @@ -1983,7 +2146,7 @@ axios@^0.21.2: dependencies: follow-redirects "^1.14.0" -axios@^1.5.1: +axios@^1.3.4, axios@^1.5.1, axios@^1.6.0: version "1.6.2" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.2.tgz#de67d42c755b571d3e698df1b6504cde9b0ee9f2" integrity sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A== @@ -2034,7 +2197,7 @@ bigint-crypto-utils@^3.0.23: resolved "https://registry.yarnpkg.com/bigint-crypto-utils/-/bigint-crypto-utils-3.3.0.tgz#72ad00ae91062cf07f2b1def9594006c279c1d77" integrity sha512-jOTSb+drvEDxEq6OuUybOAv/xxoh3cuYRUIPyu8sSHQNKM303UQ2R1DAo45o1AkcIXw6fzbaFI1+xGGdaXs2lg== -bignumber.js@^9.0.1: +bignumber.js@^9.0.1, bignumber.js@^9.1.2: version "9.1.2" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== @@ -2608,6 +2771,11 @@ cross-fetch@^3.1.5: resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== +crypto-js@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" + integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== + death@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/death/-/death-1.1.0.tgz#01aa9c401edd92750514470b8266390c66c67318" @@ -2716,6 +2884,11 @@ detect-libc@^1.0.3: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== +detect-libc@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.2.tgz#8ccf2ba9315350e1241b88d0ac3b0e1fbd99605d" + integrity sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw== + detect-port@^1.3.0: version "1.5.1" resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.5.1.tgz#451ca9b6eaf20451acb0799b8ab40dff7718727b" @@ -2760,6 +2933,13 @@ dotenv@^16.0.2: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== +eip55@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/eip55/-/eip55-2.1.1.tgz#28b743c4701ac3c811b1e9fe67e39cf1d0781b96" + integrity sha512-WcagVAmNu2Ww2cDUfzuWVntYwFxbvZ5MvIyLZpMjTTkjD6sCvkGOiS86jTppzu9/gWsc8isLHAeMBWK02OnZmA== + dependencies: + keccak "^3.0.3" + elliptic@6.5.4, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" @@ -3312,7 +3492,7 @@ flat@^5.0.2: resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== -follow-redirects@^1.12.1, follow-redirects@^1.14.0, follow-redirects@^1.14.9, follow-redirects@^1.15.0: +follow-redirects@^1.12.1, follow-redirects@^1.14.0, follow-redirects@^1.14.8, follow-redirects@^1.14.9, follow-redirects@^1.15.0: version "1.15.3" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== @@ -3984,7 +4164,7 @@ interpret@^1.0.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== -invariant@2: +invariant@2, invariant@^2.2.2: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -4303,7 +4483,7 @@ keccak@3.0.2: node-gyp-build "^4.2.0" readable-stream "^3.6.0" -keccak@^3.0.0, keccak@^3.0.2: +keccak@^3.0.0, keccak@^3.0.2, keccak@^3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.4.tgz#edc09b89e633c0549da444432ecf062ffadee86d" integrity sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q== @@ -4459,7 +4639,7 @@ long@^4.0.0: resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== -loose-envify@^1.0.0: +loose-envify@^1.0.0, loose-envify@^1.1.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -4492,6 +4672,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-cache@^7.14.1: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + lru_map@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" @@ -4802,6 +4987,13 @@ node-abi@^2.18.0, node-abi@^2.21.0, node-abi@^2.7.0: dependencies: semver "^5.4.1" +node-abi@^3.3.0: + version "3.51.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.51.0.tgz#970bf595ef5a26a271307f8a4befa02823d4e87d" + integrity sha512-SQkEP4hmNWjlniS5zdnfIXTk1x7Ome85RDzHlTbBtzE97Gfwz/Ipw4v/Ryk20DWIy3yCNVLVlGKApCnmvYoJbA== + dependencies: + semver "^7.3.5" + node-addon-api@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" @@ -4817,6 +5009,11 @@ node-addon-api@^4.2.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== +node-addon-api@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76" + integrity sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA== + node-emoji@^1.10.0: version "1.11.0" resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.11.0.tgz#69a0150e6946e2f115e9d7ea4df7971e2628301c" @@ -4841,6 +5038,11 @@ node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.7.0.tgz#749f0033590b2a89ac8edb5e0775f95f5ae86d15" integrity sha512-PbZERfeFdrHQOOXiAKOY0VPbykZy90ndPKk0d+CFDegTKmWp1VgOTz2xACVbr1BjCWxrQp68CXtvNsveFhqDJg== +node-gyp-build@^4.5.0: + version "4.7.1" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.7.1.tgz#cd7d2eb48e594874053150a9418ac85af83ca8f7" + integrity sha512-wTSrZ+8lsRRa3I3H8Xr65dLWSgCvY2l4AOnaeKdPA9TB/WYMPaTcrzf3rXvFoVvjKNVnu0CcWSx54qq9GKRUYg== + node-hid@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/node-hid/-/node-hid-1.3.0.tgz#346a468505cee13d69ccd760052cbaf749f66a41" @@ -4860,6 +5062,15 @@ node-hid@2.1.1: node-addon-api "^3.0.2" prebuild-install "^6.0.0" +node-hid@^2.1.2: + version "2.2.0" + resolved "https://registry.yarnpkg.com/node-hid/-/node-hid-2.2.0.tgz#33e039e7530a7bfe2b7a25f0a2f9496af8b02236" + integrity sha512-vj48zh9j555DZzUhMc8tk/qw6xPFrDyPBH1ST1Z/hWaA/juBJw7IuSxPeOgpzNFNU36mGYj+THioRMt1xOdm/g== + dependencies: + bindings "^1.5.0" + node-addon-api "^3.0.2" + prebuild-install "^7.1.1" + node-port-check@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/node-port-check/-/node-port-check-2.0.1.tgz#72cae367d3ca906b0903b261ce818c297aade11f" @@ -5172,6 +5383,24 @@ prebuild-install@^6.0.0: tar-fs "^2.0.0" tunnel-agent "^0.6.0" +prebuild-install@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" + integrity sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw== + dependencies: + detect-libc "^2.0.0" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^1.0.1" + node-abi "^3.3.0" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^4.0.0" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -5299,6 +5528,21 @@ rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" +react-dom@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.23.0" + +react@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== + dependencies: + loose-envify "^1.1.0" + readable-stream@^2.0.6, readable-stream@^2.2.2: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" @@ -5493,6 +5737,13 @@ rxjs@6: dependencies: tslib "^1.9.0" +rxjs@^7.8.1: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== + dependencies: + tslib "^2.1.0" + safe-array-concat@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.1.tgz#91686a63ce3adbea14d61b14c99572a8ff84754c" @@ -5547,6 +5798,13 @@ sc-istanbul@^0.4.5: which "^1.1.1" wordwrap "^1.0.0" +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== + dependencies: + loose-envify "^1.1.0" + scrypt-js@3.0.1, scrypt-js@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" @@ -5684,6 +5942,15 @@ simple-get@^3.0.3: once "^1.3.1" simple-concat "^1.0.0" +simple-get@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" + integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== + dependencies: + decompress-response "^6.0.0" + once "^1.3.1" + simple-concat "^1.0.0" + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -6174,6 +6441,11 @@ tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.1.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + tsort@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/tsort/-/tsort-0.0.1.tgz#e2280f5e817f8bf4275657fd0f9aebd44f5a2786" @@ -6362,6 +6634,15 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +usb@2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/usb/-/usb-2.9.0.tgz#8ae3b175f93bee559400bff33491eee63406b6a2" + integrity sha512-G0I/fPgfHUzWH8xo2KkDxTTFruUWfppgSFJ+bQxz/kVY2x15EQ/XDB7dqD1G432G4gBG4jYQuF3U7j/orSs5nw== + dependencies: + "@types/w3c-web-usb" "^1.0.6" + node-addon-api "^6.0.0" + node-gyp-build "^4.5.0" + usb@^1.6.3: version "1.9.2" resolved "https://registry.yarnpkg.com/usb/-/usb-1.9.2.tgz#fb6b36f744ecc707a196c45a6ec72442cb6f2b73" @@ -6394,6 +6675,11 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +utility-types@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b" + integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg== + uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" From 68faca9dc1a71653f90e76b1d53dac4290a5c496 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Mon, 27 Nov 2023 16:27:25 +1000 Subject: [PATCH 02/40] Fix mocha issue --- .mocharc.json | 7 +++++++ package.json | 4 ++-- scripts/bootstrap/README.md | 2 +- scripts/e2e/README.md | 2 +- scripts/localdev/README.md | 2 +- 5 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 .mocharc.json diff --git a/.mocharc.json b/.mocharc.json new file mode 100644 index 00000000..5c5a28b0 --- /dev/null +++ b/.mocharc.json @@ -0,0 +1,7 @@ +{ + "extensions": ["ts"], + "node-option": [ + "experimental-specifier-resolution=node", + "loader=ts-node/esm" + ] +} \ No newline at end of file diff --git a/package.json b/package.json index b4e5ca0a..2e6cab40 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,8 @@ "lint": "forge fmt", "local:start": "cd scripts/localdev; ./start.sh", "local:setup": "cd scripts/localdev; ./deploy.sh", - "local:test": "cd scripts/localdev; npx mocha --require mocha-suppress-logs ../e2e/", - "local:ci": "cd scripts/localdev; ./ci.sh && ./deploy.sh && npx mocha --require mocha-suppress-logs ../e2e/ && ./stop.sh", + "local:test": "cd scripts/localdev; npx mocha --require mocha-suppress-logs ../e2e/e2e.ts", + "local:ci": "cd scripts/localdev; ./ci.sh && ./deploy.sh && npx mocha --require mocha-suppress-logs ../e2e/e2e.ts && ./stop.sh", "local:chainonly": "cd scripts/localdev; LOCAL_CHAIN_ONLY=true ./start.sh", "local:axelaronly": "cd scripts/localdev; node axelar_setup.js", "stop": "cd scripts/localdev; ./stop.sh" diff --git a/scripts/bootstrap/README.md b/scripts/bootstrap/README.md index 2dda4bc7..f6b9d3df 100644 --- a/scripts/bootstrap/README.md +++ b/scripts/bootstrap/README.md @@ -197,5 +197,5 @@ TEST_ACCOUNT_SECRET= ``` 13. Test bridge functions ``` -npx mocha --require mocha-suppress-logs ../e2e/ 2>&1 | tee -a bootstrap.out +npx mocha --require mocha-suppress-logs ../e2e/e2e.ts 2>&1 | tee -a bootstrap.out ``` \ No newline at end of file diff --git a/scripts/e2e/README.md b/scripts/e2e/README.md index 8fb2cd88..e3c39e80 100644 --- a/scripts/e2e/README.md +++ b/scripts/e2e/README.md @@ -51,5 +51,5 @@ TEST_ACCOUNT_SECRET= 3. Run end to end tests ``` -npx mocha --require mocha-suppress-logs . +npx mocha --require mocha-suppress-logs ./e2e.ts ``` \ No newline at end of file diff --git a/scripts/localdev/README.md b/scripts/localdev/README.md index fb294cab..00c6d7d9 100644 --- a/scripts/localdev/README.md +++ b/scripts/localdev/README.md @@ -31,5 +31,5 @@ The addresses of deployed contracts will be saved in: To run end to end tests against local development network: ``` -npx mocha --require mocha-suppress-logs ../e2e/ +npx mocha --require mocha-suppress-logs ../e2e/e2e.ts ``` \ No newline at end of file From ac227d3eaccc21a9de9ec30cb5a2ebb14f34f8d4 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Sun, 3 Dec 2023 00:13:52 +1000 Subject: [PATCH 03/40] Fix issues --- package.json | 8 +- scripts/bootstrap/.env.example | 90 +++----- scripts/bootstrap/0_pre_validation.ts | 148 +++++++++++++ scripts/bootstrap/1_deployer_funding.ts | 76 ++++--- scripts/bootstrap/2_deployment_validation.ts | 2 - scripts/bootstrap/6_imx_burning.ts | 100 ++++----- scripts/bootstrap/7_imx_rebalancing.ts | 53 +++-- scripts/bootstrap/9_test_preparation.ts | 71 ++++++ scripts/bootstrap/README.md | 134 +++++------- scripts/deploy/.env.example | 83 +++---- scripts/deploy/README.md | 82 +++---- scripts/deploy/child_deployment.ts | 207 ++++++++++++------ scripts/deploy/child_initialisation.ts | 65 +++--- scripts/deploy/root_deployment.ts | 176 ++++++++++----- scripts/deploy/root_initialisation.ts | 86 +++----- .../e2e/.root.bridge.contracts.json.example | 3 +- scripts/e2e/README.md | 5 +- scripts/e2e/e2e.ts | 110 +++++++--- scripts/helpers/helpers.ts | 81 ++++++- scripts/localdev/.env.local | 84 +++---- scripts/localdev/README.md | 2 +- scripts/localdev/childchain.config.ts | 1 + scripts/localdev/childchain_setup.ts | 9 +- scripts/localdev/deploy.sh | 5 +- scripts/localdev/rootchain.config.ts | 1 + scripts/localdev/rootchain_setup.ts | 49 ++--- scripts/localdev/start.sh | 5 +- 27 files changed, 1001 insertions(+), 735 deletions(-) create mode 100644 scripts/bootstrap/0_pre_validation.ts create mode 100644 scripts/bootstrap/9_test_preparation.ts diff --git a/package.json b/package.json index 2e6cab40..10de3b11 100644 --- a/package.json +++ b/package.json @@ -10,11 +10,11 @@ "test": "forge test", "lint": "forge fmt", "local:start": "cd scripts/localdev; ./start.sh", - "local:setup": "cd scripts/localdev; ./deploy.sh", - "local:test": "cd scripts/localdev; npx mocha --require mocha-suppress-logs ../e2e/e2e.ts", - "local:ci": "cd scripts/localdev; ./ci.sh && ./deploy.sh && npx mocha --require mocha-suppress-logs ../e2e/e2e.ts && ./stop.sh", + "local:setup": "cd scripts/localdev; rm -rf .child.bridge.contracts.json .root.bridge.contracts.json; ./deploy.sh", + "local:test": "cd scripts/localdev; LONG_WAIT=0 SHORT_WAIT=0 npx mocha --require mocha-suppress-logs ../e2e/e2e.ts", + "local:ci": "cd scripts/localdev; rm -rf .child.bridge.contracts.json .root.bridge.contracts.json; ./ci.sh && ./deploy.sh && LONG_WAIT=0 SHORT_WAIT=0 npx mocha --require mocha-suppress-logs ../e2e/e2e.ts && ./stop.sh", "local:chainonly": "cd scripts/localdev; LOCAL_CHAIN_ONLY=true ./start.sh", - "local:axelaronly": "cd scripts/localdev; node axelar_setup.js", + "local:axelaronly": "cd scripts/localdev; npx ts-node axelar_setup.ts", "stop": "cd scripts/localdev; ./stop.sh" }, "author": "", diff --git a/scripts/bootstrap/.env.example b/scripts/bootstrap/.env.example index 1806dcaf..c64b22bb 100644 --- a/scripts/bootstrap/.env.example +++ b/scripts/bootstrap/.env.example @@ -1,82 +1,44 @@ -# Set prior to 1_deployer_funding.js +# Set prior to 0_pre_validation.js +# Name of the child chain MUST match Axelar's definition. CHILD_CHAIN_NAME= +# The RPC URL of child chain. CHILD_RPC_URL= +# The chain ID of the child chain. CHILD_CHAIN_ID= +# Name of the root chain MUST match Axelar's definition. ROOT_CHAIN_NAME= +# The RPC URL of root chain. ROOT_RPC_URL= +# The chain ID of the root chain. ROOT_CHAIN_ID= -## The admin EOA address on the child chain. -CHILD_ADMIN_ADDR= -## The private key for the admin EOA or "ledger" if using hardware wallet. -CHILD_ADMIN_EOA_SECRET= -## The ledger index for the admin EOA, required if using ledger. -CHILD_ADMIN_EOA_LEDGER_INDEX= -## The deployer address on child chain. -CHILD_DEPLOYER_ADDR= -## The private key for the deployer on child chain or "ledger" if using hardware wallet. -CHILD_DEPLOYER_SECRET= -## The ledger index for the deployer on child chain, required if using ledger. -CHILD_DEPLOYER_LEDGER_INDEX= -## The amount of fund deployer required on L2, unit is in IMX or 10^18 Wei. -CHILD_DEPLOYER_FUND= -## The deployer address on root chain. -ROOT_DEPLOYER_ADDR= -## The private key for the deployer on root chain or "ledger" if using hardware wallet. -ROOT_DEPLOYER_SECRET= -## The ledger index for the deployer on root chain, required if using ledger. -ROOT_DEPLOYER_LEDGER_INDEX= -## The private key for rate admin or "ledger" if using hardware wallet. -ROOT_BRIDGE_RATE_ADMIN_SECRET= -## The ledger index for the rate admin, required if using ledger. -ROOT_BRIDGE_RATE_ADMIN_LEDGER_INDEX= +## The deployer address on child & root chains. +DEPLOYER_ADDR= +## The private key for the deployer on child & root chains or "ledger" if using hardware wallet. +DEPLOYER_SECRET= +## The ledger index for the deployer on child & root chains, required if using ledger. +DEPLOYER_LEDGER_INDEX= +## The nonce reserved deployer address on child & root chains. +NONCE_RESERVED_DEPLOYER_ADDR= +## The nonce reserved deployer, or "ledger" if using hardware wallet. +NONCE_RESERVED_DEPLOYER_SECRET= +## The ledger index for the nonce reserved deployer. +NONCE_RESERVED_DEPLOYER_INDEX= +## The reserved nonce for token template deployment. +NONCE_RESERVED= ## The IMX token address on root chain. ROOT_IMX_ADDR= ## The Wrapped ETH token address on the root chain. ROOT_WETH_ADDR= -## The Axelar address for receive initial funding on the child chain. +## The Axelar address to receive initial funding on the child chain. AXELAR_EOA= ## The amount of fund Axelar requested, unit is in IMX or 10^18 Wei. AXELAR_FUND= +## The amount of fund deployer to be left with after bootstrapping on L2, unit is in IMX or 10^18 Wei. +CHILD_DEPLOYER_FUND= +## The amount of fund nonce reserved deployer required on L2, unit is in IMX or 10^18 Wei. +CHILD_NONCE_RESERVED_DEPLOYER_FUND= ## The maximum amount of IMX that can be deposited to L2, unit is in IMX or 10^18 Wei. IMX_DEPOSIT_LIMIT= -## The address to perform child bridge upgrade. -CHILD_PROXY_ADMIN= -## The address to be assigned with DEFAULT_ADMIN_ROLE in child bridge. -CHILD_BRIDGE_DEFAULT_ADMIN= -## The address to be assigned with PAUSER_ROLE in child bridge. -CHILD_BRIDGE_PAUSER= -## The address to be assigned with UNPAUSER_ROLE in child bridge. -CHILD_BRIDGE_UNPAUSER= -## The address to be assigned with ADAPTOR_MANAGER_ROLE in child bridge. -CHILD_BRIDGE_ADAPTOR_MANAGER= -## The address to be assigned with DEFAULT_ADMIN_ROLE in child adaptor. -CHILD_ADAPTOR_DEFAULT_ADMIN= -## The address to be assigned with BRIDGE_MANAGER_ROLE in child adaptor. -CHILD_ADAPTOR_BRIDGE_MANAGER= -## The address to be assigned with GAS_SERVICE_MANAGER_ROLE in child adaptor. -CHILD_ADAPTOR_GAS_SERVICE_MANAGER= -## The address to be assigned with TARGET_MANAGER_ROLE in child adaptor. -CHILD_ADAPTOR_TARGET_MANAGER= -## The address to perform root adaptor upgrade. -ROOT_PROXY_ADMIN= -## The address to be assigned with DEFAULT_ADMIN_ROLE in root bridge. -ROOT_BRIDGE_DEFAULT_ADMIN= -## The address to be assigned with PAUSER_ROLE in root bridge. -ROOT_BRIDGE_PAUSER= -## The address to be assigned with UNPAUSER_ROLE in root bridge. -ROOT_BRIDGE_UNPAUSER= -## The address to be assigned with VARIABLE_MANAGER_ROLE in root bridge. -ROOT_BRIDGE_VARIABLE_MANAGER= -## The address to be assigned with ADAPTOR_MANAGER_ROLE in root bridge. -ROOT_BRIDGE_ADAPTOR_MANAGER= -## The address to be assigned with DEFAULT_ADMIN_ROLE in root adaptor. -ROOT_ADAPTOR_DEFAULT_ADMIN= -## The address to be assigned with BRIDGE_MANAGER_ROLE in root adaptor. -ROOT_ADAPTOR_BRIDGE_MANAGER= -## The address to be assigned with GAS_SERVICE_MANAGER_ROLE in root adaptor. -ROOT_ADAPTOR_GAS_SERVICE_MANAGER= -## The address to be assigned with TARGET_MANAGER_ROLE in root adaptor. -ROOT_ADAPTOR_TARGET_MANAGER= ## The capacity of the rate limit policy of IMX token, unit is in 10^18. RATE_LIMIT_IMX_CAPACITY= ## The refill rate of the rate limit policy of IMX token, unit is in 10^18. diff --git a/scripts/bootstrap/0_pre_validation.ts b/scripts/bootstrap/0_pre_validation.ts new file mode 100644 index 00000000..874a5515 --- /dev/null +++ b/scripts/bootstrap/0_pre_validation.ts @@ -0,0 +1,148 @@ +// Pre validation +import * as dotenv from "dotenv"; +dotenv.config(); +import { ethers } from "ethers"; +import { requireEnv, hasDuplicates } from "../helpers/helpers"; +import { LedgerSigner } from "../helpers/ledger_signer"; + +// The total supply of IMX +const TOTAL_SUPPLY = "2000000000"; + +// The contract ABI of IMX on L1. +const IMX_ABI = `[{"inputs":[{"internalType":"address","name":"minter","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]`; + +function tryThrow(errorMsg: string) { + if (process.env["THROW_ON_FAIL"] != null) { + throw(errorMsg); + } else { + console.log(errorMsg); + } +} + +async function run() { + console.log("=======Start Pre Validation======="); + + // Check environment variables + requireEnv("CHILD_CHAIN_NAME"); + let childRPCURL = requireEnv("CHILD_RPC_URL"); + let childChainID = requireEnv("CHILD_CHAIN_ID"); + requireEnv("ROOT_CHAIN_NAME"); + let rootRPCURL = requireEnv("ROOT_RPC_URL"); + let rootChainID = requireEnv("ROOT_CHAIN_ID"); + let deployerAddr = requireEnv("DEPLOYER_ADDR"); + let deployerSecret = requireEnv("DEPLOYER_SECRET"); + let reservedDeployerAddr = requireEnv("NONCE_RESERVED_DEPLOYER_ADDR"); + let reservedDeployerSecret = requireEnv("NONCE_RESERVED_DEPLOYER_SECRET"); + Number(requireEnv("NONCE_RESERVED")); + let rootIMXAddr = requireEnv("ROOT_IMX_ADDR"); + let rootWETHAddr = requireEnv("ROOT_WETH_ADDR"); + let axelarEOA = requireEnv("AXELAR_EOA"); + let axelarFund = requireEnv("AXELAR_FUND"); + let childDeployerFund = requireEnv("CHILD_DEPLOYER_FUND"); + let childReservedDeployerFund = requireEnv("CHILD_NONCE_RESERVED_DEPLOYER_FUND"); + let imxDepositLimit = requireEnv("IMX_DEPOSIT_LIMIT"); + requireEnv("RATE_LIMIT_IMX_CAPACITY"); + requireEnv("RATE_LIMIT_IMX_REFILL_RATE"); + requireEnv("RATE_LIMIT_IMX_LARGE_THRESHOLD"); + requireEnv("RATE_LIMIT_ETH_CAPACITY"); + requireEnv("RATE_LIMIT_ETH_REFILL_RATE"); + requireEnv("RATE_LIMIT_ETH_LARGE_THRESHOLD"); + requireEnv("RATE_LIMIT_USDC_ADDR"); + requireEnv("RATE_LIMIT_USDC_CAPACITY"); + requireEnv("RATE_LIMIT_USDC_REFILL_RATE"); + requireEnv("RATE_LIMIT_USDC_LARGE_THRESHOLD"); + requireEnv("RATE_LIMIT_GU_ADDR"); + requireEnv("RATE_LIMIT_GU_CAPACITY"); + requireEnv("RATE_LIMIT_GU_REFILL_RATE"); + requireEnv("RATE_LIMIT_GU_LARGE_THRESHOLD"); + requireEnv("RATE_LIMIT_CHECKMATE_ADDR"); + requireEnv("RATE_LIMIT_CHECKMATE_CAPACITY"); + requireEnv("RATE_LIMIT_CHECKMATE_REFILL_RATE"); + requireEnv("RATE_LIMIT_CHECKMATE_LARGE_THRESHOLD"); + requireEnv("RATE_LIMIT_GOG_ADDR"); + requireEnv("RATE_LIMIT_GOG_CAPACITY"); + requireEnv("RATE_LIMIT_GOG_REFILL_RATE"); + requireEnv("RATE_LIMIT_GOG_LARGE_THRESHOLD"); + + const childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); + const rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); + let deployerWallet; + if (deployerSecret == "ledger") { + let index = requireEnv("DEPLOYER_LEDGER_INDEX"); + const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; + deployerWallet = new LedgerSigner(childProvider, derivationPath); + } else { + deployerWallet = new ethers.Wallet(deployerSecret, childProvider); + } + let reservedWallet; + if (reservedDeployerSecret == "ledger") { + let index = requireEnv("NONCE_RESERVED_DEPLOYER_INDEX"); + const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; + reservedWallet = new LedgerSigner(childProvider, derivationPath); + } else { + reservedWallet = new ethers.Wallet(reservedDeployerSecret, childProvider); + } + + // Check deployer address matches deployer addr + let actualDeployerAddress = await deployerWallet.getAddress(); + if (actualDeployerAddress != deployerAddr) { + tryThrow("Deployer addresses mismatch, expect " + deployerAddr + " actual " + actualDeployerAddress); + } + let actualReservedDeployerAddress = await reservedWallet.getAddress(); + if (actualReservedDeployerAddress != reservedDeployerAddr) { + tryThrow("Reserved Nonce deployer addresses mismatch, expect " + reservedDeployerAddr + " actual " + actualReservedDeployerAddress); + } + + // Check duplicates + if (hasDuplicates([actualDeployerAddress, actualReservedDeployerAddress, axelarEOA])) { + throw("Duplicate address detected!"); + } + if (hasDuplicates([rootIMXAddr, rootWETHAddr])) { + throw("Duplicate address detected!"); + } + + // Check deployer fund on root chain and child chain. + let IMX = new ethers.Contract(rootIMXAddr, IMX_ABI, rootProvider); + let rootDeployerETHBalance = await rootProvider.getBalance(actualDeployerAddress); + let rootReservedDeployerETHBalance = await rootProvider.getBalance(actualReservedDeployerAddress); + if (rootDeployerETHBalance.lt(ethers.utils.parseEther("0.1"))) { + tryThrow("Deployer on root chain needs to have at least 0.1 ETH, got " + ethers.utils.formatEther(rootDeployerETHBalance)); + } + if (rootReservedDeployerETHBalance.lt(ethers.utils.parseEther("0.1"))) { + tryThrow("Reserved deployer on root chain needs to have at least 0.1 ETH, got " + ethers.utils.formatEther(rootReservedDeployerETHBalance)); + } + let rootDeployerIMXBalance = await IMX.balanceOf(actualDeployerAddress); + let axelarRequiredIMX = ethers.utils.parseEther(axelarFund); + let deployerRequiredIMX = ethers.utils.parseEther(childDeployerFund); + let reservedDeployerRequiredIMX = ethers.utils.parseEther(childReservedDeployerFund); + if (axelarRequiredIMX.lt(ethers.utils.parseEther("500.0"))) { + tryThrow("Axelar on child chain should request at least 500 IMX, got" + ethers.utils.formatEther(axelarRequiredIMX)); + } + if (deployerRequiredIMX.lt(ethers.utils.parseEther("500.0"))) { + tryThrow("Deployer on child chain should request at least 500 IMX, got" + ethers.utils.formatEther(deployerRequiredIMX)); + } + if (reservedDeployerRequiredIMX.lt(ethers.utils.parseEther("10.0"))) { + tryThrow("Reserved deployer on child chain should request at least 10 IMX, got" + ethers.utils.formatEther(reservedDeployerRequiredIMX)); + } + let extraIMX = ethers.utils.parseEther("100.0"); + let requiredIMX = axelarRequiredIMX.add(deployerRequiredIMX).add(reservedDeployerRequiredIMX).add(extraIMX); + if (rootDeployerIMXBalance.lt(requiredIMX)) { + tryThrow("Deployer on root chain needs to have at least " + ethers.utils.formatEther(requiredIMX) + " IMX, got " + ethers.utils.formatEther(rootDeployerIMXBalance)); + } + let childDeployerIMXBalance = await childProvider.getBalance(actualDeployerAddress); + if (!childDeployerIMXBalance.eq(ethers.utils.parseEther(TOTAL_SUPPLY))) { + tryThrow("Deployer on child chain needs to have 2B units of pre-mined IMX, got " + ethers.utils.formatEther(childDeployerIMXBalance)); + } + + // Check IMX deposit limit + let depositLimit = ethers.utils.parseEther(imxDepositLimit); + if (depositLimit.gt(ethers.utils.parseEther("200000000"))) { + tryThrow("Deposit limit should be at most 200m, got " + ethers.utils.formatEther(depositLimit)); + } + if (depositLimit.lt(ethers.utils.parseEther("2000000"))) { + tryThrow("Deposit limit should be at least 2m, got " + ethers.utils.formatEther(depositLimit)); + } + + console.log("=======End Pre Validation======="); +} +run(); \ No newline at end of file diff --git a/scripts/bootstrap/1_deployer_funding.ts b/scripts/bootstrap/1_deployer_funding.ts index 8e3b1341..a6a9aaca 100644 --- a/scripts/bootstrap/1_deployer_funding.ts +++ b/scripts/bootstrap/1_deployer_funding.ts @@ -11,61 +11,69 @@ async function run() { // Check environment variables let childRPCURL = requireEnv("CHILD_RPC_URL"); let childChainID = requireEnv("CHILD_CHAIN_ID"); - let adminEOASecret = requireEnv("CHILD_ADMIN_EOA_SECRET"); + let deployerSecret = requireEnv("DEPLOYER_SECRET"); + let reservedDeployerAddr = requireEnv("NONCE_RESERVED_DEPLOYER_ADDR"); + let reservedDeployerFund = requireEnv("CHILD_NONCE_RESERVED_DEPLOYER_FUND"); let axelarEOA = requireEnv("AXELAR_EOA"); let axelarFund = requireEnv("AXELAR_FUND"); - let deployerEOA = requireEnv("CHILD_DEPLOYER_ADDR"); - let deployerFund = requireEnv("CHILD_DEPLOYER_FUND"); - // Get admin EOA address + // Get deployer address const childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); - let adminWallet; - if (adminEOASecret == "ledger") { - let index = requireEnv("CHILD_ADMIN_EOA_LEDGER_INDEX"); + let childDeployerWallet; + if (deployerSecret == "ledger") { + let index = requireEnv("DEPLOYER_LEDGER_INDEX"); const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; - adminWallet = new LedgerSigner(childProvider, derivationPath); + childDeployerWallet = new LedgerSigner(childProvider, derivationPath); } else { - adminWallet = new ethers.Wallet(adminEOASecret, childProvider); + childDeployerWallet = new ethers.Wallet(deployerSecret, childProvider); } - let adminAddr = await adminWallet.getAddress(); - console.log("Admin address is: ", adminAddr); + let deployerAddr = await childDeployerWallet.getAddress(); + console.log("Deployer address is: ", deployerAddr); // Check duplicates - if (hasDuplicates([adminAddr, axelarEOA, deployerEOA])) { + if (hasDuplicates([deployerAddr, axelarEOA, reservedDeployerAddr])) { throw("Duplicate address detected!"); } // Execute + console.log("Nonce reserved deployer now has: ", ethers.utils.formatEther(await childProvider.getBalance(reservedDeployerAddr))); console.log("Axelar EOA now has: ", ethers.utils.formatEther(await childProvider.getBalance(axelarEOA))); - console.log("Deployer EOA now has: ", ethers.utils.formatEther(await childProvider.getBalance(deployerEOA))); console.log("Fund Axelar and deployer on child chain in..."); await waitForConfirmation(); - let [priorityFee, maxFee] = await getFee(childProvider); - console.log("Transfer value to axelar..."); - let resp = await adminWallet.sendTransaction({ - to: axelarEOA, - value: ethers.utils.parseEther(axelarFund), - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }) - console.log("Transaction submitted: " + JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, childProvider); + if ((await childProvider.getBalance(reservedDeployerAddr)).gte(ethers.utils.parseEther(reservedDeployerFund))) { + console.log("Nonce reserved deployer has already got requested amount, skip."); + } else { + let [priorityFee, maxFee] = await getFee(childProvider); + console.log("Transfer value to reserved nonce deployer..."); + let resp = await childDeployerWallet.sendTransaction({ + to: reservedDeployerAddr, + value: ethers.utils.parseEther(reservedDeployerFund), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }) + console.log("Transaction submitted: " + JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, childProvider); + } - [priorityFee, maxFee] = await getFee(childProvider); - console.log("Transfer value to deployer..."); - resp = await adminWallet.sendTransaction({ - to: deployerEOA, - value: ethers.utils.parseEther(deployerFund), - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }) - console.log("Transaction submitted: " + JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, childProvider); + if ((await childProvider.getBalance(axelarEOA)).gte(ethers.utils.parseEther(axelarFund))) { + console.log("Axelar has already got requested amount, skip."); + } else { + let [priorityFee, maxFee] = await getFee(childProvider); + console.log("Transfer value to axelar..."); + let resp = await childDeployerWallet.sendTransaction({ + to: axelarEOA, + value: ethers.utils.parseEther(axelarFund), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }) + console.log("Transaction submitted: " + JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, childProvider); + } // Print target balance + console.log("Nonce reserved deployer now has: ", ethers.utils.formatEther(await childProvider.getBalance(reservedDeployerAddr))); console.log("Axelar EOA now has: ", ethers.utils.formatEther(await childProvider.getBalance(axelarEOA))); - console.log("Deployer EOA now has: ", ethers.utils.formatEther(await childProvider.getBalance(deployerEOA))); console.log("=======End Deployer Funding======="); } diff --git a/scripts/bootstrap/2_deployment_validation.ts b/scripts/bootstrap/2_deployment_validation.ts index b6b2835b..d49d8469 100644 --- a/scripts/bootstrap/2_deployment_validation.ts +++ b/scripts/bootstrap/2_deployment_validation.ts @@ -3,8 +3,6 @@ import * as dotenv from "dotenv"; dotenv.config(); import { ethers } from "ethers"; import { requireEnv, hasDuplicates, requireNonEmptyCode } from "../helpers/helpers"; -import { LedgerSigner } from "../helpers/ledger_signer"; -import * as fs from "fs"; async function run() { console.log("=======Start Deployment Validation======="); diff --git a/scripts/bootstrap/6_imx_burning.ts b/scripts/bootstrap/6_imx_burning.ts index d67ddbd4..6dd1b16d 100644 --- a/scripts/bootstrap/6_imx_burning.ts +++ b/scripts/bootstrap/6_imx_burning.ts @@ -2,9 +2,8 @@ import * as dotenv from "dotenv"; dotenv.config(); import { ethers } from "ethers"; -import { requireEnv, waitForConfirmation, hasDuplicates, waitForReceipt, getFee } from "../helpers/helpers"; +import { requireEnv, waitForConfirmation, hasDuplicates, waitForReceipt, getFee, getContract, getChildContracts } from "../helpers/helpers"; import { LedgerSigner } from "../helpers/ledger_signer"; -import * as fs from "fs"; async function run() { console.log("=======Start IMX Burning======="); @@ -12,85 +11,82 @@ async function run() { // Check environment variables let childRPCURL = requireEnv("CHILD_RPC_URL"); let childChainID = requireEnv("CHILD_CHAIN_ID"); - let adminEOASecret = requireEnv("CHILD_ADMIN_EOA_SECRET"); + let deployerSecret = requireEnv("DEPLOYER_SECRET"); let multisigAddr = requireEnv("MULTISIG_CONTRACT_ADDRESS"); let imxDepositLimit = requireEnv("IMX_DEPOSIT_LIMIT"); + let deployerFund = requireEnv("CHILD_DEPLOYER_FUND"); // Read from contract file. - let data = fs.readFileSync(".child.bridge.contracts.json", 'utf-8'); - let childContracts = JSON.parse(data); + let childContracts = getChildContracts(); let childBridgeAddr = childContracts.CHILD_BRIDGE_ADDRESS; - // Get admin address + // Get deployer address const childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); - let adminWallet; - if (adminEOASecret == "ledger") { - let index = requireEnv("CHILD_ADMIN_EOA_LEDGER_INDEX"); + let childDeployerWallet; + if (deployerSecret == "ledger") { + let index = requireEnv("DEPLOYER_LEDGER_INDEX"); const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; - adminWallet = new LedgerSigner(childProvider, derivationPath); + childDeployerWallet = new LedgerSigner(childProvider, derivationPath); } else { - adminWallet = new ethers.Wallet(adminEOASecret, childProvider); + childDeployerWallet = new ethers.Wallet(deployerSecret, childProvider); } - let adminAddr = await adminWallet.getAddress(); - console.log("Admin address is: ", adminAddr); + let deployerAddr = await childDeployerWallet.getAddress(); + console.log("Deployer address is: ", deployerAddr); // Check duplicates - if (hasDuplicates([adminAddr, childBridgeAddr, multisigAddr])) { + if (hasDuplicates([deployerAddr, childBridgeAddr, multisigAddr])) { throw("Duplicate address detected!"); } // Execute - let adminBal = await childProvider.getBalance(adminAddr); + let deployerBal = await childProvider.getBalance(deployerAddr); let bridgeBal = await childProvider.getBalance(childBridgeAddr); let multisigBal = await childProvider.getBalance(multisigAddr); - console.log("Admin balance: ", ethers.utils.formatEther(adminBal)); + console.log("Deployer balance: ", ethers.utils.formatEther(deployerBal)); console.log("Bridge balance: ", ethers.utils.formatEther(bridgeBal)); console.log("Multisig balance: ", ethers.utils.formatEther(multisigBal)); - if (adminBal.lt(ethers.utils.parseEther("0.01"))) { - console.log("IMX Burning has already been done, skip.") - return; - } - console.log("Burn IMX in..."); await waitForConfirmation(); - let childBridgeObj = JSON.parse(fs.readFileSync('../../out/ChildERC20Bridge.sol/ChildERC20Bridge.json', 'utf8')); - let childBridge = new ethers.Contract(childBridgeAddr, childBridgeObj.abi, childProvider); - - console.log("Transfer " + imxDepositLimit + " IMX to child bridge..."); - let [priorityFee, maxFee] = await getFee(childProvider); - let resp = await childBridge.connect(adminWallet).privilegedDeposit({ - value: ethers.utils.parseEther(imxDepositLimit), - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }) - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)) - await waitForReceipt(resp.hash, childProvider); - - adminBal = await childProvider.getBalance(adminAddr); - bridgeBal = await childProvider.getBalance(childBridgeAddr); - multisigBal = await childProvider.getBalance(multisigAddr); - console.log("Admin balance: ", ethers.utils.formatEther(adminBal)); - console.log("Bridge balance: ", ethers.utils.formatEther(bridgeBal)); - console.log("Multisig balance: ", ethers.utils.formatEther(multisigBal)); + if ((await childProvider.getBalance(childBridgeAddr)).gte(ethers.utils.parseEther(imxDepositLimit))) { + console.log("Child bridge has already got burned IMX, skip."); + } else { + console.log("Transfer " + imxDepositLimit + " IMX to child bridge..."); + let [priorityFee, maxFee] = await getFee(childProvider); + let childBridge = getContract("ChildERC20Bridge", childBridgeAddr, childProvider); + let resp = await childBridge.connect(childDeployerWallet).privilegedDeposit({ + value: ethers.utils.parseEther(imxDepositLimit), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }) + console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)) + await waitForReceipt(resp.hash, childProvider); + } // Transfer to multisig - console.log("Transfer remaining to multisig..."); - [priorityFee, maxFee] = await getFee(childProvider); - resp = await adminWallet.sendTransaction({ - to: multisigAddr, - value: adminBal.sub(ethers.utils.parseEther("0.01")), - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)) - await waitForReceipt(resp.hash, childProvider); + let remain = ethers.utils.parseEther(deployerFund); + deployerBal = await childProvider.getBalance(deployerAddr); + if (deployerBal.lte(remain)) { + console.log("Multisig has already got remaining burned IMX, skip."); + } else { + console.log("Transfer remaining to multisig..."); + let toTransfer = deployerBal.sub(remain); + let [priorityFee, maxFee] = await getFee(childProvider); + let resp = await childDeployerWallet.sendTransaction({ + to: multisigAddr, + value: toTransfer, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)) + await waitForReceipt(resp.hash, childProvider); + } - adminBal = await childProvider.getBalance(adminAddr); + deployerBal = await childProvider.getBalance(deployerAddr); bridgeBal = await childProvider.getBalance(childBridgeAddr); multisigBal = await childProvider.getBalance(multisigAddr); - console.log("Admin balance: ", ethers.utils.formatEther(adminBal)); + console.log("Deployer balance: ", ethers.utils.formatEther(deployerBal)); console.log("Bridge balance: ", ethers.utils.formatEther(bridgeBal)); console.log("Multisig balance: ", ethers.utils.formatEther(multisigBal)); diff --git a/scripts/bootstrap/7_imx_rebalancing.ts b/scripts/bootstrap/7_imx_rebalancing.ts index 01f7d805..4d18e9c3 100644 --- a/scripts/bootstrap/7_imx_rebalancing.ts +++ b/scripts/bootstrap/7_imx_rebalancing.ts @@ -2,9 +2,8 @@ import * as dotenv from "dotenv"; dotenv.config(); import { ethers } from "ethers"; -import { requireEnv, waitForConfirmation, waitForReceipt, getFee, hasDuplicates } from "../helpers/helpers"; +import { requireEnv, waitForConfirmation, waitForReceipt, getFee, hasDuplicates, getChildContracts, getRootContracts } from "../helpers/helpers"; import { LedgerSigner } from "../helpers/ledger_signer"; -import * as fs from "fs"; // The total supply of IMX const TOTAL_SUPPLY = "2000000000"; @@ -20,37 +19,35 @@ async function run() { let childChainID = requireEnv("CHILD_CHAIN_ID"); let rootRPCURL = requireEnv("ROOT_RPC_URL"); let rootChainID = requireEnv("ROOT_CHAIN_ID"); - let rootDeployerSecret = requireEnv("ROOT_DEPLOYER_SECRET"); + let deployerSecret = requireEnv("DEPLOYER_SECRET"); let multisigAddr = requireEnv("MULTISIG_CONTRACT_ADDRESS"); let rootIMXAddr = requireEnv("ROOT_IMX_ADDR"); // Read from contract file. - let data = fs.readFileSync(".child.bridge.contracts.json", 'utf-8'); - let childContracts = JSON.parse(data); + let childContracts = getChildContracts(); let childBridgeAddr = childContracts.CHILD_BRIDGE_ADDRESS; - data = fs.readFileSync(".root.bridge.contracts.json", 'utf-8'); - let rootContracts = JSON.parse(data); + let rootContracts = getRootContracts(); let rootBridgeAddr = rootContracts.ROOT_BRIDGE_ADDRESS; - // Get admin address + // Get deployer address const childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); const rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); - let adminWallet; - if (rootDeployerSecret == "ledger") { - let index = requireEnv("ROOT_DEPLOYER_LEDGER_INDEX"); + let rootDeployerWallet; + if (deployerSecret == "ledger") { + let index = requireEnv("DEPLOYER_LEDGER_INDEX"); const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; - adminWallet = new LedgerSigner(rootProvider, derivationPath); + rootDeployerWallet = new LedgerSigner(rootProvider, derivationPath); } else { - adminWallet = new ethers.Wallet(rootDeployerSecret, rootProvider); + rootDeployerWallet = new ethers.Wallet(deployerSecret, rootProvider); } - let adminAddr = await adminWallet.getAddress(); - console.log("Deployer address is: ", adminAddr); + let deployerAddr = await rootDeployerWallet.getAddress(); + console.log("Deployer address is: ", deployerAddr); // Check duplicates - if (hasDuplicates([adminAddr, childBridgeAddr, multisigAddr])) { + if (hasDuplicates([deployerAddr, childBridgeAddr, multisigAddr])) { throw("Duplicate address detected!"); } - if (hasDuplicates([adminAddr, rootBridgeAddr, rootIMXAddr])) { + if (hasDuplicates([deployerAddr, rootBridgeAddr, rootIMXAddr])) { throw("Duplicate address detected!"); } @@ -62,29 +59,29 @@ async function run() { console.log("The amount to balance on L1 is: ", ethers.utils.formatEther(balanceAmt)); let IMX = new ethers.Contract(rootIMXAddr, IMX_ABI, rootProvider); - let adminL1Balance = await IMX.balanceOf(adminAddr); + let deployerL1Balance = await IMX.balanceOf(deployerAddr); let rootBridgeBalance = await IMX.balanceOf(rootBridgeAddr); - console.log("Admin L1 IMX balance: ", ethers.utils.formatEther(adminL1Balance)); + console.log("Deployer L1 IMX balance: ", ethers.utils.formatEther(deployerL1Balance)); console.log("Root bridge L1 IMX balance: ", ethers.utils.formatEther(rootBridgeBalance)); - - if (rootBridgeBalance.gt(ethers.utils.parseEther("1.0"))) { - console.log("IMX Rebalancing has already been done, skip.") - return; - } console.log("Rebalance in..."); await waitForConfirmation(); + if (deployerL1Balance.lt(balanceAmt)) { + console.log("Insufficient balance to rebalance (already balanced?), skip."); + return; + } + // Rebalancing - console.log("Transfer...") - let resp = await IMX.connect(adminWallet).transfer(rootBridgeAddr, balanceAmt); + console.log("Rebalancing...") + let resp = await IMX.connect(rootDeployerWallet).transfer(rootBridgeAddr, balanceAmt); console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)) await waitForReceipt(resp.hash, rootProvider); - adminL1Balance = await IMX.balanceOf(adminAddr); + deployerL1Balance = await IMX.balanceOf(deployerAddr); rootBridgeBalance = await IMX.balanceOf(rootBridgeAddr); - console.log("Admin L1 IMX balance: ", ethers.utils.formatEther(adminL1Balance)); + console.log("Deployer L1 IMX balance: ", ethers.utils.formatEther(deployerL1Balance)); console.log("Root bridge L1 IMX balance: ", ethers.utils.formatEther(rootBridgeBalance)); console.log("=======End IMX Rebalancing======="); diff --git a/scripts/bootstrap/9_test_preparation.ts b/scripts/bootstrap/9_test_preparation.ts new file mode 100644 index 00000000..49bac642 --- /dev/null +++ b/scripts/bootstrap/9_test_preparation.ts @@ -0,0 +1,71 @@ +// Prepare for test +import * as dotenv from "dotenv"; +dotenv.config(); +import { ethers } from "ethers"; +import { deployRootContract, getContract, getRootContracts, requireEnv, saveRootContracts, waitForConfirmation, waitForReceipt } from "../helpers/helpers"; +import { LedgerSigner } from "../helpers/ledger_signer"; + +async function run() { + console.log("=======Start Test Preparation======="); + + // Check environment variables + let rootRPCURL = requireEnv("ROOT_RPC_URL"); + let rootChainID = requireEnv("ROOT_CHAIN_ID"); + let deployerSecret = requireEnv("DEPLOYER_SECRET"); + let testAccountKey = requireEnv("TEST_ACCOUNT_SECRET"); + + // Get deployer address + const rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); + let rootDeployerWallet; + if (deployerSecret == "ledger") { + let index = requireEnv("DEPLOYER_LEDGER_INDEX"); + const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; + rootDeployerWallet = new LedgerSigner(rootProvider, derivationPath); + } else { + rootDeployerWallet = new ethers.Wallet(deployerSecret, rootProvider); + } + let deployerAddr = await rootDeployerWallet.getAddress(); + console.log("Deployer address is: ", deployerAddr); + + let rootTestWallet = new ethers.Wallet(testAccountKey, rootProvider); + + let rootContracts = getRootContracts(); + let rootBridge = getContract("RootERC20BridgeFlowRate", rootContracts.ROOT_BRIDGE_ADDRESS, rootProvider); + + // Execute + console.log("Prepare test in..."); + await waitForConfirmation(); + + // Deploy a custom token + let rootCustomToken; + if (rootContracts.ROOT_TEST_CUSTOM_TOKEN != "") { + console.log("Root test custom token has already been deployed to: " + rootContracts.ROOT_TEST_CUSTOM_TOKEN + ", skip."); + rootCustomToken = getContract("ERC20PresetMinterPauser", rootContracts.ROOT_TEST_CUSTOM_TOKEN, rootProvider); + } else { + console.log("Deploy root test custom token..."); + rootCustomToken = await deployRootContract("ERC20PresetMinterPauser", rootDeployerWallet, null, "Custom Token", "CTK"); + await waitForReceipt(rootCustomToken.deployTransaction.hash, rootProvider); + console.log("Custom token deployed to: ", rootCustomToken) + } + rootContracts.ROOT_TEST_CUSTOM_TOKEN=rootCustomToken.address; + saveRootContracts(rootContracts); + console.log("Deployed to ROOT_TEST_CUSTOM_TOKEN: ", rootCustomToken.address); + + // Mint tokens + console.log("Mint tokens..."); + let resp = await rootCustomToken.connect(rootDeployerWallet).mint(rootTestWallet.address, ethers.utils.parseEther("1000.0").toBigInt()); + await waitForReceipt(resp.hash, rootProvider); + + console.log("Set rate control..."); + // Set rate control + resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( + rootCustomToken.address, + ethers.utils.parseEther("20016.0"), + ethers.utils.parseEther("5.56"), + ethers.utils.parseEther("10008.0") + ); + await waitForReceipt(resp.hash, rootProvider); + + console.log("=======End Test Preparation======="); +} +run(); \ No newline at end of file diff --git a/scripts/bootstrap/README.md b/scripts/bootstrap/README.md index f6b9d3df..25d7242c 100644 --- a/scripts/bootstrap/README.md +++ b/scripts/bootstrap/README.md @@ -3,12 +3,11 @@ ## Prerequisite 1. Coordinate with Axelar to obtain their admin address for initial funding as well as the desired amount in $IMX. (500 IMX in previous discussion). 2. Obtain the deployer account on both child chain and root chain. -3. Obtain the amount to fund deployer on child chain in $IMX. (500 IMX by default). -4. Coordinate with security to obtain the addresses for different roles. -5. Fund deployer with `ETH` and `IMX` on root chain. (As a rule of thumb, _0.1 ETH and 1100 IMX_ (TBD)). -6. Fund a test account with `ETH` and `IMX` on root chain. (As a rule of thumb, _0.1 ETH and 50 IMX_ (TBD)). -7. Fund a rate admin account with `ETH` on root chain. (As a rule of thumb, _0.1 ETH_ (TBD)). - +3. Obtain the nonce reserved deployer account and reserved nonce on both child chain and root chain. +4. Obtain the amount to fund reserved deployer on child chain in $IMX. (10 IMX by default). +5. Obtain the amount to fund deployer on child chain in $IMX. (500 IMX by default). +6. Fund deployer with `ETH` and `IMX` on root chain. (As a rule of thumb, _0.1 ETH and 1100 IMX_ (TBD)). +7. Fund a test account with `ETH` and `IMX` on root chain. (As a rule of thumb, _0.1 ETH and 50 IMX_ (TBD)). ## Bootstrapping 0. Install dependency and compile contracts (Run in root directory) @@ -23,85 +22,47 @@ cp .env.example .env ``` 2. Set the following environment variables ``` -# Set prior to 1_deployer_funding.js +# Set prior to 0_pre_validation.js +# Name of the child chain MUST match Axelar's definition. CHILD_CHAIN_NAME= +# The RPC URL of child chain. CHILD_RPC_URL= +# The chain ID of the child chain. CHILD_CHAIN_ID= +# Name of the root chain MUST match Axelar's definition. ROOT_CHAIN_NAME= +# The RPC URL of root chain. ROOT_RPC_URL= +# The chain ID of the root chain. ROOT_CHAIN_ID= -## The admin EOA address on the child chain. -CHILD_ADMIN_ADDR= -## The private key for the admin EOA or "ledger" if using hardware wallet. -CHILD_ADMIN_EOA_SECRET= -## The ledger index for the admin EOA, required if using ledger. -CHILD_ADMIN_EOA_LEDGER_INDEX= -## The deployer address on child chain. -CHILD_DEPLOYER_ADDR= -## The private key for the deployer on child chain or "ledger" if using hardware wallet. -CHILD_DEPLOYER_SECRET= -## The ledger index for the deployer on child chain, required if using ledger. -CHILD_DEPLOYER_LEDGER_INDEX= -## The amount of fund deployer required on L2, unit is in IMX or 10^18 Wei. -CHILD_DEPLOYER_FUND= -## The deployer address on root chain. -ROOT_DEPLOYER_ADDR= -## The private key for the deployer on root chain or "ledger" if using hardware wallet. -ROOT_DEPLOYER_SECRET= -## The ledger index for the deployer on root chain, required if using ledger. -ROOT_DEPLOYER_LEDGER_INDEX= -## The private key for rate admin or "ledger" if using hardware wallet. -ROOT_BRIDGE_RATE_ADMIN_SECRET= -## The ledger index for the rate admin, required if using ledger. -ROOT_BRIDGE_RATE_ADMIN_LEDGER_INDEX= +## The deployer address on child & root chains. +DEPLOYER_ADDR= +## The private key for the deployer on child & root chains or "ledger" if using hardware wallet. +DEPLOYER_SECRET= +## The ledger index for the deployer on child & root chains, required if using ledger. +DEPLOYER_LEDGER_INDEX= +## The nonce reserved deployer address on child & root chains. +NONCE_RESERVED_DEPLOYER_ADDR= +## The nonce reserved deployer, or "ledger" if using hardware wallet. +NONCE_RESERVED_DEPLOYER_SECRET= +## The ledger index for the nonce reserved deployer. +NONCE_RESERVED_DEPLOYER_INDEX= +## The reserved nonce for token template deployment. +NONCE_RESERVED= ## The IMX token address on root chain. ROOT_IMX_ADDR= ## The Wrapped ETH token address on the root chain. ROOT_WETH_ADDR= -## The Axelar address for receive initial funding on the child chain. +## The Axelar address to receive initial funding on the child chain. AXELAR_EOA= ## The amount of fund Axelar requested, unit is in IMX or 10^18 Wei. AXELAR_FUND= +## The amount of fund deployer to be left with after bootstrapping on L2, unit is in IMX or 10^18 Wei. +CHILD_DEPLOYER_FUND= +## The amount of fund nonce reserved deployer required on L2, unit is in IMX or 10^18 Wei. +CHILD_NONCE_RESERVED_DEPLOYER_FUND= ## The maximum amount of IMX that can be deposited to L2, unit is in IMX or 10^18 Wei. IMX_DEPOSIT_LIMIT= -## The address to perform child bridge upgrade. -CHILD_PROXY_ADMIN= -## The address to be assigned with DEFAULT_ADMIN_ROLE in child bridge. -CHILD_BRIDGE_DEFAULT_ADMIN= -## The address to be assigned with PAUSER_ROLE in child bridge. -CHILD_BRIDGE_PAUSER= -## The address to be assigned with UNPAUSER_ROLE in child bridge. -CHILD_BRIDGE_UNPAUSER= -## The address to be assigned with ADAPTOR_MANAGER_ROLE in child bridge. -CHILD_BRIDGE_ADAPTOR_MANAGER= -## The address to be assigned with DEFAULT_ADMIN_ROLE in child adaptor. -CHILD_ADAPTOR_DEFAULT_ADMIN= -## The address to be assigned with BRIDGE_MANAGER_ROLE in child adaptor. -CHILD_ADAPTOR_BRIDGE_MANAGER= -## The address to be assigned with GAS_SERVICE_MANAGER_ROLE in child adaptor. -CHILD_ADAPTOR_GAS_SERVICE_MANAGER= -## The address to be assigned with TARGET_MANAGER_ROLE in child adaptor. -CHILD_ADAPTOR_TARGET_MANAGER= -## The address to perform root adaptor upgrade. -ROOT_PROXY_ADMIN= -## The address to be assigned with DEFAULT_ADMIN_ROLE in root bridge. -ROOT_BRIDGE_DEFAULT_ADMIN= -## The address to be assigned with PAUSER_ROLE in root bridge. -ROOT_BRIDGE_PAUSER= -## The address to be assigned with UNPAUSER_ROLE in root bridge. -ROOT_BRIDGE_UNPAUSER= -## The address to be assigned with VARIABLE_MANAGER_ROLE in root bridge. -ROOT_BRIDGE_VARIABLE_MANAGER= -## The address to be assigned with ADAPTOR_MANAGER_ROLE in root bridge. -ROOT_BRIDGE_ADAPTOR_MANAGER= -## The address to be assigned with DEFAULT_ADMIN_ROLE in root adaptor. -ROOT_ADAPTOR_DEFAULT_ADMIN= -## The address to be assigned with BRIDGE_MANAGER_ROLE in root adaptor. -ROOT_ADAPTOR_BRIDGE_MANAGER= -## The address to be assigned with GAS_SERVICE_MANAGER_ROLE in root adaptor. -ROOT_ADAPTOR_GAS_SERVICE_MANAGER= -## The address to be assigned with TARGET_MANAGER_ROLE in root adaptor. -ROOT_ADAPTOR_TARGET_MANAGER= ## The capacity of the rate limit policy of IMX token, unit is in 10^18. RATE_LIMIT_IMX_CAPACITY= ## The refill rate of the rate limit policy of IMX token, unit is in 10^18. @@ -147,12 +108,16 @@ RATE_LIMIT_GOG_REFILL_RATE= ## The large threshold of the rate limit policy of GOG token, unit is in 10^18. RATE_LIMIT_GOG_LARGE_THRESHOLD= ``` -3. Fund deployer +3. Pre validation +``` +npx ts-node 0_pre_validation.ts 2>&1 | tee bootstrap.out +``` +4. Fund deployer ``` npx ts-node 1_deployer_funding.ts 2>&1 | tee bootstrap.out ``` -4. Wait for Axelar to deploy & setup their system and Security team to deploy & setup multisig wallet. -5. Set the following environment variables +5. Wait for Axelar to deploy & setup their system and Security team to deploy & setup multisig wallet. +6. Set the following environment variables ``` CHILD_GATEWAY_ADDRESS= CHILD_GAS_SERVICE_ADDRESS= @@ -160,8 +125,7 @@ MULTISIG_CONTRACT_ADDRESS= ROOT_GATEWAY_ADDRESS= ROOT_GAS_SERVICE_ADDRESS= ``` -6. Basic contract validation - +7. Basic contract validation If multisig is deployed: ``` npx ts-node 2_deployment_validation.ts 2>&1 | tee -a bootstrap.out @@ -170,32 +134,36 @@ If multisig isn't deployed: ``` SKIP_MULTISIG_CHECK=true npx ts-node 2_deployment_validation.ts 2>&1 | tee -a bootstrap.out ``` -7. Deploy bridge contracts on child and root chain. +8. Deploy bridge contracts on child and root chain. ``` npx ts-node 3_child_deployment.ts 2>&1 | tee -a bootstrap.out npx ts-node 4_root_deployment.ts 2>&1 | tee -a bootstrap.out ``` -8. Initialise bridge contracts on child chain. +9. Initialise bridge contracts on child chain. ``` npx ts-node 5_child_initialisation.ts 2>&1 | tee -a bootstrap.out ``` -9. IMX Burning +10. IMX Burning ``` npx ts-node 6_imx_burning.ts 2>&1 | tee -a bootstrap.out ``` -10. IMX Rebalancing +11. IMX Rebalancing ``` npx ts-node 7_imx_rebalancing.ts 2>&1 | tee -a bootstrap.out ``` -11. Initialise bridge contracts on root chain. +12. Initialise bridge contracts on root chain. ``` npx ts-node 8_root_initialisation.ts 2>&1 | tee -a bootstrap.out ``` -12. Set the following environment variable +13. Set the following environment variable ``` TEST_ACCOUNT_SECRET= ``` -13. Test bridge functions +14. Prepare for test +``` +npx ts-node 9_test_preparation.ts 2>&1 | tee -a bootstrap.out +``` +15. Test bridge functions ``` -npx mocha --require mocha-suppress-logs ../e2e/e2e.ts 2>&1 | tee -a bootstrap.out +LONG_WAIT=1200000 SHORT_WAIT=300000 npx mocha --require mocha-suppress-logs ../e2e/e2e.ts 2>&1 | tee -a bootstrap.out ``` \ No newline at end of file diff --git a/scripts/deploy/.env.example b/scripts/deploy/.env.example index 0fcc7293..d0b6c81f 100644 --- a/scripts/deploy/.env.example +++ b/scripts/deploy/.env.example @@ -1,70 +1,43 @@ -# Access to child and root chains. +# Name of the child chain MUST match Axelar's definition. CHILD_CHAIN_NAME= +# The RPC URL of child chain. CHILD_RPC_URL= +# The chain ID of the child chain. CHILD_CHAIN_ID= +# Name of the root chain MUST match Axelar's definition. ROOT_CHAIN_NAME= +# The RPC URL of root chain. ROOT_RPC_URL= +# The chain ID of the root chain. ROOT_CHAIN_ID= -## The admin EOA address on the child chain that is allowed to top up the child bridge contract. -CHILD_ADMIN_ADDR= -## The multisig contract that is allowed to top up the child bridge contract. -MULTISIG_CONTRACT_ADDRESS= -## The private key for the deployer on child chain or "ledger" if using hardware wallet. -CHILD_DEPLOYER_SECRET= -## The ledger index for the deployer on child chain, required if using ledger. -CHILD_DEPLOYER_LEDGER_INDEX= -## The private key for the deployer on root chain or "ledger" if using hardware wallet. -ROOT_DEPLOYER_SECRET= -## The ledger index for the deployer on root chain, required if using ledger. -ROOT_DEPLOYER_LEDGER_INDEX= -## The private key for rate admin or "ledger" if using hardware wallet. -ROOT_BRIDGE_RATE_ADMIN_SECRET= -## The ledger index for the rate admin, required if using ledger. -ROOT_BRIDGE_RATE_ADMIN_LEDGER_INDEX= +## The deployer address on child & root chains. +DEPLOYER_ADDR= +## The private key for the deployer on child & root chains or "ledger" if using hardware wallet. +DEPLOYER_SECRET= +## The ledger index for the deployer on child & root chains, required if using ledger. +DEPLOYER_LEDGER_INDEX= +## The nonce reserved deployer address on child & root chains. +NONCE_RESERVED_DEPLOYER_ADDR= +## The nonce reserved deployer, or "ledger" if using hardware wallet. +NONCE_RESERVED_DEPLOYER_SECRET= +## The ledger index for the nonce reserved deployer. +NONCE_RESERVED_DEPLOYER_INDEX= +## The reserved nonce for token template deployment. +NONCE_RESERVED= ## The IMX token address on root chain. ROOT_IMX_ADDR= ## The Wrapped ETH token address on the root chain. ROOT_WETH_ADDR= +## The Axelar address to receive initial funding on the child chain. +AXELAR_EOA= +## The amount of fund Axelar requested, unit is in IMX or 10^18 Wei. +AXELAR_FUND= +## The amount of fund deployer to be left with after bootstrapping on L2, unit is in IMX or 10^18 Wei. +CHILD_DEPLOYER_FUND= +## The amount of fund nonce reserved deployer required on L2, unit is in IMX or 10^18 Wei. +CHILD_NONCE_RESERVED_DEPLOYER_FUND= ## The maximum amount of IMX that can be deposited to L2, unit is in IMX or 10^18 Wei. IMX_DEPOSIT_LIMIT= -## The address to perform child bridge upgrade. -CHILD_PROXY_ADMIN= -## The address to be assigned with DEFAULT_ADMIN_ROLE in child bridge. -CHILD_BRIDGE_DEFAULT_ADMIN= -## The address to be assigned with PAUSER_ROLE in child bridge. -CHILD_BRIDGE_PAUSER= -## The address to be assigned with UNPAUSER_ROLE in child bridge. -CHILD_BRIDGE_UNPAUSER= -## The address to be assigned with ADAPTOR_MANAGER_ROLE in child bridge. -CHILD_BRIDGE_ADAPTOR_MANAGER= -## The address to be assigned with DEFAULT_ADMIN_ROLE in child adaptor. -CHILD_ADAPTOR_DEFAULT_ADMIN= -## The address to be assigned with BRIDGE_MANAGER_ROLE in child adaptor. -CHILD_ADAPTOR_BRIDGE_MANAGER= -## The address to be assigned with GAS_SERVICE_MANAGER_ROLE in child adaptor. -CHILD_ADAPTOR_GAS_SERVICE_MANAGER= -## The address to be assigned with TARGET_MANAGER_ROLE in child adaptor. -CHILD_ADAPTOR_TARGET_MANAGER= -## The address to perform root adaptor upgrade. -ROOT_PROXY_ADMIN= -## The address to be assigned with DEFAULT_ADMIN_ROLE in root bridge. -ROOT_BRIDGE_DEFAULT_ADMIN= -## The address to be assigned with PAUSER_ROLE in root bridge. -ROOT_BRIDGE_PAUSER= -## The address to be assigned with UNPAUSER_ROLE in root bridge. -ROOT_BRIDGE_UNPAUSER= -## The address to be assigned with VARIABLE_MANAGER_ROLE in root bridge. -ROOT_BRIDGE_VARIABLE_MANAGER= -## The address to be assigned with ADAPTOR_MANAGER_ROLE in root bridge. -ROOT_BRIDGE_ADAPTOR_MANAGER= -## The address to be assigned with DEFAULT_ADMIN_ROLE in root adaptor. -ROOT_ADAPTOR_DEFAULT_ADMIN= -## The address to be assigned with BRIDGE_MANAGER_ROLE in root adaptor. -ROOT_ADAPTOR_BRIDGE_MANAGER= -## The address to be assigned with GAS_SERVICE_MANAGER_ROLE in root adaptor. -ROOT_ADAPTOR_GAS_SERVICE_MANAGER= -## The address to be assigned with TARGET_MANAGER_ROLE in root adaptor. -ROOT_ADAPTOR_TARGET_MANAGER= ## The capacity of the rate limit policy of IMX token, unit is in 10^18. RATE_LIMIT_IMX_CAPACITY= ## The refill rate of the rate limit policy of IMX token, unit is in 10^18. diff --git a/scripts/deploy/README.md b/scripts/deploy/README.md index 0251cc9f..b9b6f4af 100644 --- a/scripts/deploy/README.md +++ b/scripts/deploy/README.md @@ -12,72 +12,46 @@ cp .env.example .env 2. Set the following fields inside `.env` ``` # Access to child and root chains. +# Name of the child chain MUST match Axelar's definition. CHILD_CHAIN_NAME= +# The RPC URL of child chain. CHILD_RPC_URL= +# The chain ID of the child chain. CHILD_CHAIN_ID= +# Name of the root chain MUST match Axelar's definition. ROOT_CHAIN_NAME= +# The RPC URL of root chain. ROOT_RPC_URL= +# The chain ID of the root chain. ROOT_CHAIN_ID= -## The admin EOA address on the child chain that is allowed to top up the child bridge contract. -CHILD_ADMIN_ADDR= -## The multisig contract that is allowed to top up the child bridge contract. -MULTISIG_CONTRACT_ADDRESS= -## The private key for the deployer on child chain or "ledger" if using hardware wallet. -CHILD_DEPLOYER_SECRET= -## The ledger index for the deployer on child chain, required if using ledger. -CHILD_DEPLOYER_LEDGER_INDEX= -## The private key for the deployer on root chain or "ledger" if using hardware wallet. -ROOT_DEPLOYER_SECRET= -## The ledger index for the deployer on root chain, required if using ledger. -ROOT_DEPLOYER_LEDGER_INDEX= -## The private key for rate admin or "ledger" if using hardware wallet. -ROOT_BRIDGE_RATE_ADMIN_SECRET= -## The ledger index for the rate admin, required if using ledger. -ROOT_BRIDGE_RATE_ADMIN_LEDGER_INDEX= +## The deployer address on child & root chains. +DEPLOYER_ADDR= +## The private key for the deployer on child & root chains or "ledger" if using hardware wallet. +DEPLOYER_SECRET= +## The ledger index for the deployer on child & root chains, required if using ledger. +DEPLOYER_LEDGER_INDEX= +## The nonce reserved deployer address on child & root chains. +NONCE_RESERVED_DEPLOYER_ADDR= +## The nonce reserved deployer, or "ledger" if using hardware wallet. +NONCE_RESERVED_DEPLOYER_SECRET= +## The ledger index for the nonce reserved deployer. +NONCE_RESERVED_DEPLOYER_INDEX= +## The reserved nonce for token template deployment. +NONCE_RESERVED= ## The IMX token address on root chain. ROOT_IMX_ADDR= ## The Wrapped ETH token address on the root chain. ROOT_WETH_ADDR= +## The Axelar address to receive initial funding on the child chain. +AXELAR_EOA= +## The amount of fund Axelar requested, unit is in IMX or 10^18 Wei. +AXELAR_FUND= +## The amount of fund deployer to be left with after bootstrapping on L2, unit is in IMX or 10^18 Wei. +CHILD_DEPLOYER_FUND= +## The amount of fund nonce reserved deployer required on L2, unit is in IMX or 10^18 Wei. +CHILD_NONCE_RESERVED_DEPLOYER_FUND= ## The maximum amount of IMX that can be deposited to L2, unit is in IMX or 10^18 Wei. IMX_DEPOSIT_LIMIT= -## The address to perform child bridge upgrade. -CHILD_PROXY_ADMIN= -## The address to be assigned with DEFAULT_ADMIN_ROLE in child bridge. -CHILD_BRIDGE_DEFAULT_ADMIN= -## The address to be assigned with PAUSER_ROLE in child bridge. -CHILD_BRIDGE_PAUSER= -## The address to be assigned with UNPAUSER_ROLE in child bridge. -CHILD_BRIDGE_UNPAUSER= -## The address to be assigned with ADAPTOR_MANAGER_ROLE in child bridge. -CHILD_BRIDGE_ADAPTOR_MANAGER= -## The address to be assigned with DEFAULT_ADMIN_ROLE in child adaptor. -CHILD_ADAPTOR_DEFAULT_ADMIN= -## The address to be assigned with BRIDGE_MANAGER_ROLE in child adaptor. -CHILD_ADAPTOR_BRIDGE_MANAGER= -## The address to be assigned with GAS_SERVICE_MANAGER_ROLE in child adaptor. -CHILD_ADAPTOR_GAS_SERVICE_MANAGER= -## The address to be assigned with TARGET_MANAGER_ROLE in child adaptor. -CHILD_ADAPTOR_TARGET_MANAGER= -## The address to perform root adaptor upgrade. -ROOT_PROXY_ADMIN= -## The address to be assigned with DEFAULT_ADMIN_ROLE in root bridge. -ROOT_BRIDGE_DEFAULT_ADMIN= -## The address to be assigned with PAUSER_ROLE in root bridge. -ROOT_BRIDGE_PAUSER= -## The address to be assigned with UNPAUSER_ROLE in root bridge. -ROOT_BRIDGE_UNPAUSER= -## The address to be assigned with VARIABLE_MANAGER_ROLE in root bridge. -ROOT_BRIDGE_VARIABLE_MANAGER= -## The address to be assigned with ADAPTOR_MANAGER_ROLE in root bridge. -ROOT_BRIDGE_ADAPTOR_MANAGER= -## The address to be assigned with DEFAULT_ADMIN_ROLE in root adaptor. -ROOT_ADAPTOR_DEFAULT_ADMIN= -## The address to be assigned with BRIDGE_MANAGER_ROLE in root adaptor. -ROOT_ADAPTOR_BRIDGE_MANAGER= -## The address to be assigned with GAS_SERVICE_MANAGER_ROLE in root adaptor. -ROOT_ADAPTOR_GAS_SERVICE_MANAGER= -## The address to be assigned with TARGET_MANAGER_ROLE in root adaptor. -ROOT_ADAPTOR_TARGET_MANAGER= ## The capacity of the rate limit policy of IMX token, unit is in 10^18. RATE_LIMIT_IMX_CAPACITY= ## The refill rate of the rate limit policy of IMX token, unit is in 10^18. diff --git a/scripts/deploy/child_deployment.ts b/scripts/deploy/child_deployment.ts index 2ac569a0..71de36b2 100644 --- a/scripts/deploy/child_deployment.ts +++ b/scripts/deploy/child_deployment.ts @@ -2,112 +2,177 @@ import * as dotenv from "dotenv"; dotenv.config(); import { ethers } from "ethers"; -import { requireEnv, waitForConfirmation, deployChildContract, waitForReceipt, getFee } from "../helpers/helpers"; +import { requireEnv, waitForConfirmation, deployChildContract, waitForReceipt, getFee, getChildContracts, getContract, saveChildContracts } from "../helpers/helpers"; import { LedgerSigner } from "../helpers/ledger_signer"; -import * as fs from "fs"; export async function deployChildContracts() { // Check environment variables let childRPCURL = requireEnv("CHILD_RPC_URL"); let childChainID = requireEnv("CHILD_CHAIN_ID"); - let childDeployerSecret = requireEnv("CHILD_DEPLOYER_SECRET"); + let deployerSecret = requireEnv("DEPLOYER_SECRET"); + let nonceReservedDeployerSecret = requireEnv("NONCE_RESERVED_DEPLOYER_SECRET"); + let nonceReserved = Number(requireEnv("NONCE_RESERVED")); let childGatewayAddr = requireEnv("CHILD_GATEWAY_ADDRESS"); - let childProxyAdmin = requireEnv("CHILD_PROXY_ADMIN"); - // Get admin address + // Read from contract file. + let childContracts = getChildContracts(); + + // Get deployer address const childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); - let adminWallet; - if (childDeployerSecret == "ledger") { - let index = requireEnv("CHILD_DEPLOYER_LEDGER_INDEX"); + let childDeployerWallet; + if (deployerSecret == "ledger") { + let index = requireEnv("DEPLOYER_LEDGER_INDEX"); const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; - adminWallet = new LedgerSigner(childProvider, derivationPath); + childDeployerWallet = new LedgerSigner(childProvider, derivationPath); } else { - adminWallet = new ethers.Wallet(childDeployerSecret, childProvider); + childDeployerWallet = new ethers.Wallet(deployerSecret, childProvider); + } + let deployerAddr = await childDeployerWallet.getAddress(); + console.log("Deployer address is: ", deployerAddr); + + // Get reserved wallet + let reservedDeployerWallet; + if (nonceReservedDeployerSecret == "ledger") { + let index = requireEnv("NONCE_RESERVED_DEPLOYER_INDEX"); + const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; + reservedDeployerWallet = new LedgerSigner(childProvider, derivationPath); + } else { + reservedDeployerWallet = new ethers.Wallet(nonceReservedDeployerSecret, childProvider); + } + let reservedDeployerAddr = await reservedDeployerWallet.getAddress(); + console.log("Reserved deployer address is: ", reservedDeployerAddr); + + // Check the current nonce matches the reserved nonce + let currentNonce = await childProvider.getTransactionCount(reservedDeployerAddr); + if (nonceReserved != currentNonce) { + throw("Nonce mismatch, expected " + nonceReserved + " actual " + currentNonce); } - let adminAddr = await adminWallet.getAddress(); - console.log("Deployer address is: ", adminAddr); // Execute console.log("Deploy child contracts in..."); await waitForConfirmation(); // Deploy child token template - console.log("Deploy child token template..."); - let childTokenTemplate = await deployChildContract("ChildERC20", adminWallet); - console.log("Transaction submitted: ", JSON.stringify(childTokenTemplate.deployTransaction, null, 2)); - await waitForReceipt(childTokenTemplate.deployTransaction.hash, childProvider); - // Initialise template - console.log("Initialise child token template..."); - let [priorityFee, maxFee] = await getFee(childProvider); - let resp = await childTokenTemplate.connect(adminWallet).initialize("000000000000000000000000000000000000007B", "TEMPLATE", "TPT", 18, { - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, childProvider); + let childTokenTemplate; + if (childContracts.CHILD_TOKEN_TEMPLATE != "") { + console.log("Child token template has already been deployed to: " + childContracts.CHILD_TOKEN_TEMPLATE + ", skip."); + childTokenTemplate = getContract("ChildERC20", childContracts.CHILD_TOKEN_TEMPLATE, childProvider); + } else { + console.log("Deploy child token template..."); + childTokenTemplate = await deployChildContract("ChildERC20", reservedDeployerWallet, nonceReserved); + console.log("Transaction submitted: ", JSON.stringify(childTokenTemplate.deployTransaction, null, 2)); + await waitForReceipt(childTokenTemplate.deployTransaction.hash, childProvider); + } + childContracts.CHILD_TOKEN_TEMPLATE = childTokenTemplate.address; + saveChildContracts(childContracts); console.log("Deployed to CHILD_TOKEN_TEMPLATE: ", childTokenTemplate.address); + + // Initialise template + if (await childTokenTemplate.name() == "TEMPLATE") { + console.log("Child token template has already been initialised, skip."); + } else { + console.log("Initialise child token template..."); + let [priorityFee, maxFee] = await getFee(childProvider); + let resp = await childTokenTemplate.connect(reservedDeployerWallet).initialize("000000000000000000000000000000000000007B", "TEMPLATE", "TPT", 18, { + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, childProvider); + } + console.log("Initialised CHILD_TOKEN_TEMPLATE at: ", childTokenTemplate.address); // Deploy wrapped IMX - console.log("Deploy wrapped IMX..."); - let wrappedIMX = await deployChildContract("WIMX", adminWallet); - console.log("Transaction submitted: ", JSON.stringify(wrappedIMX.deployTransaction, null, 2)); - await waitForReceipt(wrappedIMX.deployTransaction.hash, childProvider); + let wrappedIMX; + if (childContracts.WRAPPED_IMX_ADDRESS != "") { + console.log("Wrapped IMX has already been deployed to: " + childContracts.WRAPPED_IMX_ADDRESS + ", skip."); + wrappedIMX = getContract("WIMX", childContracts.WRAPPED_IMX_ADDRESS, childProvider); + } else { + console.log("Deploy wrapped IMX..."); + wrappedIMX = await deployChildContract("WIMX", childDeployerWallet, null); + console.log("Transaction submitted: ", JSON.stringify(wrappedIMX.deployTransaction, null, 2)); + await waitForReceipt(wrappedIMX.deployTransaction.hash, childProvider); + } + childContracts.WRAPPED_IMX_ADDRESS = wrappedIMX.address; + saveChildContracts(childContracts); console.log("Deployed to WRAPPED_IMX_ADDRESS: ", wrappedIMX.address); // Deploy proxy admin - console.log("Deploy proxy admin..."); - let proxyAdmin = await deployChildContract("ProxyAdmin", adminWallet); - console.log("Transaction submitted: ", JSON.stringify(proxyAdmin.deployTransaction, null, 2)); - await waitForReceipt(proxyAdmin.deployTransaction.hash, childProvider); - // Change owner - console.log("Change ownership..."); - [priorityFee, maxFee] = await getFee(childProvider); - resp = await proxyAdmin.connect(adminWallet).transferOwnership(childProxyAdmin, { - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, childProvider); + let proxyAdmin; + if (childContracts.CHILD_PROXY_ADMIN != "") { + console.log("Proxy admin has already been deployed to: " + childContracts.CHILD_PROXY_ADMIN + ", skip."); + proxyAdmin = getContract("ProxyAdmin", childContracts.CHILD_PROXY_ADMIN, childProvider); + } else { + console.log("Deploy proxy admin..."); + proxyAdmin = await deployChildContract("ProxyAdmin", childDeployerWallet, null); + console.log("Transaction submitted: ", JSON.stringify(proxyAdmin.deployTransaction, null, 2)); + await waitForReceipt(proxyAdmin.deployTransaction.hash, childProvider); + } + childContracts.CHILD_PROXY_ADMIN = proxyAdmin.address; + saveChildContracts(childContracts); console.log("Deployed to CHILD_PROXY_ADMIN: ", proxyAdmin.address); // Deploy child bridge impl - console.log("Deploy child bridge impl..."); - let childBridgeImpl = await deployChildContract("ChildERC20Bridge", adminWallet); - console.log("Transaction submitted: ", JSON.stringify(childBridgeImpl.deployTransaction, null, 2)); - await waitForReceipt(childBridgeImpl.deployTransaction.hash, childProvider); + let childBridgeImpl; + if (childContracts.CHILD_BRIDGE_IMPL_ADDRESS != "") { + console.log("Child bridge impl has already been deployed to: " + childContracts.CHILD_BRIDGE_IMPL_ADDRESS + ", skip."); + childBridgeImpl = getContract("ChildERC20Bridge", childContracts.CHILD_BRIDGE_IMPL_ADDRESS, childProvider); + } else { + console.log("Deploy child bridge impl..."); + childBridgeImpl = await deployChildContract("ChildERC20Bridge", childDeployerWallet, null); + console.log("Transaction submitted: ", JSON.stringify(childBridgeImpl.deployTransaction, null, 2)); + await waitForReceipt(childBridgeImpl.deployTransaction.hash, childProvider); + } + childContracts.CHILD_BRIDGE_IMPL_ADDRESS = childBridgeImpl.address; + saveChildContracts(childContracts); console.log("Deployed to CHILD_BRIDGE_IMPL_ADDRESS: ", childBridgeImpl.address); // Deploy child bridge proxy - console.log("Deploy child bridge proxy..."); - let childBridgeProxy = await deployChildContract("TransparentUpgradeableProxy", adminWallet, childBridgeImpl.address, proxyAdmin.address, []); - console.log("Transaction submitted: ", JSON.stringify(childBridgeProxy.deployTransaction, null, 2)); - await waitForReceipt(childBridgeProxy.deployTransaction.hash, childProvider); + let childBridgeProxy; + if (childContracts.CHILD_BRIDGE_PROXY_ADDRESS != "") { + console.log("Child bridge proxy has already been deployed to: " + childContracts.CHILD_BRIDGE_PROXY_ADDRESS + ", skip."); + childBridgeProxy = getContract("TransparentUpgradeableProxy", childContracts.CHILD_BRIDGE_PROXY_ADDRESS, childProvider); + } else { + console.log("Deploy child bridge proxy..."); + childBridgeProxy = await deployChildContract("TransparentUpgradeableProxy", childDeployerWallet, null, childBridgeImpl.address, proxyAdmin.address, []); + console.log("Transaction submitted: ", JSON.stringify(childBridgeProxy.deployTransaction, null, 2)); + await waitForReceipt(childBridgeProxy.deployTransaction.hash, childProvider); + } + childContracts.CHILD_BRIDGE_PROXY_ADDRESS = childBridgeProxy.address; + saveChildContracts(childContracts); console.log("Deployed to CHILD_BRIDGE_PROXY_ADDRESS: ", childBridgeProxy.address); - + // Deploy child adaptor impl - console.log("Deploy child adaptor impl..."); - let childAdaptorImpl = await deployChildContract("ChildAxelarBridgeAdaptor", adminWallet, childGatewayAddr); - console.log("Transaction submitted: ", JSON.stringify(childAdaptorImpl.deployTransaction, null, 2)); - await waitForReceipt(childAdaptorImpl.deployTransaction.hash, childProvider); + let childAdaptorImpl; + if (childContracts.CHILD_ADAPTOR_IMPL_ADDRESS != "") { + console.log("Child adaptor impl has already been deployed to: " + childContracts.CHILD_ADAPTOR_IMPL_ADDRESS + ", skip."); + childAdaptorImpl = getContract("ChildAxelarBridgeAdaptor", childContracts.CHILD_ADAPTOR_IMPL_ADDRESS, childProvider); + } else { + console.log("Deploy child adaptor impl..."); + childAdaptorImpl = await deployChildContract("ChildAxelarBridgeAdaptor", childDeployerWallet, null, childGatewayAddr); + console.log("Transaction submitted: ", JSON.stringify(childAdaptorImpl.deployTransaction, null, 2)); + await waitForReceipt(childAdaptorImpl.deployTransaction.hash, childProvider); + } + childContracts.CHILD_ADAPTOR_IMPL_ADDRESS = childAdaptorImpl.address; + saveChildContracts(childContracts); console.log("Deployed to CHILD_ADAPTOR_IMPL_ADDRESS: ", childAdaptorImpl.address); // Deploy child adaptor proxy - console.log("Deploy child adaptor proxy..."); - let childAdaptorProxy = await deployChildContract("TransparentUpgradeableProxy", adminWallet, childAdaptorImpl.address, proxyAdmin.address, []); - console.log("Transaction submitted: ", JSON.stringify(childAdaptorProxy.deployTransaction, null, 2)); - await waitForReceipt(childAdaptorProxy.deployTransaction.hash, childProvider); + let childAdaptorProxy; + if (childContracts.CHILD_ADAPTOR_PROXY_ADDRESS != "") { + console.log("Child adaptor proxy has already been deployed to: " + childContracts.CHILD_ADAPTOR_PROXY_ADDRESS + ", skip."); + childAdaptorProxy = getContract("TransparentUpgradeableProxy", childContracts.CHILD_ADAPTOR_PROXY_ADDRESS, childProvider); + } else { + console.log("Deploy child adaptor proxy..."); + childAdaptorProxy = await deployChildContract("TransparentUpgradeableProxy", childDeployerWallet, null, childAdaptorImpl.address, proxyAdmin.address, []); + console.log("Transaction submitted: ", JSON.stringify(childAdaptorProxy.deployTransaction, null, 2)); + await waitForReceipt(childAdaptorProxy.deployTransaction.hash, childProvider); + } + childContracts.CHILD_ADAPTOR_PROXY_ADDRESS = childAdaptorProxy.address; + saveChildContracts(childContracts); console.log("Deployed to CHILD_ADAPTOR_PROXY_ADDRESS: ", childAdaptorProxy.address); - let contractData = { - CHILD_PROXY_ADMIN: proxyAdmin.address, - CHILD_BRIDGE_IMPL_ADDRESS: childBridgeImpl.address, - CHILD_BRIDGE_PROXY_ADDRESS: childBridgeProxy.address, - CHILD_BRIDGE_ADDRESS: childBridgeProxy.address, - CHILD_ADAPTOR_IMPL_ADDRESS: childAdaptorImpl.address, - CHILD_ADAPTOR_PROXY_ADDRESS: childAdaptorProxy.address, - CHILD_ADAPTOR_ADDRESS: childAdaptorProxy.address, - CHILD_TOKEN_TEMPLATE: childTokenTemplate.address, - WRAPPED_IMX_ADDRESS: wrappedIMX.address, - }; - fs.writeFileSync(".child.bridge.contracts.json", JSON.stringify(contractData, null, 2)); + childContracts.CHILD_BRIDGE_ADDRESS = childBridgeProxy.address; + childContracts.CHILD_ADAPTOR_ADDRESS = childAdaptorProxy.address, + saveChildContracts(childContracts); } \ No newline at end of file diff --git a/scripts/deploy/child_initialisation.ts b/scripts/deploy/child_initialisation.ts index 996e50a7..c7d8cc0d 100644 --- a/scripts/deploy/child_initialisation.ts +++ b/scripts/deploy/child_initialisation.ts @@ -3,51 +3,45 @@ import * as dotenv from "dotenv"; dotenv.config(); import { ethers } from "ethers"; -import { requireEnv, waitForConfirmation, waitForReceipt, getFee, getContract } from "../helpers/helpers"; +import { requireEnv, waitForConfirmation, waitForReceipt, getFee, getContract, getChildContracts, getRootContracts } from "../helpers/helpers"; import { LedgerSigner } from "../helpers/ledger_signer"; -import * as fs from "fs"; export async function initialiseChildContracts() { let rootChainName = requireEnv("ROOT_CHAIN_NAME"); let childRPCURL = requireEnv("CHILD_RPC_URL"); let childChainID = requireEnv("CHILD_CHAIN_ID"); - let adminEOAAddr = requireEnv("CHILD_ADMIN_ADDR"); - let childBridgeDefaultAdmin = requireEnv("CHILD_BRIDGE_DEFAULT_ADMIN"); - let childBridgePauser = requireEnv("CHILD_BRIDGE_PAUSER"); - let childBridgeUnpauser = requireEnv("CHILD_BRIDGE_UNPAUSER"); - let childBridgeAdaptorManager = requireEnv("CHILD_BRIDGE_ADAPTOR_MANAGER"); - let childAdaptorDefaultAdmin = requireEnv("CHILD_ADAPTOR_DEFAULT_ADMIN"); - let childAdaptorBridgeManager = requireEnv("CHILD_ADAPTOR_BRIDGE_MANAGER"); - let childAdaptorGasServiceManager = requireEnv("CHILD_ADAPTOR_GAS_SERVICE_MANAGER"); - let childAdaptorTargetManager = requireEnv("CHILD_ADAPTOR_TARGET_MANAGER"); - let childDeployerSecret = requireEnv("CHILD_DEPLOYER_SECRET"); + let deployerSecret = requireEnv("DEPLOYER_SECRET"); let childGasServiceAddr = requireEnv("CHILD_GAS_SERVICE_ADDRESS"); let multisigAddr = requireEnv("MULTISIG_CONTRACT_ADDRESS"); let rootIMXAddr = requireEnv("ROOT_IMX_ADDR"); // Read from contract file. - let data = fs.readFileSync(".child.bridge.contracts.json", 'utf-8'); - let childContracts = JSON.parse(data); + let childContracts = getChildContracts(); + let rootContracts = getRootContracts(); let childBridgeAddr = childContracts.CHILD_BRIDGE_ADDRESS; let childAdaptorAddr = childContracts.CHILD_ADAPTOR_ADDRESS; let childWIMXAddr = childContracts.WRAPPED_IMX_ADDRESS; let childTemplateAddr = childContracts.CHILD_TOKEN_TEMPLATE; - data = fs.readFileSync(".root.bridge.contracts.json", 'utf-8'); - let rootContracts = JSON.parse(data); let rootAdaptorAddr = rootContracts.ROOT_ADAPTOR_ADDRESS; + let rootTemplateAddr = rootContracts.ROOT_TOKEN_TEMPLATE; - // Get admin address + // Root token template must have the same address as child token template + if (childTemplateAddr != rootTemplateAddr) { + throw("Token template contract address mismatch: root " + rootTemplateAddr + " child " + childTemplateAddr); + } + + // Get deployer address const childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); - let adminWallet; - if (childDeployerSecret == "ledger") { - let index = requireEnv("CHILD_DEPLOYER_LEDGER_INDEX"); + let childDeployerWallet; + if (deployerSecret == "ledger") { + let index = requireEnv("DEPLOYER_LEDGER_INDEX"); const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; - adminWallet = new LedgerSigner(childProvider, derivationPath); + childDeployerWallet = new LedgerSigner(childProvider, derivationPath); } else { - adminWallet = new ethers.Wallet(childDeployerSecret, childProvider); + childDeployerWallet = new ethers.Wallet(deployerSecret, childProvider); } - let adminAddr = await adminWallet.getAddress(); - console.log("Deployer address is: ", adminAddr); + let deployerAddr = await childDeployerWallet.getAddress(); + console.log("Deployer address is: ", deployerAddr); // Execute console.log("Initialise child contracts in..."); @@ -57,13 +51,13 @@ export async function initialiseChildContracts() { console.log("Initialise child bridge..."); let childBridge = getContract("ChildERC20Bridge", childBridgeAddr, childProvider); let [priorityFee, maxFee] = await getFee(childProvider); - let resp = await childBridge.connect(adminWallet).initialize( + let resp = await childBridge.connect(childDeployerWallet).initialize( { - defaultAdmin: childBridgeDefaultAdmin, - pauser: childBridgePauser, - unpauser: childBridgeUnpauser, - adaptorManager: childBridgeAdaptorManager, - initialDepositor: adminEOAAddr, + defaultAdmin: deployerAddr, + pauser: deployerAddr, + unpauser: deployerAddr, + adaptorManager: deployerAddr, + initialDepositor: deployerAddr, treasuryManager: multisigAddr, }, childAdaptorAddr, @@ -80,14 +74,13 @@ export async function initialiseChildContracts() { // Initialise child adaptor console.log("Initialise child adaptor..."); let childAdaptor = getContract("ChildAxelarBridgeAdaptor", childAdaptorAddr, childProvider); - // let childAdaptor = new ethers.Contract(childAdaptorAddr, childAdaptorObj.abi, childProvider); [priorityFee, maxFee] = await getFee(childProvider); - resp = await childAdaptor.connect(adminWallet).initialize( + resp = await childAdaptor.connect(childDeployerWallet).initialize( { - defaultAdmin: childAdaptorDefaultAdmin, - bridgeManager: childAdaptorBridgeManager, - gasServiceManager: childAdaptorGasServiceManager, - targetManager: childAdaptorTargetManager, + defaultAdmin: deployerAddr, + bridgeManager: deployerAddr, + gasServiceManager: deployerAddr, + targetManager: deployerAddr, }, childBridgeAddr, rootChainName, diff --git a/scripts/deploy/root_deployment.ts b/scripts/deploy/root_deployment.ts index 11cca6a7..68cac805 100644 --- a/scripts/deploy/root_deployment.ts +++ b/scripts/deploy/root_deployment.ts @@ -2,96 +2,158 @@ import * as dotenv from "dotenv"; dotenv.config(); import { ethers } from "ethers"; -import { requireEnv, waitForConfirmation, deployRootContract, waitForReceipt } from "../helpers/helpers"; +import { requireEnv, waitForConfirmation, deployRootContract, waitForReceipt, getRootContracts, getContract, saveRootContracts } from "../helpers/helpers"; import { LedgerSigner } from "../helpers/ledger_signer"; -import * as fs from "fs"; export async function deployRootContracts() { // Check environment variables let rootRPCURL = requireEnv("ROOT_RPC_URL"); let rootChainID = requireEnv("ROOT_CHAIN_ID"); - let rootDeployerSecret = requireEnv("ROOT_DEPLOYER_SECRET"); - let rootProxyAdmin = requireEnv("ROOT_PROXY_ADMIN"); + let deployerSecret = requireEnv("DEPLOYER_SECRET"); + let nonceReservedDeployerSecret = requireEnv("NONCE_RESERVED_DEPLOYER_SECRET"); + let nonceReserved = Number(requireEnv("NONCE_RESERVED")); let rootGatewayAddr = requireEnv("ROOT_GATEWAY_ADDRESS"); - // Get admin address + // Read from contract file. + let rootContracts = getRootContracts(); + + // Get deployer address const rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); - let adminWallet; - if (rootDeployerSecret == "ledger") { - let index = requireEnv("ROOT_DEPLOYER_LEDGER_INDEX"); + let rootDeployerWallet; + if (deployerSecret == "ledger") { + let index = requireEnv("DEPLOYER_LEDGER_INDEX"); const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; - adminWallet = new LedgerSigner(rootProvider, derivationPath); + rootDeployerWallet = new LedgerSigner(rootProvider, derivationPath); } else { - adminWallet = new ethers.Wallet(rootDeployerSecret, rootProvider); + rootDeployerWallet = new ethers.Wallet(deployerSecret, rootProvider); + } + let deployerAddr = await rootDeployerWallet.getAddress(); + console.log("Deployer address is: ", deployerAddr); + + // Get reserved wallet + let reservedDeployerWallet; + if (nonceReservedDeployerSecret == "ledger") { + let index = requireEnv("NONCE_RESERVED_DEPLOYER_INDEX"); + const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; + reservedDeployerWallet = new LedgerSigner(rootProvider, derivationPath); + } else { + reservedDeployerWallet = new ethers.Wallet(nonceReservedDeployerSecret, rootProvider); + } + let reservedDeployerAddr = await reservedDeployerWallet.getAddress(); + console.log("Reserved deployer address is: ", reservedDeployerAddr); + + // Check the current nonce matches the reserved nonce + let currentNonce = await rootProvider.getTransactionCount(reservedDeployerAddr); + if (nonceReserved != currentNonce) { + throw("Nonce mismatch, expected " + nonceReserved + " actual " + currentNonce); } - let adminAddr = await adminWallet.getAddress(); - console.log("Deployer address is: ", adminAddr); // Execute console.log("Deploy root contracts in..."); await waitForConfirmation(); // Deploy root token template - console.log("Deploy root token template..."); - let rootTokenTemplate = await deployRootContract("ChildERC20", adminWallet); - console.log("Transaction submitted: ", JSON.stringify(rootTokenTemplate.deployTransaction, null, 2)); - await waitForReceipt(rootTokenTemplate.deployTransaction.hash, rootProvider); + let rootTokenTemplate; + if (rootContracts.ROOT_TOKEN_TEMPLATE != "") { + console.log("Root token template has already been deployed to: " + rootContracts.ROOT_TOKEN_TEMPLATE + ", skip."); + rootTokenTemplate = getContract("ChildERC20", rootContracts.ROOT_TOKEN_TEMPLATE, rootProvider); + } else { + console.log("Deploy root token template..."); + rootTokenTemplate = await deployRootContract("ChildERC20", reservedDeployerWallet, nonceReserved); + console.log("Transaction submitted: ", JSON.stringify(rootTokenTemplate.deployTransaction, null, 2)); + await waitForReceipt(rootTokenTemplate.deployTransaction.hash, rootProvider); + } + rootContracts.ROOT_TOKEN_TEMPLATE = rootTokenTemplate.address; + saveRootContracts(rootContracts); + console.log("Deployed to ROOT_TOKEN_TEMPLATE: ", rootTokenTemplate.address); + // Initialise template - console.log("Initialise root token template..."); - let resp = await rootTokenTemplate.connect(adminWallet).initialize("000000000000000000000000000000000000007B", "TEMPLATE", "TPT", 18); - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, rootProvider); + if (await rootTokenTemplate.name() == "TEMPLATE") { + console.log("Root token template has already been initialised, skip."); + } else { + console.log("Initialise root token template..."); + let resp = await rootTokenTemplate.connect(reservedDeployerWallet).initialize("000000000000000000000000000000000000007B", "TEMPLATE", "TPT", 18); + console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, rootProvider); + } console.log("Deployed to ROOT_TOKEN_TEMPLATE: ", rootTokenTemplate.address); - + // Deploy proxy admin - console.log("Deploy proxy admin..."); - let proxyAdmin = await deployRootContract("ProxyAdmin", adminWallet); - console.log("Transaction submitted: ", JSON.stringify(proxyAdmin.deployTransaction, null, 2)); - await waitForReceipt(proxyAdmin.deployTransaction.hash, rootProvider); - // Change owner - console.log("Change ownership...") - resp = await proxyAdmin.connect(adminWallet).transferOwnership(rootProxyAdmin); - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, rootProvider); + let proxyAdmin; + if (rootContracts.ROOT_PROXY_ADMIN != "") { + console.log("Proxy admin has already been deployed to: " + rootContracts.ROOT_PROXY_ADMIN + ", skip."); + proxyAdmin = getContract("ProxyAdmin", rootContracts.ROOT_PROXY_ADMIN, rootProvider); + } else { + console.log("Deploy proxy admin..."); + proxyAdmin = await deployRootContract("ProxyAdmin", rootDeployerWallet, null); + console.log("Transaction submitted: ", JSON.stringify(proxyAdmin.deployTransaction, null, 2)); + await waitForReceipt(proxyAdmin.deployTransaction.hash, rootProvider); + } + rootContracts.ROOT_PROXY_ADMIN = proxyAdmin.address; + saveRootContracts(rootContracts); console.log("Deployed to ROOT_PROXY_ADMIN: ", proxyAdmin.address); // Deploy root bridge impl - console.log("Deploy root bridge impl..."); - let rootBridgeImpl = await deployRootContract("RootERC20BridgeFlowRate", adminWallet); - console.log("Transaction submitted: ", JSON.stringify(rootBridgeImpl.deployTransaction, null, 2)); - await waitForReceipt(rootBridgeImpl.deployTransaction.hash, rootProvider); + let rootBridgeImpl; + if (rootContracts.ROOT_BRIDGE_IMPL_ADDRESS != "") { + console.log("Root bridge impl has already been deployed to: " + rootContracts.ROOT_BRIDGE_IMPL_ADDRESS + ", skip."); + rootBridgeImpl = getContract("RootERC20BridgeFlowRate", rootContracts.ROOT_BRIDGE_IMPL_ADDRESS, rootProvider); + } else { + console.log("Deploy root bridge impl..."); + rootBridgeImpl = await deployRootContract("RootERC20BridgeFlowRate", rootDeployerWallet, null); + console.log("Transaction submitted: ", JSON.stringify(rootBridgeImpl.deployTransaction, null, 2)); + await waitForReceipt(rootBridgeImpl.deployTransaction.hash, rootProvider); + } + rootContracts.ROOT_BRIDGE_IMPL_ADDRESS = rootBridgeImpl.address; + saveRootContracts(rootContracts); console.log("Deployed to ROOT_BRIDGE_IMPL_ADDRESS: ", rootBridgeImpl.address); // Deploy root bridge proxy - console.log("Deploy root bridge proxy..."); - let rootBridgeProxy = await deployRootContract("TransparentUpgradeableProxy", adminWallet, rootBridgeImpl.address, proxyAdmin.address, []); - console.log("Transaction submitted: ", JSON.stringify(rootBridgeProxy.deployTransaction, null, 2)); - await waitForReceipt(rootBridgeProxy.deployTransaction.hash, rootProvider); + let rootBridgeProxy; + if (rootContracts.ROOT_BRIDGE_PROXY_ADDRESS != "") { + console.log("Root bridge proxy has already been deployed to: " + rootContracts.ROOT_BRIDGE_PROXY_ADDRESS + ", skip."); + rootBridgeProxy = getContract("TransparentUpgradeableProxy", rootContracts.ROOT_BRIDGE_PROXY_ADDRESS, rootProvider); + } else { + console.log("Deploy root bridge proxy..."); + rootBridgeProxy = await deployRootContract("TransparentUpgradeableProxy", rootDeployerWallet, null, rootBridgeImpl.address, proxyAdmin.address, []); + console.log("Transaction submitted: ", JSON.stringify(rootBridgeProxy.deployTransaction, null, 2)); + await waitForReceipt(rootBridgeProxy.deployTransaction.hash, rootProvider); + } + rootContracts.ROOT_BRIDGE_PROXY_ADDRESS = rootBridgeProxy.address; + saveRootContracts(rootContracts); console.log("Deployed to ROOT_BRIDGE_PROXY_ADDRESS: ", rootBridgeProxy.address); // Deploy root adaptor impl - console.log("Deploy root adaptor impl..."); - let rootAdaptorImpl = await deployRootContract("RootAxelarBridgeAdaptor", adminWallet, rootGatewayAddr); - console.log("Transaction submitted: ", JSON.stringify(rootAdaptorImpl.deployTransaction, null, 2)); - await waitForReceipt(rootAdaptorImpl.deployTransaction.hash, rootProvider); + let rootAdaptorImpl; + if (rootContracts.ROOT_ADAPTOR_IMPL_ADDRESS != "") { + console.log("Root adaptor impl has already been deployed to: " + rootContracts.ROOT_ADAPTOR_IMPL_ADDRESS + ", skip."); + rootAdaptorImpl = getContract("RootAxelarBridgeAdaptor", rootContracts.ROOT_ADAPTOR_IMPL_ADDRESS, rootProvider); + } else { + console.log("Deploy root adaptor impl..."); + rootAdaptorImpl = await deployRootContract("RootAxelarBridgeAdaptor", rootDeployerWallet, null, rootGatewayAddr); + console.log("Transaction submitted: ", JSON.stringify(rootAdaptorImpl.deployTransaction, null, 2)); + await waitForReceipt(rootAdaptorImpl.deployTransaction.hash, rootProvider); + } + rootContracts.ROOT_ADAPTOR_IMPL_ADDRESS = rootAdaptorImpl.address; + saveRootContracts(rootContracts); console.log("Deployed to ROOT_ADAPTOR_IMPL_ADDRESS: ", rootAdaptorImpl.address); // Deploy root adaptor proxy - console.log("Deploy root adaptor proxy..."); - let rootAdaptorProxy = await deployRootContract("TransparentUpgradeableProxy", adminWallet, rootAdaptorImpl.address, proxyAdmin.address, []); - console.log("Transaction submitted: ", JSON.stringify(rootAdaptorProxy.deployTransaction, null, 2)); - await waitForReceipt(rootAdaptorProxy.deployTransaction.hash, rootProvider); + let rootAdaptorProxy; + if (rootContracts.ROOT_ADAPTOR_PROXY_ADDRESS != "") { + console.log("Root adaptor proxy has already been deployed to: " + rootContracts.ROOT_ADAPTOR_PROXY_ADDRESS + ", skip."); + rootAdaptorProxy = getContract("TransparentUpgradeableProxy", rootContracts.ROOT_ADAPTOR_PROXY_ADDRESS, rootProvider); + } else { + console.log("Deploy root adaptor proxy..."); + rootAdaptorProxy = await deployRootContract("TransparentUpgradeableProxy", rootDeployerWallet, null, rootAdaptorImpl.address, proxyAdmin.address, []); + console.log("Transaction submitted: ", JSON.stringify(rootAdaptorProxy.deployTransaction, null, 2)); + await waitForReceipt(rootAdaptorProxy.deployTransaction.hash, rootProvider); + } + rootContracts.ROOT_ADAPTOR_PROXY_ADDRESS = rootAdaptorProxy.address; + saveRootContracts(rootContracts); console.log("Deployed to ROOT_ADAPTOR_PROXY_ADDRESS: ", rootAdaptorProxy.address); - let contractData = { - ROOT_PROXY_ADMIN: rootBridgeImpl.address, - ROOT_BRIDGE_IMPL_ADDRESS: rootBridgeImpl.address, - ROOT_BRIDGE_PROXY_ADDRESS: rootBridgeProxy.address, - ROOT_BRIDGE_ADDRESS: rootBridgeProxy.address, - ROOT_ADAPTOR_IMPL_ADDRESS: rootAdaptorImpl.address, - ROOT_ADAPTOR_PROXY_ADDRESS: rootAdaptorProxy.address, - ROOT_ADAPTOR_ADDRESS: rootAdaptorProxy.address, - ROOT_TOKEN_TEMPLATE: rootTokenTemplate.address, - }; - fs.writeFileSync(".root.bridge.contracts.json", JSON.stringify(contractData, null, 2)); + rootContracts.ROOT_BRIDGE_ADDRESS = rootBridgeProxy.address; + rootContracts.ROOT_ADAPTOR_ADDRESS = rootAdaptorProxy.address; + saveRootContracts(rootContracts); } \ No newline at end of file diff --git a/scripts/deploy/root_initialisation.ts b/scripts/deploy/root_initialisation.ts index 1f61002f..a11448b4 100644 --- a/scripts/deploy/root_initialisation.ts +++ b/scripts/deploy/root_initialisation.ts @@ -2,26 +2,15 @@ import * as dotenv from "dotenv"; dotenv.config(); import { ethers } from "ethers"; -import { requireEnv, waitForConfirmation, waitForReceipt, getContract } from "../helpers/helpers"; +import { requireEnv, waitForConfirmation, waitForReceipt, getContract, getChildContracts, getRootContracts } from "../helpers/helpers"; import { LedgerSigner } from "../helpers/ledger_signer"; -import * as fs from "fs"; export async function initialiseRootContracts() { // Check environment variables let childChainName = requireEnv("CHILD_CHAIN_NAME"); let rootRPCURL = requireEnv("ROOT_RPC_URL"); let rootChainID = requireEnv("ROOT_CHAIN_ID"); - let rootDeployerSecret = requireEnv("ROOT_DEPLOYER_SECRET"); - let rootRateAdminSecret = requireEnv("ROOT_BRIDGE_RATE_ADMIN_SECRET"); - let rootBridgeDefaultAdmin = requireEnv("ROOT_BRIDGE_DEFAULT_ADMIN"); - let rootBridgePauser = requireEnv("ROOT_BRIDGE_PAUSER"); - let rootBridgeUnpauser = requireEnv("ROOT_BRIDGE_UNPAUSER"); - let rootBridgeVariableManager = requireEnv("ROOT_BRIDGE_VARIABLE_MANAGER"); - let rootBridgeAdaptorManager = requireEnv("ROOT_BRIDGE_ADAPTOR_MANAGER"); - let rootAdaptorDefaultAdmin = requireEnv("ROOT_ADAPTOR_DEFAULT_ADMIN"); - let rootAdaptorBridgeManager = requireEnv("ROOT_ADAPTOR_BRIDGE_MANAGER"); - let rootAdaptorGasServiceManager = requireEnv("ROOT_ADAPTOR_GAS_SERVICE_MANAGER"); - let rootAdaptorTargetManager = requireEnv("ROOT_ADAPTOR_TARGET_MANAGER"); + let deployerSecret = requireEnv("DEPLOYER_SECRET"); let rootGasServiceAddr = requireEnv("ROOT_GAS_SERVICE_ADDRESS"); let rootIMXAddr = requireEnv("ROOT_IMX_ADDR"); let rootWETHAddr = requireEnv("ROOT_WETH_ADDR"); @@ -50,41 +39,26 @@ export async function initialiseRootContracts() { let rateLimitGOGLargeThreshold = requireEnv("RATE_LIMIT_GOG_LARGE_THRESHOLD"); // Read from contract file. - let data = fs.readFileSync(".child.bridge.contracts.json", 'utf-8'); - let childContracts = JSON.parse(data); + let childContracts = getChildContracts(); let childBridgeAddr = childContracts.CHILD_BRIDGE_ADDRESS; let childAdaptorAddr = childContracts.CHILD_ADAPTOR_ADDRESS; - data = fs.readFileSync(".root.bridge.contracts.json", 'utf-8'); - let rootContracts = JSON.parse(data); + let rootContracts = getRootContracts(); let rootBridgeAddr = rootContracts.ROOT_BRIDGE_ADDRESS; let rootAdaptorAddr = rootContracts.ROOT_ADAPTOR_ADDRESS; let rootTemplateAddr = rootContracts.ROOT_TOKEN_TEMPLATE; - // Get admin address + // Get deployer address const rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); - let adminWallet; - if (rootDeployerSecret == "ledger") { - let index = requireEnv("ROOT_DEPLOYER_LEDGER_INDEX"); + let rootDeployerWallet; + if (deployerSecret == "ledger") { + let index = requireEnv("DEPLOYER_LEDGER_INDEX"); const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; - adminWallet = new LedgerSigner(rootProvider, derivationPath); + rootDeployerWallet = new LedgerSigner(rootProvider, derivationPath); } else { - adminWallet = new ethers.Wallet(rootDeployerSecret, rootProvider); + rootDeployerWallet = new ethers.Wallet(deployerSecret, rootProvider); } - let adminAddr = await adminWallet.getAddress(); - console.log("Deployer address is: ", adminAddr); - - // Get rate admin address - let rateAdminWallet; - if (rootRateAdminSecret == "ledger") { - let index = requireEnv("ROOT_BRIDGE_RATE_ADMIN_LEDGER_INDEX"); - const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; - rateAdminWallet = new LedgerSigner(rootProvider, derivationPath); - } else { - rateAdminWallet = new ethers.Wallet(rootRateAdminSecret, rootProvider); - } - let rateAdminAddr = await rateAdminWallet.getAddress(); - console.log("Rate admin address is: ", rateAdminAddr); - + let deployerAddr = await rootDeployerWallet.getAddress(); + console.log("Deployer address is: ", deployerAddr); // Execute console.log("Initialise root contracts in..."); @@ -93,13 +67,13 @@ export async function initialiseRootContracts() { // Initialise root bridge console.log("Initialise root bridge..."); let rootBridge = getContract("RootERC20BridgeFlowRate", rootBridgeAddr, rootProvider); - let resp = await rootBridge.connect(adminWallet)["initialize((address,address,address,address,address),address,address,address,address,address,uint256,address)"]( + let resp = await rootBridge.connect(rootDeployerWallet)["initialize((address,address,address,address,address),address,address,address,address,address,uint256,address)"]( { - defaultAdmin: rootBridgeDefaultAdmin, - pauser: rootBridgePauser, - unpauser: rootBridgeUnpauser, - variableManager: rootBridgeVariableManager, - adaptorManager: rootBridgeAdaptorManager, + defaultAdmin: deployerAddr, + pauser: deployerAddr, + unpauser: deployerAddr, + variableManager: deployerAddr, + adaptorManager: deployerAddr, }, rootAdaptorAddr, childBridgeAddr, @@ -107,14 +81,14 @@ export async function initialiseRootContracts() { rootIMXAddr, rootWETHAddr, ethers.utils.parseEther(imxDepositLimit), - rateAdminAddr); + deployerAddr); console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); await waitForReceipt(resp.hash, rootProvider); // Configure rate // IMX console.log("Configure rate limiting for IMX...") - resp = await rootBridge.connect(rateAdminWallet).setRateControlThreshold( + resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( rootIMXAddr, ethers.utils.parseEther(rateLimitIMXCap), ethers.utils.parseEther(rateLimitIMXRefill), @@ -125,7 +99,7 @@ export async function initialiseRootContracts() { // ETH console.log("Configure rate limiting for ETH...") - resp = await rootBridge.connect(rateAdminWallet).setRateControlThreshold( + resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( await rootBridge.NATIVE_ETH(), ethers.utils.parseEther(rateLimitETHCap), ethers.utils.parseEther(rateLimitETHRefill), @@ -136,7 +110,7 @@ export async function initialiseRootContracts() { // USDC console.log("Configure rate limiting for USDC...") - resp = await rootBridge.connect(rateAdminWallet).setRateControlThreshold( + resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( rateLimitUSDCAddr, ethers.utils.parseEther(rateLimitUSDCCap), ethers.utils.parseEther(rateLimitUSDCRefill), @@ -147,7 +121,7 @@ export async function initialiseRootContracts() { // GU console.log("Configure rate limiting for GU...") - resp = await rootBridge.connect(rateAdminWallet).setRateControlThreshold( + resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( rateLimitGUAddr, ethers.utils.parseEther(rateLimitGUCap), ethers.utils.parseEther(rateLimitGURefill), @@ -158,7 +132,7 @@ export async function initialiseRootContracts() { // Checkmate console.log("Configure rate limiting for CheckMate...") - resp = await rootBridge.connect(rateAdminWallet).setRateControlThreshold( + resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( rateLimitCheckMateAddr, ethers.utils.parseEther(rateLimitCheckMateCap), ethers.utils.parseEther(rateLimitCheckMateRefill), @@ -169,7 +143,7 @@ export async function initialiseRootContracts() { // GOG console.log("Configure rate limiting for GOG...") - resp = await rootBridge.connect(rateAdminWallet).setRateControlThreshold( + resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( rateLimitGOGAddr, ethers.utils.parseEther(rateLimitGOGCap), ethers.utils.parseEther(rateLimitGOGRefill), @@ -181,12 +155,12 @@ export async function initialiseRootContracts() { // Initialise root adaptor console.log("Initialise root adaptor..."); let rootAdaptor = getContract("RootAxelarBridgeAdaptor", rootAdaptorAddr, rootProvider); - resp = await rootAdaptor.connect(adminWallet).initialize( + resp = await rootAdaptor.connect(rootDeployerWallet).initialize( { - defaultAdmin: rootAdaptorDefaultAdmin, - bridgeManager: rootAdaptorBridgeManager, - gasServiceManager: rootAdaptorGasServiceManager, - targetManager: rootAdaptorTargetManager, + defaultAdmin: deployerAddr, + bridgeManager: deployerAddr, + gasServiceManager: deployerAddr, + targetManager: deployerAddr, }, rootBridgeAddr, childChainName, diff --git a/scripts/e2e/.root.bridge.contracts.json.example b/scripts/e2e/.root.bridge.contracts.json.example index 4a593209..8484c978 100644 --- a/scripts/e2e/.root.bridge.contracts.json.example +++ b/scripts/e2e/.root.bridge.contracts.json.example @@ -1,5 +1,6 @@ { "ROOT_BRIDGE_ADDRESS": "", "ROOT_ADAPTOR_ADDRESS": "", - "ROOT_TOKEN_TEMPLATE": "" + "ROOT_TOKEN_TEMPLATE": "", + "ROOT_TEST_CUSTOM_TOKEN": "" } \ No newline at end of file diff --git a/scripts/e2e/README.md b/scripts/e2e/README.md index e3c39e80..3989fbb5 100644 --- a/scripts/e2e/README.md +++ b/scripts/e2e/README.md @@ -45,11 +45,12 @@ TEST_ACCOUNT_SECRET= { "ROOT_BRIDGE_ADDRESS": "", "ROOT_ADAPTOR_ADDRESS": "", - "ROOT_TOKEN_TEMPLATE": "" + "ROOT_TOKEN_TEMPLATE": "", + "ROOT_TEST_CUSTOM_TOKEN": "" } ``` 3. Run end to end tests ``` -npx mocha --require mocha-suppress-logs ./e2e.ts +LONG_WAIT=1200000 SHORT_WAIT=300000 npx mocha --require mocha-suppress-logs ./e2e.ts ``` \ No newline at end of file diff --git a/scripts/e2e/e2e.ts b/scripts/e2e/e2e.ts index d62e0f49..7adba223 100644 --- a/scripts/e2e/e2e.ts +++ b/scripts/e2e/e2e.ts @@ -2,8 +2,7 @@ import * as dotenv from "dotenv"; dotenv.config(); import { ethers, providers } from "ethers"; -import { requireEnv, waitForReceipt, getFee, getContract, deployRootContract, delay } from "../helpers/helpers"; -import * as fs from "fs"; +import { requireEnv, waitForReceipt, getFee, getContract, delay, getChildContracts, getRootContracts, saveChildContracts } from "../helpers/helpers"; import { expect } from "chai"; // The contract ABI of IMX on L1. @@ -22,54 +21,50 @@ describe("Bridge e2e test", () => { let childWIMX: ethers.Contract; let rootCustomToken: ethers.Contract; let childCustomToken: ethers.Contract; + let longWait: number; + let shortWait: number; before(async function () { - this.timeout(30000); + this.timeout(300000); let rootRPCURL = requireEnv("ROOT_RPC_URL"); let rootChainID = requireEnv("ROOT_CHAIN_ID"); let childRPCURL = requireEnv("CHILD_RPC_URL"); let childChainID = requireEnv("CHILD_CHAIN_ID"); - let rootRateAdminSecret = requireEnv("ROOT_BRIDGE_RATE_ADMIN_SECRET"); let testAccountKey = requireEnv("TEST_ACCOUNT_SECRET"); let rootIMXAddr = requireEnv("ROOT_IMX_ADDR"); let rootWETHAddr = requireEnv("ROOT_WETH_ADDR"); + if (process.env["LONG_WAIT"] == null || process.env["LONG_WAIT"] == "") { + longWait = 1200000; + } else { + longWait = Number(process.env["LONG_WAIT"]) + } + if (process.env["SHORT_WAIT"] == null || process.env["SHORT_WAIT"] == "") { + longWait = 300000; + } else { + longWait = Number(process.env["SHORT_WAIT"]) + } + // Read from contract file. - let data = fs.readFileSync(".child.bridge.contracts.json", 'utf-8'); - let childContracts = JSON.parse(data); + let childContracts = getChildContracts(); let childBridgeAddr = childContracts.CHILD_BRIDGE_ADDRESS; let childWIMXAddr = childContracts.WRAPPED_IMX_ADDRESS; - data = fs.readFileSync(".root.bridge.contracts.json", 'utf-8'); - let rootContracts = JSON.parse(data); + let rootContracts = getRootContracts(); let rootBridgeAddr = rootContracts.ROOT_BRIDGE_ADDRESS; + let rootCustomTokenAddr = rootContracts.ROOT_TEST_CUSTOM_TOKEN; rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); rootTestWallet = new ethers.Wallet(testAccountKey, rootProvider); childTestWallet = new ethers.Wallet(testAccountKey, childProvider); - let rootRateAdminWallet = new ethers.Wallet(rootRateAdminSecret, rootProvider); rootBridge = getContract("RootERC20BridgeFlowRate", rootBridgeAddr, rootProvider); rootWETH = getContract("WETH", rootWETHAddr, rootProvider); rootIMX = new ethers.Contract(rootIMXAddr, IMX_ABI, rootProvider); + rootCustomToken = getContract("ChildERC20", rootCustomTokenAddr, rootProvider); childBridge = getContract("ChildERC20Bridge", childBridgeAddr, childProvider); childETH = getContract("ChildERC20", await childBridge.childETHToken(), childProvider); childWIMX = getContract("WIMX", childWIMXAddr, childProvider); - - // Deploy a custom token - rootCustomToken = await deployRootContract("ERC20PresetMinterPauser", rootTestWallet, "Custom Token", "CTK"); - await waitForReceipt(rootCustomToken.deployTransaction.hash, rootProvider); - // Mint tokens - let resp = await rootCustomToken.connect(rootTestWallet).mint(rootTestWallet.address, ethers.utils.parseEther("1000.0").toBigInt()); - await waitForReceipt(resp.hash, rootProvider); - // Set rate control - resp = await rootBridge.connect(rootRateAdminWallet).setRateControlThreshold( - rootCustomToken.address, - ethers.utils.parseEther("20016.0"), - ethers.utils.parseEther("5.56"), - ethers.utils.parseEther("10008.0") - ); - await waitForReceipt(resp.hash, rootProvider); }) it("should successfully deposit IMX to self from L1 to L2", async() => { @@ -93,6 +88,10 @@ describe("Bridge e2e test", () => { let postBalL1 = await rootIMX.balanceOf(rootTestWallet.address); let postBalL2 = preBalL2; + console.log("Wait " + longWait + " ms"); + await delay(longWait); + console.log("Done"); + while (postBalL2.eq(preBalL2)) { postBalL2 = await childProvider.getBalance(childTestWallet.address); await delay(1000); @@ -103,7 +102,7 @@ describe("Bridge e2e test", () => { let expectedPostL2 = preBalL2.add(amt); expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); - }).timeout(60000) + }).timeout(1800000) it("should successfully withdraw IMX to self from L2 to L1", async() => { // Get IMX balance on root & child chains before withdraw @@ -125,6 +124,10 @@ describe("Bridge e2e test", () => { let postBalL1 = preBalL1; let postBalL2 = await childProvider.getBalance(childTestWallet.address); + console.log("Wait " + shortWait + " ms"); + await delay(shortWait); + console.log("Done"); + while (postBalL1.eq(preBalL1)) { postBalL1 = await rootIMX.balanceOf(rootTestWallet.address); await delay(1000); @@ -132,12 +135,12 @@ describe("Bridge e2e test", () => { // Verify let receipt = await childProvider.getTransactionReceipt(resp.hash); - let txFee = receipt.cumulativeGasUsed.mul(receipt.effectiveGasPrice); + let txFee = receipt.gasUsed.mul(receipt.effectiveGasPrice); let expectedPostL1 = preBalL1.add(amt); let expectedPostL2 = preBalL2.sub(txFee).sub(amt).sub(bridgeFee); expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); - }).timeout(120000) + }).timeout(1800000) it("should successfully withdraw wIMX to self from L2 to L1", async() => { // Wrap 1 IMX @@ -176,6 +179,10 @@ describe("Bridge e2e test", () => { let postBalL1 = preBalL1; let postBalL2 = await childWIMX.balanceOf(childTestWallet.address); + console.log("Wait " + shortWait + " ms"); + await delay(shortWait); + console.log("Done"); + while (postBalL1.eq(preBalL1)) { postBalL1 = await rootIMX.balanceOf(rootTestWallet.address); await delay(1000); @@ -186,7 +193,7 @@ describe("Bridge e2e test", () => { let expectedPostL2 = preBalL2.sub(amt); expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); - }).timeout(120000) + }).timeout(1800000) it("should successfully deposit ETH to self from L1 to L2", async() => { // Get ETH balance on root & child chains before deposit @@ -205,6 +212,10 @@ describe("Bridge e2e test", () => { let postBalL1 = await rootProvider.getBalance(rootTestWallet.address); let postBalL2 = preBalL2; + console.log("Wait " + longWait + " ms"); + await delay(longWait); + console.log("Done"); + while (postBalL2.eq(preBalL2)) { postBalL2 = await childETH.balanceOf(childTestWallet.address); await delay(1000); @@ -217,7 +228,7 @@ describe("Bridge e2e test", () => { let expectedPostL2 = preBalL2.add(amt); expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); - }).timeout(60000) + }).timeout(1800000) it("should successfully deposit wETH to self from L1 to L2", async() => { // Wrap 0.01 ETH @@ -246,6 +257,10 @@ describe("Bridge e2e test", () => { let postBalL1 = await rootWETH.balanceOf(rootTestWallet.address); let postBalL2 = preBalL2; + console.log("Wait " + longWait + " ms"); + await delay(longWait); + console.log("Done"); + while (postBalL2.eq(preBalL2)) { postBalL2 = await childETH.balanceOf(childTestWallet.address); await delay(1000); @@ -256,7 +271,7 @@ describe("Bridge e2e test", () => { let expectedPostL2 = preBalL2.add(amt); expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); - }).timeout(60000) + }).timeout(1800000) it("should successfully withdraw ETH to self from L2 to L1", async() => { // Get ETH balance on root & child chains before withdraw @@ -278,6 +293,10 @@ describe("Bridge e2e test", () => { let postBalL1 = preBalL1; let postBalL2 = await childETH.balanceOf(childTestWallet.address); + console.log("Wait " + shortWait + " ms"); + await delay(shortWait); + console.log("Done"); + while (postBalL1.eq(preBalL1)) { postBalL1 = await rootProvider.getBalance(rootTestWallet.address); await delay(1000); @@ -288,9 +307,16 @@ describe("Bridge e2e test", () => { let expectedPostL2 = preBalL2.sub(amt); expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); - }).timeout(120000) + }).timeout(1800000) it("should successfully map a ERC20 Token", async() => { + let childContracts = getChildContracts(); + let childCustomTokenAddr = childContracts.CHILD_TEST_CUSTOM_TOKEN; + if (childCustomTokenAddr != "") { + childCustomToken = getContract("ChildERC20", childCustomTokenAddr, childProvider); + console.log("Custom token has already been mapped, skip."); + return; + } // Map token let bridgeFee = ethers.utils.parseEther("0.001"); let expectedChildTokenAddr = await rootBridge.callStatic.mapToken(rootCustomToken.address, { @@ -302,15 +328,22 @@ describe("Bridge e2e test", () => { await waitForReceipt(resp.hash, rootProvider); let childTokenAddr = await childBridge.rootTokenToChildToken(rootCustomToken.address); + + console.log("Wait " + longWait + " ms"); + await delay(longWait); + console.log("Done"); + while (childTokenAddr == ethers.constants.AddressZero) { childTokenAddr = await childBridge.rootTokenToChildToken(rootCustomToken.address); await delay(1000); } childCustomToken = getContract("ChildERC20", childTokenAddr, childProvider); + childContracts.CHILD_TEST_CUSTOM_TOKEN = childTokenAddr; + saveChildContracts(childContracts); // Verify expect(childTokenAddr).to.equal(expectedChildTokenAddr); - }).timeout(60000) + }).timeout(1800000) it("should successfully deposit mapped ERC20 Token to self from L1 to L2", async() => { // Get token balance on root & child chains before deposit @@ -332,6 +365,11 @@ describe("Bridge e2e test", () => { let postBalL1 = await rootCustomToken.balanceOf(rootTestWallet.address); let postBalL2 = preBalL2; + + console.log("Wait " + longWait + " ms"); + await delay(longWait); + console.log("Done"); + while (postBalL2.eq(preBalL2)) { postBalL2 = await childCustomToken.balanceOf(childTestWallet.address); await delay(1000); @@ -342,7 +380,7 @@ describe("Bridge e2e test", () => { let expectedPostL2 = preBalL2.add(amt); expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); - }).timeout(60000) + }).timeout(1800000) it("should successfully withdraw mapped ERC20 Token to self from L2 to L1", async() => { // Get token balance on root & child chains before deposit @@ -364,6 +402,10 @@ describe("Bridge e2e test", () => { let postBalL1 = preBalL1; let postBalL2 = await childCustomToken.balanceOf(childTestWallet.address); + console.log("Wait " + shortWait + " ms"); + await delay(shortWait); + console.log("Done"); + while (postBalL1.eq(preBalL1)) { postBalL1 = await rootCustomToken.balanceOf(rootTestWallet.address); await delay(1000); @@ -374,5 +416,5 @@ describe("Bridge e2e test", () => { let expectedPostL2 = preBalL2.sub(amt); expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); - }).timeout(120000) + }).timeout(1800000) }) \ No newline at end of file diff --git a/scripts/helpers/helpers.ts b/scripts/helpers/helpers.ts index 678a4be1..10f238ba 100644 --- a/scripts/helpers/helpers.ts +++ b/scripts/helpers/helpers.ts @@ -68,23 +68,90 @@ export function hasDuplicates(array: string[]) { return (new Set(array)).size !== array.length; } -export async function deployChildContract(contract: string, adminWallet: ethers.Wallet | LedgerSigner, ...args: any) { +export async function deployChildContract(contract: string, adminWallet: ethers.Wallet | LedgerSigner, reservedNonce: number | null, ...args: any) { let contractObj = JSON.parse(fs.readFileSync(`../../out/${contract}.sol/${contract}.json`, 'utf8')); let [priorityFee, maxFee] = await exports.getFee(adminWallet); let factory = new ContractFactory(contractObj.abi, contractObj.bytecode, adminWallet); - return await factory.deploy(...args, { - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); + let overrides; + if (reservedNonce != null) { + overrides = { + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + nonce: reservedNonce, + } + } else { + overrides = { + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + } + } + return await factory.deploy(...args, overrides); } -export async function deployRootContract(contract: string, adminWallet: ethers.Wallet | LedgerSigner, ...args: any) { +export async function deployRootContract(contract: string, adminWallet: ethers.Wallet | LedgerSigner, reservedNonce: number | null, ...args: any) { let contractObj = JSON.parse(fs.readFileSync(`../../out/${contract}.sol/${contract}.json`, 'utf8')); let factory = new ContractFactory(contractObj.abi, contractObj.bytecode, adminWallet); - return await factory.deploy(...args); + if (reservedNonce == null) { + return await factory.deploy(...args); + } else { + return await factory.deploy(...args, { + nonce: reservedNonce, + }) + } } export function getContract(contract: string, contractAddr: string, provider: providers.JsonRpcProvider) { let contractObj = JSON.parse(fs.readFileSync(`../../out/${contract}.sol/${contract}.json`, 'utf8')); return new ethers.Contract(contractAddr, contractObj.abi, provider); +} + +export function getChildContracts() { + let childContracts; + if (fs.existsSync(".child.bridge.contracts.json")) { + let data = fs.readFileSync(".child.bridge.contracts.json", 'utf-8'); + childContracts = JSON.parse(data); + } else { + childContracts = { + CHILD_PROXY_ADMIN: "", + CHILD_BRIDGE_IMPL_ADDRESS: "", + CHILD_BRIDGE_PROXY_ADDRESS: "", + CHILD_BRIDGE_ADDRESS: "", + CHILD_ADAPTOR_IMPL_ADDRESS: "", + CHILD_ADAPTOR_PROXY_ADDRESS: "", + CHILD_ADAPTOR_ADDRESS: "", + CHILD_TOKEN_TEMPLATE: "", + WRAPPED_IMX_ADDRESS: "", + CHILD_TEST_CUSTOM_TOKEN: "", + }; + } + return childContracts; +} + +export function saveChildContracts(contractData: any) { + fs.writeFileSync(".child.bridge.contracts.json", JSON.stringify(contractData, null, 2)); +} + +export function getRootContracts() { + let rootContracts; + if (fs.existsSync(".root.bridge.contracts.json")) { + let data = fs.readFileSync(".root.bridge.contracts.json", 'utf-8'); + rootContracts = JSON.parse(data); + } else { + rootContracts = { + ROOT_PROXY_ADMIN: "", + ROOT_BRIDGE_IMPL_ADDRESS: "", + ROOT_BRIDGE_PROXY_ADDRESS: "", + ROOT_BRIDGE_ADDRESS: "", + ROOT_ADAPTOR_IMPL_ADDRESS: "", + ROOT_ADAPTOR_PROXY_ADDRESS: "", + ROOT_ADAPTOR_ADDRESS: "", + ROOT_TOKEN_TEMPLATE: "", + ROOT_TEST_CUSTOM_TOKEN: "", + }; + } + return rootContracts; +} + +export function saveRootContracts(contractData: any) { + fs.writeFileSync(".root.bridge.contracts.json", JSON.stringify(contractData, null, 2)); } \ No newline at end of file diff --git a/scripts/localdev/.env.local b/scripts/localdev/.env.local index e4572792..af4b312b 100644 --- a/scripts/localdev/.env.local +++ b/scripts/localdev/.env.local @@ -1,74 +1,44 @@ -# Set prior to 1_deployer_funding.js +# Set prior to 0_pre_validation.js +# Name of the child chain MUST match Axelar's definition. CHILD_CHAIN_NAME="Immutable zkEVM E2E" +# The RPC URL of child chain. CHILD_RPC_URL=http://127.0.0.1:8501 +# The chain ID of the child chain. CHILD_CHAIN_ID=2501 +# Name of the root chain MUST match Axelar's definition. ROOT_CHAIN_NAME="Ethereum E2E" +# The RPC URL of root chain. ROOT_RPC_URL=http://127.0.0.1:8500 +# The chain ID of the root chain. ROOT_CHAIN_ID=2500 -## The admin EOA address on the child chain. -CHILD_ADMIN_ADDR=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 -## The private key for the admin EOA or "ledger" if using hardware wallet. -CHILD_ADMIN_EOA_SECRET=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 -## The deployer address on child chain. -CHILD_DEPLOYER_ADDR=0x70997970C51812dc3A010C7d01b50e0d17dc79C8 -## The private key for the deployer on child chain or "ledger" if using hardware wallet. -CHILD_DEPLOYER_SECRET=59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d -## The amount of fund deployer required on L2, unit is in IMX or 10^18 Wei. -CHILD_DEPLOYER_FUND=500 -## The deployer address on root chain. -ROOT_DEPLOYER_ADDR=0x70997970C51812dc3A010C7d01b50e0d17dc79C8 -## The private key for the deployer on root chain or "ledger" if using hardware wallet. -ROOT_DEPLOYER_SECRET=59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d -## The private key for rate admin or "ledger" if using hardware wallet. -ROOT_BRIDGE_RATE_ADMIN_SECRET=8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba +## The deployer address on child & root chains. +DEPLOYER_ADDR=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 +## The private key for the deployer on child & root chains or "ledger" if using hardware wallet. +DEPLOYER_SECRET=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 +## The ledger index for the deployer on child & root chains, required if using ledger. +DEPLOYER_LEDGER_INDEX= +## The nonce reserved deployer address on child & root chains. +NONCE_RESERVED_DEPLOYER_ADDR=0x70997970C51812dc3A010C7d01b50e0d17dc79C8 +## The nonce reserved deployer, or "ledger" if using hardware wallet. +NONCE_RESERVED_DEPLOYER_SECRET=59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d +## The ledger index for the nonce reserved deployer. +NONCE_RESERVED_DEPLOYER_INDEX= +## The reserved nonce for token template deployment. +NONCE_RESERVED=0 ## The IMX token address on root chain. ROOT_IMX_ADDR=0x73511669fd4dE447feD18BB79bAFeAC93aB7F31f ## The Wrapped ETH token address on the root chain. ROOT_WETH_ADDR=0xB581C9264f59BF0289fA76D61B2D0746dCE3C30D -## The Axelar address for receive initial funding on the child chain. +## The Axelar address to receive initial funding on the child chain. AXELAR_EOA=0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC ## The amount of fund Axelar requested, unit is in IMX or 10^18 Wei. AXELAR_FUND=500 +## The amount of fund deployer to be left with after bootstrapping on L2, unit is in IMX or 10^18 Wei. +CHILD_DEPLOYER_FUND=500 +## The amount of fund nonce reserved deployer required on L2, unit is in IMX or 10^18 Wei. +CHILD_NONCE_RESERVED_DEPLOYER_FUND=10 ## The maximum amount of IMX that can be deposited to L2, unit is in IMX or 10^18 Wei. -IMX_DEPOSIT_LIMIT=200000000 -## The address to perform child bridge upgrade. -CHILD_PROXY_ADMIN=0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 -## The address to be assigned with DEFAULT_ADMIN_ROLE in child bridge. -CHILD_BRIDGE_DEFAULT_ADMIN=0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc -## The address to be assigned with PAUSER_ROLE in child bridge. -CHILD_BRIDGE_PAUSER=0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc -## The address to be assigned with UNPAUSER_ROLE in child bridge. -CHILD_BRIDGE_UNPAUSER=0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc -## The address to be assigned with ADAPTOR_MANAGER_ROLE in child bridge. -CHILD_BRIDGE_ADAPTOR_MANAGER=0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc -## The address to be assigned with DEFAULT_ADMIN_ROLE in child adaptor. -CHILD_ADAPTOR_DEFAULT_ADMIN=0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc -## The address to be assigned with BRIDGE_MANAGER_ROLE in child adaptor. -CHILD_ADAPTOR_BRIDGE_MANAGER=0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc -## The address to be assigned with GAS_SERVICE_MANAGER_ROLE in child adaptor. -CHILD_ADAPTOR_GAS_SERVICE_MANAGER=0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc -## The address to be assigned with TARGET_MANAGER_ROLE in child adaptor. -CHILD_ADAPTOR_TARGET_MANAGER=0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc -## The address to perform root adaptor upgrade. -ROOT_PROXY_ADMIN=0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 -## The address to be assigned with DEFAULT_ADMIN_ROLE in root bridge. -ROOT_BRIDGE_DEFAULT_ADMIN=0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc -## The address to be assigned with PAUSER_ROLE in root bridge. -ROOT_BRIDGE_PAUSER=0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc -## The address to be assigned with UNPAUSER_ROLE in root bridge. -ROOT_BRIDGE_UNPAUSER=0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc -## The address to be assigned with VARIABLE_MANAGER_ROLE in root bridge. -ROOT_BRIDGE_VARIABLE_MANAGER=0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc -## The address to be assigned with ADAPTOR_MANAGER_ROLE in root bridge. -ROOT_BRIDGE_ADAPTOR_MANAGER=0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc -## The address to be assigned with DEFAULT_ADMIN_ROLE in root adaptor. -ROOT_ADAPTOR_DEFAULT_ADMIN=0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc -## The address to be assigned with BRIDGE_MANAGER_ROLE in root adaptor. -ROOT_ADAPTOR_BRIDGE_MANAGER=0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc -## The address to be assigned with GAS_SERVICE_MANAGER_ROLE in root adaptor. -ROOT_ADAPTOR_GAS_SERVICE_MANAGER=0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc -## The address to be assigned with TARGET_MANAGER_ROLE in root adaptor. -ROOT_ADAPTOR_TARGET_MANAGER=0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc +IMX_DEPOSIT_LIMIT=100000000 ## The capacity of the rate limit policy of IMX token, unit is in 10^18. RATE_LIMIT_IMX_CAPACITY=15516 ## The refill rate of the rate limit policy of IMX token, unit is in 10^18. diff --git a/scripts/localdev/README.md b/scripts/localdev/README.md index 00c6d7d9..407fee87 100644 --- a/scripts/localdev/README.md +++ b/scripts/localdev/README.md @@ -31,5 +31,5 @@ The addresses of deployed contracts will be saved in: To run end to end tests against local development network: ``` -npx mocha --require mocha-suppress-logs ../e2e/e2e.ts +LONG_WAIT=0 SHORT_WAIT=0 npx mocha --require mocha-suppress-logs ../e2e/e2e.ts ``` \ No newline at end of file diff --git a/scripts/localdev/childchain.config.ts b/scripts/localdev/childchain.config.ts index b7df4ed5..c91065e3 100644 --- a/scripts/localdev/childchain.config.ts +++ b/scripts/localdev/childchain.config.ts @@ -4,6 +4,7 @@ import "@nomicfoundation/hardhat-toolbox"; const config: HardhatUserConfig = { networks: { hardhat: { + hardfork: "grayGlacier", mining: { auto: false, interval: 200 diff --git a/scripts/localdev/childchain_setup.ts b/scripts/localdev/childchain_setup.ts index 889ffbd5..93a0abd1 100644 --- a/scripts/localdev/childchain_setup.ts +++ b/scripts/localdev/childchain_setup.ts @@ -7,21 +7,18 @@ import { requireEnv } from "../helpers/helpers"; async function main() { let childRPCURL = requireEnv("CHILD_RPC_URL"); let childChainID = requireEnv("CHILD_CHAIN_ID"); - let childEOAKey = requireEnv("CHILD_ADMIN_EOA_SECRET"); + let deployerAddr = requireEnv("DEPLOYER_ADDR"); // Get child provider. let childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); - // Get admin EOA on the child chain. - let childEOA = new ethers.Wallet(childEOAKey); - // Give admin EOA account 2B IMX. await hardhat.provider.send("hardhat_setBalance", [ - childEOA.address, + deployerAddr, "0x6765c793fa10079d0000000", ]); - console.log("Child admin EOA now has " + ethers.utils.formatEther(await childProvider.getBalance(childEOA.address)) + " IMX."); + console.log("Child admin EOA now has " + ethers.utils.formatEther(await childProvider.getBalance(deployerAddr)) + " IMX."); console.log("Finished setting up on child chain.") } main(); \ No newline at end of file diff --git a/scripts/localdev/deploy.sh b/scripts/localdev/deploy.sh index a770cc33..c5e2b24f 100755 --- a/scripts/localdev/deploy.sh +++ b/scripts/localdev/deploy.sh @@ -21,4 +21,7 @@ SKIP_WAIT_FOR_CONFIRMATION=true npx ts-node ../bootstrap/6_imx_burning.ts 2>&1 | SKIP_WAIT_FOR_CONFIRMATION=true npx ts-node ../bootstrap/7_imx_rebalancing.ts 2>&1 | tee -a bootstrap.out # Initialise root contracts -SKIP_WAIT_FOR_CONFIRMATION=true npx ts-node ../bootstrap/8_root_initialisation.ts 2>&1 | tee -a bootstrap.out \ No newline at end of file +SKIP_WAIT_FOR_CONFIRMATION=true npx ts-node ../bootstrap/8_root_initialisation.ts 2>&1 | tee -a bootstrap.out + +# Prepare for test +SKIP_WAIT_FOR_CONFIRMATION=true npx ts-node ../bootstrap/9_test_preparation.ts 2>&1 | tee -a bootstrap.out \ No newline at end of file diff --git a/scripts/localdev/rootchain.config.ts b/scripts/localdev/rootchain.config.ts index db622ae6..6ec638b6 100644 --- a/scripts/localdev/rootchain.config.ts +++ b/scripts/localdev/rootchain.config.ts @@ -4,6 +4,7 @@ import "@nomicfoundation/hardhat-toolbox"; const config: HardhatUserConfig = { networks: { hardhat: { + hardfork: "shanghai", mining: { auto: false, interval: 1200 diff --git a/scripts/localdev/rootchain_setup.ts b/scripts/localdev/rootchain_setup.ts index 0bd6f26d..abaaa503 100644 --- a/scripts/localdev/rootchain_setup.ts +++ b/scripts/localdev/rootchain_setup.ts @@ -2,32 +2,23 @@ import * as dotenv from "dotenv"; dotenv.config(); import { ethers as hardhat } from "hardhat"; import { ethers } from "ethers"; -import { requireEnv, deployRootContract, waitForReceipt } from "../helpers/helpers"; +import { requireEnv, deployRootContract, waitForReceipt, saveRootContracts } from "../helpers/helpers"; import * as fs from "fs"; async function main() { let rootRPCURL = requireEnv("ROOT_RPC_URL"); let rootChainID = requireEnv("ROOT_CHAIN_ID"); let rootAdminKey = requireEnv("ROOT_EOA_SECRET"); - let rootDeployerKey = requireEnv("ROOT_DEPLOYER_SECRET"); - let axelarDeployerKey = requireEnv("AXELAR_ROOT_EOA_SECRET"); + let deployerAddr = requireEnv("DEPLOYER_ADDR"); + let reservedAddr = requireEnv("NONCE_RESERVED_DEPLOYER_ADDR"); + let axelarEOA = requireEnv("AXELAR_EOA"); let rootTestKey = requireEnv("TEST_ACCOUNT_SECRET"); - let rootRateAdminKey = requireEnv("ROOT_BRIDGE_RATE_ADMIN_SECRET"); // Get root provider. let rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); - // Get deployer wallet on the root chain. - let rootDeployer = new ethers.Wallet(rootDeployerKey, rootProvider); - - // Get axelar wallet on the root chain. - let axelarDeployer = new ethers.Wallet(axelarDeployerKey, rootProvider); - // Get test wwallet on the root chain. let testWallet = new ethers.Wallet(rootTestKey, rootProvider); - - // Get rate admin wallet on the root chain. - let rateAdminWallet = new ethers.Wallet(rootRateAdminKey, rootProvider); // Get root admin eoa wallet. let admin = new ethers.Wallet(rootAdminKey, rootProvider); @@ -40,18 +31,18 @@ async function main() { // Deploy IMX contract console.log("Deploy IMX contract on root chain..."); - let IMX = await deployRootContract("ERC20PresetMinterPauser", admin, "IMX Token", "IMX"); + let IMX = await deployRootContract("ERC20PresetMinterPauser", admin, null, "IMX Token", "IMX"); await waitForReceipt(IMX.deployTransaction.hash, rootProvider); console.log("IMX deployed at: " + IMX.address); // Deploy WETH contract console.log("Deploy WETH contract on root chain..."); - let WETH = await deployRootContract("WETH", admin); + let WETH = await deployRootContract("WETH", admin, null); await waitForReceipt(WETH.deployTransaction.hash, rootProvider); console.log("WETH deployed at: " + WETH.address); - // Mint 1100 IMX to root deployer - let resp = await IMX.connect(admin).mint(rootDeployer.address, ethers.utils.parseEther("1100.0")); + // Mint 1110 IMX to root deployer + let resp = await IMX.connect(admin).mint(deployerAddr, ethers.utils.parseEther("1110.0")); await waitForReceipt(resp.hash, rootProvider); // Transfer 1000 IMX to test wallet @@ -60,14 +51,21 @@ async function main() { // Transfer 0.1 ETH to root deployer resp = await admin.sendTransaction({ - to: rootDeployer.address, + to: deployerAddr, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, rootProvider); + + // Transfer 0.1 ETH to nonce reserved root deployer + resp = await admin.sendTransaction({ + to: reservedAddr, value: ethers.utils.parseEther("0.1"), }) await waitForReceipt(resp.hash, rootProvider); // Transfer 500 ETH to axelar deployer resp = await admin.sendTransaction({ - to: axelarDeployer.address, + to: axelarEOA, value: ethers.utils.parseEther("500.0"), }) @@ -78,16 +76,9 @@ async function main() { }) await waitForReceipt(resp.hash, rootProvider); - // Transfer 0.1 ETH to rate admin - resp = await admin.sendTransaction({ - to: rateAdminWallet.address, - value: ethers.utils.parseEther("10.0"), - }) - await waitForReceipt(resp.hash, rootProvider); - - console.log("Root deployer now has " + ethers.utils.formatEther(await IMX.balanceOf(rootDeployer.address)) + " IMX."); - console.log("Root deployer now has " + ethers.utils.formatEther(await rootProvider.getBalance(rootDeployer.address)) + " ETH."); - console.log("Root axelar now has " + ethers.utils.formatEther(await rootProvider.getBalance(axelarDeployer.address)) + " ETH."); + console.log("Root deployer now has " + ethers.utils.formatEther(await IMX.balanceOf(deployerAddr)) + " IMX."); + console.log("Root deployer now has " + ethers.utils.formatEther(await rootProvider.getBalance(deployerAddr)) + " ETH."); + console.log("Root axelar now has " + ethers.utils.formatEther(await rootProvider.getBalance(axelarEOA)) + " ETH."); console.log("Finished setting up on root chain."); let contractData = { diff --git a/scripts/localdev/start.sh b/scripts/localdev/start.sh index 74205d77..bad47111 100755 --- a/scripts/localdev/start.sh +++ b/scripts/localdev/start.sh @@ -26,8 +26,11 @@ npx hardhat run ./childchain_setup.ts --config ./childchain.config.ts --network echo "Successfully setup root chain and child chain..." if [ -z ${LOCAL_CHAIN_ONLY+x} ]; then + # Pre validation + SKIP_WAIT_FOR_CONFIRMATION=true npx ts-node ../bootstrap/0_pre_validation.ts 2>&1 | tee bootstrap.out + # Fund accounts - SKIP_WAIT_FOR_CONFIRMATION=true npx ts-node ../bootstrap/1_deployer_funding.ts 2>&1 | tee bootstrap.out + SKIP_WAIT_FOR_CONFIRMATION=true npx ts-node ../bootstrap/1_deployer_funding.ts 2>&1 | tee -a bootstrap.out echo "Successfully run 1_deployer_funding.ts..." # Setup axelar From d90f3b43bdd8fc61650e208d5d15da6136fa2710 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Mon, 4 Dec 2023 10:45:35 +1000 Subject: [PATCH 04/40] Fix ledge issue with multiple accounts --- scripts/bootstrap/0_pre_validation.ts | 8 ++++++-- scripts/deploy/child_deployment.ts | 28 +++++++++++++++------------ scripts/deploy/root_deployment.ts | 28 +++++++++++++++------------ scripts/helpers/ledger_signer.ts | 4 ++++ 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/scripts/bootstrap/0_pre_validation.ts b/scripts/bootstrap/0_pre_validation.ts index 874a5515..746d6f64 100644 --- a/scripts/bootstrap/0_pre_validation.ts +++ b/scripts/bootstrap/0_pre_validation.ts @@ -74,6 +74,11 @@ async function run() { } else { deployerWallet = new ethers.Wallet(deployerSecret, childProvider); } + let actualDeployerAddress = await deployerWallet.getAddress(); + if (deployerWallet instanceof LedgerSigner) { + deployerWallet.close(); + } + let reservedWallet; if (reservedDeployerSecret == "ledger") { let index = requireEnv("NONCE_RESERVED_DEPLOYER_INDEX"); @@ -82,13 +87,12 @@ async function run() { } else { reservedWallet = new ethers.Wallet(reservedDeployerSecret, childProvider); } + let actualReservedDeployerAddress = await reservedWallet.getAddress(); // Check deployer address matches deployer addr - let actualDeployerAddress = await deployerWallet.getAddress(); if (actualDeployerAddress != deployerAddr) { tryThrow("Deployer addresses mismatch, expect " + deployerAddr + " actual " + actualDeployerAddress); } - let actualReservedDeployerAddress = await reservedWallet.getAddress(); if (actualReservedDeployerAddress != reservedDeployerAddr) { tryThrow("Reserved Nonce deployer addresses mismatch, expect " + reservedDeployerAddr + " actual " + actualReservedDeployerAddress); } diff --git a/scripts/deploy/child_deployment.ts b/scripts/deploy/child_deployment.ts index 71de36b2..41aa8477 100644 --- a/scripts/deploy/child_deployment.ts +++ b/scripts/deploy/child_deployment.ts @@ -17,19 +17,7 @@ export async function deployChildContracts() { // Read from contract file. let childContracts = getChildContracts(); - // Get deployer address const childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); - let childDeployerWallet; - if (deployerSecret == "ledger") { - let index = requireEnv("DEPLOYER_LEDGER_INDEX"); - const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; - childDeployerWallet = new LedgerSigner(childProvider, derivationPath); - } else { - childDeployerWallet = new ethers.Wallet(deployerSecret, childProvider); - } - let deployerAddr = await childDeployerWallet.getAddress(); - console.log("Deployer address is: ", deployerAddr); - // Get reserved wallet let reservedDeployerWallet; if (nonceReservedDeployerSecret == "ledger") { @@ -82,6 +70,22 @@ export async function deployChildContracts() { } console.log("Initialised CHILD_TOKEN_TEMPLATE at: ", childTokenTemplate.address); + if (reservedDeployerWallet instanceof LedgerSigner) { + reservedDeployerWallet.close(); + } + + // Get deployer address + let childDeployerWallet; + if (deployerSecret == "ledger") { + let index = requireEnv("DEPLOYER_LEDGER_INDEX"); + const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; + childDeployerWallet = new LedgerSigner(childProvider, derivationPath); + } else { + childDeployerWallet = new ethers.Wallet(deployerSecret, childProvider); + } + let deployerAddr = await childDeployerWallet.getAddress(); + console.log("Deployer address is: ", deployerAddr); + // Deploy wrapped IMX let wrappedIMX; if (childContracts.WRAPPED_IMX_ADDRESS != "") { diff --git a/scripts/deploy/root_deployment.ts b/scripts/deploy/root_deployment.ts index 68cac805..2b22136e 100644 --- a/scripts/deploy/root_deployment.ts +++ b/scripts/deploy/root_deployment.ts @@ -17,19 +17,7 @@ export async function deployRootContracts() { // Read from contract file. let rootContracts = getRootContracts(); - // Get deployer address const rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); - let rootDeployerWallet; - if (deployerSecret == "ledger") { - let index = requireEnv("DEPLOYER_LEDGER_INDEX"); - const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; - rootDeployerWallet = new LedgerSigner(rootProvider, derivationPath); - } else { - rootDeployerWallet = new ethers.Wallet(deployerSecret, rootProvider); - } - let deployerAddr = await rootDeployerWallet.getAddress(); - console.log("Deployer address is: ", deployerAddr); - // Get reserved wallet let reservedDeployerWallet; if (nonceReservedDeployerSecret == "ledger") { @@ -78,6 +66,22 @@ export async function deployRootContracts() { } console.log("Deployed to ROOT_TOKEN_TEMPLATE: ", rootTokenTemplate.address); + if (reservedDeployerWallet instanceof LedgerSigner) { + reservedDeployerWallet.close(); + } + + // Get deployer address + let rootDeployerWallet; + if (deployerSecret == "ledger") { + let index = requireEnv("DEPLOYER_LEDGER_INDEX"); + const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; + rootDeployerWallet = new LedgerSigner(rootProvider, derivationPath); + } else { + rootDeployerWallet = new ethers.Wallet(deployerSecret, rootProvider); + } + let deployerAddr = await rootDeployerWallet.getAddress(); + console.log("Deployer address is: ", deployerAddr); + // Deploy proxy admin let proxyAdmin; if (rootContracts.ROOT_PROXY_ADMIN != "") { diff --git a/scripts/helpers/ledger_signer.ts b/scripts/helpers/ledger_signer.ts index cb81315f..58b3e1c3 100644 --- a/scripts/helpers/ledger_signer.ts +++ b/scripts/helpers/ledger_signer.ts @@ -142,4 +142,8 @@ export class LedgerSigner extends ethers.Signer { connect(provider: ethers.providers.Provider): ethers.Signer { return new LedgerSigner(provider, this.path); } + + public async close() { + (await this._eth)?.transport.close(); + } } \ No newline at end of file From acdade4519d9bc71db77a049b483def677c77b18 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Tue, 5 Dec 2023 17:44:14 +1000 Subject: [PATCH 05/40] Fix role control --- scripts/bootstrap/.env.example | 4 +++ scripts/bootstrap/9_test_preparation.ts | 22 +++++++++++++-- scripts/bootstrap/README.md | 4 +++ scripts/deploy/.env.example | 4 +++ scripts/deploy/README.md | 4 +++ scripts/deploy/root_initialisation.ts | 37 +++++++++++++++++++------ scripts/localdev/.env.local | 4 +++ 7 files changed, 68 insertions(+), 11 deletions(-) diff --git a/scripts/bootstrap/.env.example b/scripts/bootstrap/.env.example index c64b22bb..00e96827 100644 --- a/scripts/bootstrap/.env.example +++ b/scripts/bootstrap/.env.example @@ -39,6 +39,10 @@ CHILD_DEPLOYER_FUND= CHILD_NONCE_RESERVED_DEPLOYER_FUND= ## The maximum amount of IMX that can be deposited to L2, unit is in IMX or 10^18 Wei. IMX_DEPOSIT_LIMIT= +## The privileged transaction Multisig address on the root chain. +PRIVILEGED_ROOT_MULTISIG_ADDR= +# The pauser address on the root chain. +ROOT_PAUSER_ADDR= ## The capacity of the rate limit policy of IMX token, unit is in 10^18. RATE_LIMIT_IMX_CAPACITY= ## The refill rate of the rate limit policy of IMX token, unit is in 10^18. diff --git a/scripts/bootstrap/9_test_preparation.ts b/scripts/bootstrap/9_test_preparation.ts index 49bac642..d0c3a72a 100644 --- a/scripts/bootstrap/9_test_preparation.ts +++ b/scripts/bootstrap/9_test_preparation.ts @@ -1,7 +1,7 @@ // Prepare for test import * as dotenv from "dotenv"; dotenv.config(); -import { ethers } from "ethers"; +import { ethers, utils } from "ethers"; import { deployRootContract, getContract, getRootContracts, requireEnv, saveRootContracts, waitForConfirmation, waitForReceipt } from "../helpers/helpers"; import { LedgerSigner } from "../helpers/ledger_signer"; @@ -13,6 +13,7 @@ async function run() { let rootChainID = requireEnv("ROOT_CHAIN_ID"); let deployerSecret = requireEnv("DEPLOYER_SECRET"); let testAccountKey = requireEnv("TEST_ACCOUNT_SECRET"); + let rootMultisigAddr = requireEnv("PRIVILEGED_ROOT_MULTISIG_ADDR"); // Get deployer address const rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); @@ -45,7 +46,7 @@ async function run() { console.log("Deploy root test custom token..."); rootCustomToken = await deployRootContract("ERC20PresetMinterPauser", rootDeployerWallet, null, "Custom Token", "CTK"); await waitForReceipt(rootCustomToken.deployTransaction.hash, rootProvider); - console.log("Custom token deployed to: ", rootCustomToken) + console.log("Custom token deployed to: ", rootCustomToken.address); } rootContracts.ROOT_TEST_CUSTOM_TOKEN=rootCustomToken.address; saveRootContracts(rootContracts); @@ -66,6 +67,23 @@ async function run() { ); await waitForReceipt(resp.hash, rootProvider); + // Revoke roles + console.log("Revoke RATE_CONTROL_ROLE of deployer...") + resp = await rootBridge.connect(rootDeployerWallet).revokeRole(utils.keccak256(utils.toUtf8Bytes("RATE")), deployerAddr); + console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, rootProvider); + + console.log("Revoke DEFAULT_ADMIN of deployer...") + resp = await rootBridge.connect(rootDeployerWallet).revokeRole(await rootBridge.DEFAULT_ADMIN_ROLE(), deployerAddr); + console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, rootProvider); + + // Print summary + console.log("Does multisig have DEFAULT_ADMIN: ", await rootBridge.hasRole(await rootBridge.DEFAULT_ADMIN_ROLE(), rootMultisigAddr)); + console.log("Does deployer have DEFAULT_ADMIN: ", await rootBridge.hasRole(await rootBridge.DEFAULT_ADMIN_ROLE(), deployerAddr)); + console.log("Does multisig have RATE_ADMIN: ", await rootBridge.hasRole(utils.keccak256(utils.toUtf8Bytes("RATE")), rootMultisigAddr)); + console.log("Does deployer have RATE_ADMIN: ", await rootBridge.hasRole(utils.keccak256(utils.toUtf8Bytes("RATE")), deployerAddr)); + console.log("=======End Test Preparation======="); } run(); \ No newline at end of file diff --git a/scripts/bootstrap/README.md b/scripts/bootstrap/README.md index 25d7242c..0c106618 100644 --- a/scripts/bootstrap/README.md +++ b/scripts/bootstrap/README.md @@ -63,6 +63,10 @@ CHILD_DEPLOYER_FUND= CHILD_NONCE_RESERVED_DEPLOYER_FUND= ## The maximum amount of IMX that can be deposited to L2, unit is in IMX or 10^18 Wei. IMX_DEPOSIT_LIMIT= +## The privileged transaction Multisig address on the root chain. +PRIVILEGED_ROOT_MULTISIG_ADDR= +# The pauser address on the root chain. +ROOT_PAUSER_ADDR= ## The capacity of the rate limit policy of IMX token, unit is in 10^18. RATE_LIMIT_IMX_CAPACITY= ## The refill rate of the rate limit policy of IMX token, unit is in 10^18. diff --git a/scripts/deploy/.env.example b/scripts/deploy/.env.example index d0b6c81f..2fa71b06 100644 --- a/scripts/deploy/.env.example +++ b/scripts/deploy/.env.example @@ -38,6 +38,10 @@ CHILD_DEPLOYER_FUND= CHILD_NONCE_RESERVED_DEPLOYER_FUND= ## The maximum amount of IMX that can be deposited to L2, unit is in IMX or 10^18 Wei. IMX_DEPOSIT_LIMIT= +## The privileged transaction Multisig address on the root chain. +PRIVILEGED_ROOT_MULTISIG_ADDR= +# The pauser address on the root chain. +ROOT_PAUSER_ADDR= ## The capacity of the rate limit policy of IMX token, unit is in 10^18. RATE_LIMIT_IMX_CAPACITY= ## The refill rate of the rate limit policy of IMX token, unit is in 10^18. diff --git a/scripts/deploy/README.md b/scripts/deploy/README.md index b9b6f4af..3c38f3f6 100644 --- a/scripts/deploy/README.md +++ b/scripts/deploy/README.md @@ -52,6 +52,10 @@ CHILD_DEPLOYER_FUND= CHILD_NONCE_RESERVED_DEPLOYER_FUND= ## The maximum amount of IMX that can be deposited to L2, unit is in IMX or 10^18 Wei. IMX_DEPOSIT_LIMIT= +## The privileged transaction Multisig address on the root chain. +PRIVILEGED_ROOT_MULTISIG_ADDR= +# The pauser address on the root chain. +ROOT_PAUSER_ADDR= ## The capacity of the rate limit policy of IMX token, unit is in 10^18. RATE_LIMIT_IMX_CAPACITY= ## The refill rate of the rate limit policy of IMX token, unit is in 10^18. diff --git a/scripts/deploy/root_initialisation.ts b/scripts/deploy/root_initialisation.ts index a11448b4..4051d25e 100644 --- a/scripts/deploy/root_initialisation.ts +++ b/scripts/deploy/root_initialisation.ts @@ -1,7 +1,7 @@ // Initialise root contracts import * as dotenv from "dotenv"; dotenv.config(); -import { ethers } from "ethers"; +import { ethers, utils } from "ethers"; import { requireEnv, waitForConfirmation, waitForReceipt, getContract, getChildContracts, getRootContracts } from "../helpers/helpers"; import { LedgerSigner } from "../helpers/ledger_signer"; @@ -15,6 +15,8 @@ export async function initialiseRootContracts() { let rootIMXAddr = requireEnv("ROOT_IMX_ADDR"); let rootWETHAddr = requireEnv("ROOT_WETH_ADDR"); let imxDepositLimit = requireEnv("IMX_DEPOSIT_LIMIT"); + let rootMultisigAddr = requireEnv("PRIVILEGED_ROOT_MULTISIG_ADDR"); + let rootPauser = requireEnv("ROOT_PAUSER_ADDR"); let rateLimitIMXCap = requireEnv("RATE_LIMIT_IMX_CAPACITY"); let rateLimitIMXRefill = requireEnv("RATE_LIMIT_IMX_REFILL_RATE"); let rateLimitIMXLargeThreshold = requireEnv("RATE_LIMIT_IMX_LARGE_THRESHOLD"); @@ -70,10 +72,10 @@ export async function initialiseRootContracts() { let resp = await rootBridge.connect(rootDeployerWallet)["initialize((address,address,address,address,address),address,address,address,address,address,uint256,address)"]( { defaultAdmin: deployerAddr, - pauser: deployerAddr, - unpauser: deployerAddr, - variableManager: deployerAddr, - adaptorManager: deployerAddr, + pauser: rootPauser, + unpauser: rootPauser, + variableManager: rootMultisigAddr, + adaptorManager: rootMultisigAddr, }, rootAdaptorAddr, childBridgeAddr, @@ -152,15 +154,32 @@ export async function initialiseRootContracts() { console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); await waitForReceipt(resp.hash, rootProvider); + // Grant roles + console.log("Grant RATE_CONTROL_ROLE to multisig...") + resp = await rootBridge.connect(rootDeployerWallet).grantRole(utils.keccak256(utils.toUtf8Bytes("RATE")), rootMultisigAddr); + console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, rootProvider); + + console.log("Grant DEFAULT_ADMIN to multisig...") + resp = await rootBridge.connect(rootDeployerWallet).grantRole(await rootBridge.DEFAULT_ADMIN_ROLE(), rootMultisigAddr); + console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, rootProvider); + + // Print summary + console.log("Does multisig have DEFAULT_ADMIN: ", await rootBridge.hasRole(await rootBridge.DEFAULT_ADMIN_ROLE(), rootMultisigAddr)); + console.log("Does deployer have DEFAULT_ADMIN: ", await rootBridge.hasRole(await rootBridge.DEFAULT_ADMIN_ROLE(), deployerAddr)); + console.log("Does multisig have RATE_ADMIN: ", await rootBridge.hasRole(utils.keccak256(utils.toUtf8Bytes("RATE")), rootMultisigAddr)); + console.log("Does deployer have RATE_ADMIN: ", await rootBridge.hasRole(utils.keccak256(utils.toUtf8Bytes("RATE")), deployerAddr)); + // Initialise root adaptor console.log("Initialise root adaptor..."); let rootAdaptor = getContract("RootAxelarBridgeAdaptor", rootAdaptorAddr, rootProvider); resp = await rootAdaptor.connect(rootDeployerWallet).initialize( { - defaultAdmin: deployerAddr, - bridgeManager: deployerAddr, - gasServiceManager: deployerAddr, - targetManager: deployerAddr, + defaultAdmin: rootMultisigAddr, + bridgeManager: rootMultisigAddr, + gasServiceManager: rootMultisigAddr, + targetManager: rootMultisigAddr, }, rootBridgeAddr, childChainName, diff --git a/scripts/localdev/.env.local b/scripts/localdev/.env.local index af4b312b..fd3713fe 100644 --- a/scripts/localdev/.env.local +++ b/scripts/localdev/.env.local @@ -39,6 +39,10 @@ CHILD_DEPLOYER_FUND=500 CHILD_NONCE_RESERVED_DEPLOYER_FUND=10 ## The maximum amount of IMX that can be deposited to L2, unit is in IMX or 10^18 Wei. IMX_DEPOSIT_LIMIT=100000000 +## The privileged transaction multisig address on the root chain. +PRIVILEGED_ROOT_MULTISIG_ADDR=0x14dC79964da2C08b23698B3D3cc7Ca32193d9955 +# The pauser address on the root chain. +ROOT_PAUSER_ADDR=0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f ## The capacity of the rate limit policy of IMX token, unit is in 10^18. RATE_LIMIT_IMX_CAPACITY=15516 ## The refill rate of the rate limit policy of IMX token, unit is in 10^18. From 5b7ced75084c5411948cf23bae5713c88a752caa Mon Sep 17 00:00:00 2001 From: Ermyas Abebe Date: Wed, 6 Dec 2023 08:24:02 +1100 Subject: [PATCH 06/40] Add create2 contract deployer with access control --- src/deploy/OwnableCreate2Deployer.sol | 46 +++++ test/unit/deploy/OwnableCreate2Deployer.t.sol | 172 ++++++++++++++++++ 2 files changed, 218 insertions(+) create mode 100644 src/deploy/OwnableCreate2Deployer.sol create mode 100644 test/unit/deploy/OwnableCreate2Deployer.t.sol diff --git a/src/deploy/OwnableCreate2Deployer.sol b/src/deploy/OwnableCreate2Deployer.sol new file mode 100644 index 00000000..f938a688 --- /dev/null +++ b/src/deploy/OwnableCreate2Deployer.sol @@ -0,0 +1,46 @@ +// Copyright Immutable Pty Ltd 2018 - 2023 +// SPDX-License-Identifier: Apache 2.0 +pragma solidity 0.8.19; + +import "@openzeppelin/contracts/access/Ownable.sol"; +import {Deployer} from "@axelar-gmp-sdk-solidity/contracts/deploy/Deployer.sol"; +import {Create2} from "@axelar-gmp-sdk-solidity/contracts/deploy/Create2.sol"; + +/** + * @title OwnableCreate2Deployer + * @notice Deploys and optionally initializes contracts using the `CREATE2` opcode. + * @dev This contract extends the {Deployer} contract from the Axelar SDK, by adding basic access control to the deployment functions. + * The contract has an owner, which is the only entity that can deploy new contracts. + * The owner is initially set to the deployer of this contract and can be changed using {transferOwnership}. + * + * @dev The contract deploys a contract with the same bytecode, salt, and sender(owner) to the same address. + * Attempting to deploy a contract with the same bytecode, salt, and sender(owner) will revert. + * The address where the contract will be deployed can be found using {deployedAddress}. + */ +contract OwnableCreate2Deployer is Ownable, Create2, Deployer { + constructor() Ownable() {} + + /** + * @dev Deploys a contract using the `CREATE2` opcode. + * This function is called by {deploy} and {deployAndInit} external functions in the {Deployer} contract. + * This function can only be called by the owner of this contract, hence {deploy} and {deployAndInit} can only be called by the owner. + * The address where the contract will be deployed can be found using {deployedAddress}. + * @param bytecode The bytecode of the contract to be deployed + * @param deploySalt A salt which is a hash of the salt provided by the sender and the sender's address. + * @return The address of the deployed contract + */ + function _deploy(bytes memory bytecode, bytes32 deploySalt) internal override onlyOwner returns (address) { + return _create2(bytecode, deploySalt); + } + + /** + * @dev Returns the address where a contract will be stored if deployed via {deploy} or {deployAndInit}. + * This function is called by the {deployedAddress} external functions in the {Deployer} contract. + * @param bytecode The bytecode of the contract to be deployed + * @param deploySalt A salt which is a hash of the salt provided by the sender and the sender's address. + * @return The predicted deployment address of the contract + */ + function _deployedAddress(bytes memory bytecode, bytes32 deploySalt) internal view override returns (address) { + return _create2Address(bytecode, deploySalt); + } +} diff --git a/test/unit/deploy/OwnableCreate2Deployer.t.sol b/test/unit/deploy/OwnableCreate2Deployer.t.sol new file mode 100644 index 00000000..37dc1834 --- /dev/null +++ b/test/unit/deploy/OwnableCreate2Deployer.t.sol @@ -0,0 +1,172 @@ +// Copyright Immutable Pty Ltd 2018 - 2023 +// SPDX-License-Identifier: Apache 2.0 +pragma solidity 0.8.19; + +import "forge-std/Test.sol"; +import {IDeploy} from "@axelar-gmp-sdk-solidity/contracts/interfaces/IDeploy.sol"; +import {IDeployer} from "@axelar-gmp-sdk-solidity/contracts/interfaces/IDeployer.sol"; + +import {ChildERC20} from "../../../src/child/ChildERC20.sol"; +import {OwnableCreate2Deployer} from "../../../src/deploy/OwnableCreate2Deployer.sol"; + +contract OwnableCreate2DeployerTest is Test { + OwnableCreate2Deployer private deployer; + ChildERC20 private childERC20; + + bytes private childERC20Bytecode; + bytes32 private salt; + + event Deployed(address indexed deployedAddress, address indexed sender, bytes32 indexed salt, bytes32 bytecodeHash); + + function setUp() public { + // create a new deployer that is owned by this contract + deployer = new OwnableCreate2Deployer(); + + childERC20 = new ChildERC20(); + childERC20Bytecode = type(ChildERC20).creationCode; + + salt = createSaltFromKey("test-salt"); + } + + function test_RevertIf_DeployWithEmptyByteCode() public { + vm.expectRevert(IDeploy.EmptyBytecode.selector); + deployer.deploy("", salt); + } + + function test_RevertIf_DeployWithNonOwner() public { + address nonOwner = address(0x1); + vm.startPrank(nonOwner); + vm.expectRevert("Ownable: caller is not the owner"); + deployer.deploy(childERC20Bytecode, salt); + } + + /// @dev deploying with the same bytecode, salt and sender should revert + function test_RevertIf_DeployAlreadyDeployedCreate2Contract() public { + deployer.deploy(childERC20Bytecode, salt); + + vm.expectRevert(IDeploy.AlreadyDeployed.selector); + deployer.deploy(childERC20Bytecode, salt); + } + + function test_deploy_DeploysContract() public { + address expectedAddress = predictCreate2Address(childERC20Bytecode, address(deployer), address(this), salt); + + vm.expectEmit(); + emit Deployed(expectedAddress, address(this), salt, keccak256(childERC20Bytecode)); + address deployed = deployer.deploy(childERC20Bytecode, salt); + + assertEq(deployed.code, address(childERC20).code, "deployed contract code does not match expected"); + + ChildERC20 deployedChildERC20 = ChildERC20(deployed); + assertEq(deployedChildERC20.name(), "", "deployed contract should have empty name"); + assertEq(deployedChildERC20.symbol(), "", "deployed contract should have empty symbol"); + assertEq(deployedChildERC20.decimals(), 0, "deployed contract should have 0 decimals"); + } + + function test_deploy_DeploysToPredictedAddress() public { + address deployedAddress = deployer.deploy(childERC20Bytecode, salt); + address expectedAddress = predictCreate2Address(childERC20Bytecode, address(deployer), address(this), salt); + assertEq(deployedAddress, expectedAddress, "deployed address does not match expected address"); + } + + function test_deploy_DeploysSameContractToDifferentAddresses_GivenDifferentSalts() public { + address deployed1 = deployer.deploy(childERC20Bytecode, salt); + + bytes32 newSalt = createSaltFromKey("new-salt"); + address deployed2 = deployer.deploy(childERC20Bytecode, newSalt); + + assertEq(deployed1.code, deployed2.code, "bytecode of deployed contracts do not match"); + assertNotEq(deployed1, deployed2, "deployed contracts should not have the same address"); + } + + function test_deploy_DeploysContractGivenNewOwner() public { + address newOwner = address(0x1); + + deployer.transferOwnership(newOwner); + assertEq(deployer.owner(), newOwner, "owner did not change as expected"); + + // check that the old owner cannot deploy + vm.expectRevert("Ownable: caller is not the owner"); + deployer.deploy(childERC20Bytecode, salt); + + // test that the new owner can deploy + vm.startPrank(newOwner); + address expectedAddress = predictCreate2Address(childERC20Bytecode, address(deployer), address(newOwner), salt); + + vm.expectEmit(); + emit Deployed(expectedAddress, address(newOwner), salt, keccak256(childERC20Bytecode)); + address deployed = deployer.deploy(childERC20Bytecode, salt); + + assertEq(deployed.code, address(childERC20).code, "deployed contract should match expected"); + } + + /** + * deployAndInit + */ + + function test_RevertIf_DeployAndInitWithNonOwner() public { + address nonOwner = address(0x1); + vm.startPrank(nonOwner); + vm.expectRevert("Ownable: caller is not the owner"); + deployer.deployAndInit(childERC20Bytecode, salt, ""); + } + + function test_deployAndInit_DeploysAndInitsContract() public { + address expectedAddress = predictCreate2Address(childERC20Bytecode, address(deployer), address(this), salt); + address rootToken = address(0x1); + string memory name = "Test-Token"; + string memory symbol = "TST"; + uint8 decimals = 18; + bytes memory initPayload = + abi.encodeWithSelector(ChildERC20.initialize.selector, rootToken, name, symbol, decimals); + + vm.expectEmit(); + emit Deployed(expectedAddress, address(this), salt, keccak256(childERC20Bytecode)); + address deployed = deployer.deployAndInit(childERC20Bytecode, salt, initPayload); + + // regardless of init data, the deployed address should match expected deployment + assertEq(deployed, expectedAddress, "deployed address should match expected address"); + + assertEq(deployed.code, address(childERC20).code, "deployed contract should match expected"); + + // verify initialisation + ChildERC20 deployedChildERC20 = ChildERC20(deployed); + assertEq(deployedChildERC20.rootToken(), rootToken, "rootToken does not match expected"); + assertEq(deployedChildERC20.name(), name, "name does not match expected"); + assertEq(deployedChildERC20.symbol(), symbol, "symbol does not match expected"); + assertEq(deployedChildERC20.decimals(), decimals, "decimals does not match expected"); + } + + /** + * deployedAddress + */ + + function test_deployedAddress_ReturnsPredictedAddress() public { + address deployAddress = deployer.deployedAddress(childERC20Bytecode, address(this), salt); + + address predictedAddress = predictCreate2Address(childERC20Bytecode, address(deployer), address(this), salt); + address deployedAddress = deployer.deploy(childERC20Bytecode, salt); + + assertEq(deployAddress, predictedAddress, "deployment address did not match predicted address"); + assertEq(deployAddress, deployedAddress, "deployment address did not match deployed address"); + } + + /** + * private helper functions + */ + + function predictCreate2Address(bytes memory _bytecode, address _deployer, address _sender, bytes32 _salt) + private + pure + returns (address) + { + bytes32 deploySalt = keccak256(abi.encode(_sender, _salt)); + return address( + uint160(uint256(keccak256(abi.encodePacked(hex"ff", address(_deployer), deploySalt, keccak256(_bytecode))))) + ); + } + + function createSaltFromKey(string memory key) private view returns (bytes32) { + return keccak256(abi.encode(address(this), key)); + } +} From 81747bd5049454d4633655f7c83ffd480b637073 Mon Sep 17 00:00:00 2001 From: Ermyas Abebe Date: Wed, 6 Dec 2023 10:51:16 +1100 Subject: [PATCH 07/40] Accept intended owner in constructor --- src/deploy/OwnableCreate2Deployer.sol | 5 ++-- test/unit/deploy/OwnableCreate2Deployer.t.sol | 26 ++++++++++++------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/deploy/OwnableCreate2Deployer.sol b/src/deploy/OwnableCreate2Deployer.sol index f938a688..8aa5de05 100644 --- a/src/deploy/OwnableCreate2Deployer.sol +++ b/src/deploy/OwnableCreate2Deployer.sol @@ -11,14 +11,15 @@ import {Create2} from "@axelar-gmp-sdk-solidity/contracts/deploy/Create2.sol"; * @notice Deploys and optionally initializes contracts using the `CREATE2` opcode. * @dev This contract extends the {Deployer} contract from the Axelar SDK, by adding basic access control to the deployment functions. * The contract has an owner, which is the only entity that can deploy new contracts. - * The owner is initially set to the deployer of this contract and can be changed using {transferOwnership}. * * @dev The contract deploys a contract with the same bytecode, salt, and sender(owner) to the same address. * Attempting to deploy a contract with the same bytecode, salt, and sender(owner) will revert. * The address where the contract will be deployed can be found using {deployedAddress}. */ contract OwnableCreate2Deployer is Ownable, Create2, Deployer { - constructor() Ownable() {} + constructor(address owner) Ownable() { + transferOwnership(owner); + } /** * @dev Deploys a contract using the `CREATE2` opcode. diff --git a/test/unit/deploy/OwnableCreate2Deployer.t.sol b/test/unit/deploy/OwnableCreate2Deployer.t.sol index 37dc1834..a0bf0bce 100644 --- a/test/unit/deploy/OwnableCreate2Deployer.t.sol +++ b/test/unit/deploy/OwnableCreate2Deployer.t.sol @@ -15,17 +15,21 @@ contract OwnableCreate2DeployerTest is Test { bytes private childERC20Bytecode; bytes32 private salt; + address private owner; event Deployed(address indexed deployedAddress, address indexed sender, bytes32 indexed salt, bytes32 bytecodeHash); function setUp() public { + owner = address(0x12345); + // create a new deployer that is owned by this contract - deployer = new OwnableCreate2Deployer(); + deployer = new OwnableCreate2Deployer(owner); childERC20 = new ChildERC20(); childERC20Bytecode = type(ChildERC20).creationCode; salt = createSaltFromKey("test-salt"); + vm.startPrank(owner); } function test_RevertIf_DeployWithEmptyByteCode() public { @@ -34,6 +38,8 @@ contract OwnableCreate2DeployerTest is Test { } function test_RevertIf_DeployWithNonOwner() public { + vm.stopPrank(); + address nonOwner = address(0x1); vm.startPrank(nonOwner); vm.expectRevert("Ownable: caller is not the owner"); @@ -49,10 +55,10 @@ contract OwnableCreate2DeployerTest is Test { } function test_deploy_DeploysContract() public { - address expectedAddress = predictCreate2Address(childERC20Bytecode, address(deployer), address(this), salt); + address expectedAddress = predictCreate2Address(childERC20Bytecode, address(deployer), address(owner), salt); vm.expectEmit(); - emit Deployed(expectedAddress, address(this), salt, keccak256(childERC20Bytecode)); + emit Deployed(expectedAddress, address(owner), salt, keccak256(childERC20Bytecode)); address deployed = deployer.deploy(childERC20Bytecode, salt); assertEq(deployed.code, address(childERC20).code, "deployed contract code does not match expected"); @@ -65,7 +71,7 @@ contract OwnableCreate2DeployerTest is Test { function test_deploy_DeploysToPredictedAddress() public { address deployedAddress = deployer.deploy(childERC20Bytecode, salt); - address expectedAddress = predictCreate2Address(childERC20Bytecode, address(deployer), address(this), salt); + address expectedAddress = predictCreate2Address(childERC20Bytecode, address(deployer), address(owner), salt); assertEq(deployedAddress, expectedAddress, "deployed address does not match expected address"); } @@ -105,6 +111,8 @@ contract OwnableCreate2DeployerTest is Test { */ function test_RevertIf_DeployAndInitWithNonOwner() public { + vm.stopPrank(); + address nonOwner = address(0x1); vm.startPrank(nonOwner); vm.expectRevert("Ownable: caller is not the owner"); @@ -112,7 +120,7 @@ contract OwnableCreate2DeployerTest is Test { } function test_deployAndInit_DeploysAndInitsContract() public { - address expectedAddress = predictCreate2Address(childERC20Bytecode, address(deployer), address(this), salt); + address expectedAddress = predictCreate2Address(childERC20Bytecode, address(deployer), address(owner), salt); address rootToken = address(0x1); string memory name = "Test-Token"; string memory symbol = "TST"; @@ -121,7 +129,7 @@ contract OwnableCreate2DeployerTest is Test { abi.encodeWithSelector(ChildERC20.initialize.selector, rootToken, name, symbol, decimals); vm.expectEmit(); - emit Deployed(expectedAddress, address(this), salt, keccak256(childERC20Bytecode)); + emit Deployed(expectedAddress, address(owner), salt, keccak256(childERC20Bytecode)); address deployed = deployer.deployAndInit(childERC20Bytecode, salt, initPayload); // regardless of init data, the deployed address should match expected deployment @@ -142,9 +150,9 @@ contract OwnableCreate2DeployerTest is Test { */ function test_deployedAddress_ReturnsPredictedAddress() public { - address deployAddress = deployer.deployedAddress(childERC20Bytecode, address(this), salt); + address deployAddress = deployer.deployedAddress(childERC20Bytecode, address(owner), salt); - address predictedAddress = predictCreate2Address(childERC20Bytecode, address(deployer), address(this), salt); + address predictedAddress = predictCreate2Address(childERC20Bytecode, address(deployer), address(owner), salt); address deployedAddress = deployer.deploy(childERC20Bytecode, salt); assertEq(deployAddress, predictedAddress, "deployment address did not match predicted address"); @@ -167,6 +175,6 @@ contract OwnableCreate2DeployerTest is Test { } function createSaltFromKey(string memory key) private view returns (bytes32) { - return keccak256(abi.encode(address(this), key)); + return keccak256(abi.encode(address(owner), key)); } } From 42debb974a9c522a42d59fa78d81fb940d5bcf77 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Wed, 6 Dec 2023 14:24:05 +1000 Subject: [PATCH 08/40] Fix issue --- scripts/deploy/child_deployment.ts | 11 +++++------ scripts/deploy/root_deployment.ts | 11 +++++------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/scripts/deploy/child_deployment.ts b/scripts/deploy/child_deployment.ts index 41aa8477..df7166c3 100644 --- a/scripts/deploy/child_deployment.ts +++ b/scripts/deploy/child_deployment.ts @@ -30,12 +30,6 @@ export async function deployChildContracts() { let reservedDeployerAddr = await reservedDeployerWallet.getAddress(); console.log("Reserved deployer address is: ", reservedDeployerAddr); - // Check the current nonce matches the reserved nonce - let currentNonce = await childProvider.getTransactionCount(reservedDeployerAddr); - if (nonceReserved != currentNonce) { - throw("Nonce mismatch, expected " + nonceReserved + " actual " + currentNonce); - } - // Execute console.log("Deploy child contracts in..."); await waitForConfirmation(); @@ -46,6 +40,11 @@ export async function deployChildContracts() { console.log("Child token template has already been deployed to: " + childContracts.CHILD_TOKEN_TEMPLATE + ", skip."); childTokenTemplate = getContract("ChildERC20", childContracts.CHILD_TOKEN_TEMPLATE, childProvider); } else { + // Check the current nonce matches the reserved nonce + let currentNonce = await childProvider.getTransactionCount(reservedDeployerAddr); + if (nonceReserved != currentNonce) { + throw("Nonce mismatch, expected " + nonceReserved + " actual " + currentNonce); + } console.log("Deploy child token template..."); childTokenTemplate = await deployChildContract("ChildERC20", reservedDeployerWallet, nonceReserved); console.log("Transaction submitted: ", JSON.stringify(childTokenTemplate.deployTransaction, null, 2)); diff --git a/scripts/deploy/root_deployment.ts b/scripts/deploy/root_deployment.ts index 2b22136e..c0561f04 100644 --- a/scripts/deploy/root_deployment.ts +++ b/scripts/deploy/root_deployment.ts @@ -30,12 +30,6 @@ export async function deployRootContracts() { let reservedDeployerAddr = await reservedDeployerWallet.getAddress(); console.log("Reserved deployer address is: ", reservedDeployerAddr); - // Check the current nonce matches the reserved nonce - let currentNonce = await rootProvider.getTransactionCount(reservedDeployerAddr); - if (nonceReserved != currentNonce) { - throw("Nonce mismatch, expected " + nonceReserved + " actual " + currentNonce); - } - // Execute console.log("Deploy root contracts in..."); await waitForConfirmation(); @@ -46,6 +40,11 @@ export async function deployRootContracts() { console.log("Root token template has already been deployed to: " + rootContracts.ROOT_TOKEN_TEMPLATE + ", skip."); rootTokenTemplate = getContract("ChildERC20", rootContracts.ROOT_TOKEN_TEMPLATE, rootProvider); } else { + // Check the current nonce matches the reserved nonce + let currentNonce = await rootProvider.getTransactionCount(reservedDeployerAddr); + if (nonceReserved != currentNonce) { + throw("Nonce mismatch, expected " + nonceReserved + " actual " + currentNonce); + } console.log("Deploy root token template..."); rootTokenTemplate = await deployRootContract("ChildERC20", reservedDeployerWallet, nonceReserved); console.log("Transaction submitted: ", JSON.stringify(rootTokenTemplate.deployTransaction, null, 2)); From 9e5dddc4fcb1e53ebc840a2229e0f2ec7ec1f2bd Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Wed, 6 Dec 2023 16:32:51 +1000 Subject: [PATCH 09/40] Fix defaults --- scripts/bootstrap/0_pre_validation.ts | 4 +- scripts/deploy/child_deployment.ts | 152 ++++++++++++++------------ scripts/deploy/root_deployment.ts | 124 ++++++++++++--------- scripts/localdev/.env.local | 4 +- 4 files changed, 158 insertions(+), 126 deletions(-) diff --git a/scripts/bootstrap/0_pre_validation.ts b/scripts/bootstrap/0_pre_validation.ts index 746d6f64..23cd044d 100644 --- a/scripts/bootstrap/0_pre_validation.ts +++ b/scripts/bootstrap/0_pre_validation.ts @@ -122,10 +122,10 @@ async function run() { if (axelarRequiredIMX.lt(ethers.utils.parseEther("500.0"))) { tryThrow("Axelar on child chain should request at least 500 IMX, got" + ethers.utils.formatEther(axelarRequiredIMX)); } - if (deployerRequiredIMX.lt(ethers.utils.parseEther("500.0"))) { + if (deployerRequiredIMX.lt(ethers.utils.parseEther("250.0"))) { tryThrow("Deployer on child chain should request at least 500 IMX, got" + ethers.utils.formatEther(deployerRequiredIMX)); } - if (reservedDeployerRequiredIMX.lt(ethers.utils.parseEther("10.0"))) { + if (reservedDeployerRequiredIMX.lt(ethers.utils.parseEther("250.0"))) { tryThrow("Reserved deployer on child chain should request at least 10 IMX, got" + ethers.utils.formatEther(reservedDeployerRequiredIMX)); } let extraIMX = ethers.utils.parseEther("100.0"); diff --git a/scripts/deploy/child_deployment.ts b/scripts/deploy/child_deployment.ts index df7166c3..ff17a8b6 100644 --- a/scripts/deploy/child_deployment.ts +++ b/scripts/deploy/child_deployment.ts @@ -18,6 +18,72 @@ export async function deployChildContracts() { let childContracts = getChildContracts(); const childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); + + // Get deployer address + let childDeployerWallet; + if (deployerSecret == "ledger") { + let index = requireEnv("DEPLOYER_LEDGER_INDEX"); + const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; + childDeployerWallet = new LedgerSigner(childProvider, derivationPath); + } else { + childDeployerWallet = new ethers.Wallet(deployerSecret, childProvider); + } + let deployerAddr = await childDeployerWallet.getAddress(); + console.log("Deployer address is: ", deployerAddr); + + // Execute + console.log("Deploy child contracts in..."); + await waitForConfirmation(); + + // Deploy wrapped IMX + let wrappedIMX; + if (childContracts.WRAPPED_IMX_ADDRESS != "") { + console.log("Wrapped IMX has already been deployed to: " + childContracts.WRAPPED_IMX_ADDRESS + ", skip."); + wrappedIMX = getContract("WIMX", childContracts.WRAPPED_IMX_ADDRESS, childProvider); + } else { + console.log("Deploy wrapped IMX..."); + wrappedIMX = await deployChildContract("WIMX", childDeployerWallet, null); + console.log("Transaction submitted: ", JSON.stringify(wrappedIMX.deployTransaction, null, 2)); + await waitForReceipt(wrappedIMX.deployTransaction.hash, childProvider); + } + childContracts.WRAPPED_IMX_ADDRESS = wrappedIMX.address; + saveChildContracts(childContracts); + console.log("Deployed to WRAPPED_IMX_ADDRESS: ", wrappedIMX.address); + + // Deploy child bridge impl + let childBridgeImpl; + if (childContracts.CHILD_BRIDGE_IMPL_ADDRESS != "") { + console.log("Child bridge impl has already been deployed to: " + childContracts.CHILD_BRIDGE_IMPL_ADDRESS + ", skip."); + childBridgeImpl = getContract("ChildERC20Bridge", childContracts.CHILD_BRIDGE_IMPL_ADDRESS, childProvider); + } else { + console.log("Deploy child bridge impl..."); + childBridgeImpl = await deployChildContract("ChildERC20Bridge", childDeployerWallet, null); + console.log("Transaction submitted: ", JSON.stringify(childBridgeImpl.deployTransaction, null, 2)); + await waitForReceipt(childBridgeImpl.deployTransaction.hash, childProvider); + } + childContracts.CHILD_BRIDGE_IMPL_ADDRESS = childBridgeImpl.address; + saveChildContracts(childContracts); + console.log("Deployed to CHILD_BRIDGE_IMPL_ADDRESS: ", childBridgeImpl.address); + + // Deploy child adaptor impl + let childAdaptorImpl; + if (childContracts.CHILD_ADAPTOR_IMPL_ADDRESS != "") { + console.log("Child adaptor impl has already been deployed to: " + childContracts.CHILD_ADAPTOR_IMPL_ADDRESS + ", skip."); + childAdaptorImpl = getContract("ChildAxelarBridgeAdaptor", childContracts.CHILD_ADAPTOR_IMPL_ADDRESS, childProvider); + } else { + console.log("Deploy child adaptor impl..."); + childAdaptorImpl = await deployChildContract("ChildAxelarBridgeAdaptor", childDeployerWallet, null, childGatewayAddr); + console.log("Transaction submitted: ", JSON.stringify(childAdaptorImpl.deployTransaction, null, 2)); + await waitForReceipt(childAdaptorImpl.deployTransaction.hash, childProvider); + } + childContracts.CHILD_ADAPTOR_IMPL_ADDRESS = childAdaptorImpl.address; + saveChildContracts(childContracts); + console.log("Deployed to CHILD_ADAPTOR_IMPL_ADDRESS: ", childAdaptorImpl.address); + + if (childDeployerWallet instanceof LedgerSigner) { + childDeployerWallet.close(); + } + // Get reserved wallet let reservedDeployerWallet; if (nonceReservedDeployerSecret == "ledger") { @@ -30,10 +96,6 @@ export async function deployChildContracts() { let reservedDeployerAddr = await reservedDeployerWallet.getAddress(); console.log("Reserved deployer address is: ", reservedDeployerAddr); - // Execute - console.log("Deploy child contracts in..."); - await waitForConfirmation(); - // Deploy child token template let childTokenTemplate; if (childContracts.CHILD_TOKEN_TEMPLATE != "") { @@ -69,45 +131,19 @@ export async function deployChildContracts() { } console.log("Initialised CHILD_TOKEN_TEMPLATE at: ", childTokenTemplate.address); - if (reservedDeployerWallet instanceof LedgerSigner) { - reservedDeployerWallet.close(); - } - - // Get deployer address - let childDeployerWallet; - if (deployerSecret == "ledger") { - let index = requireEnv("DEPLOYER_LEDGER_INDEX"); - const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; - childDeployerWallet = new LedgerSigner(childProvider, derivationPath); - } else { - childDeployerWallet = new ethers.Wallet(deployerSecret, childProvider); - } - let deployerAddr = await childDeployerWallet.getAddress(); - console.log("Deployer address is: ", deployerAddr); - - // Deploy wrapped IMX - let wrappedIMX; - if (childContracts.WRAPPED_IMX_ADDRESS != "") { - console.log("Wrapped IMX has already been deployed to: " + childContracts.WRAPPED_IMX_ADDRESS + ", skip."); - wrappedIMX = getContract("WIMX", childContracts.WRAPPED_IMX_ADDRESS, childProvider); - } else { - console.log("Deploy wrapped IMX..."); - wrappedIMX = await deployChildContract("WIMX", childDeployerWallet, null); - console.log("Transaction submitted: ", JSON.stringify(wrappedIMX.deployTransaction, null, 2)); - await waitForReceipt(wrappedIMX.deployTransaction.hash, childProvider); - } - childContracts.WRAPPED_IMX_ADDRESS = wrappedIMX.address; - saveChildContracts(childContracts); - console.log("Deployed to WRAPPED_IMX_ADDRESS: ", wrappedIMX.address); - // Deploy proxy admin let proxyAdmin; if (childContracts.CHILD_PROXY_ADMIN != "") { console.log("Proxy admin has already been deployed to: " + childContracts.CHILD_PROXY_ADMIN + ", skip."); proxyAdmin = getContract("ProxyAdmin", childContracts.CHILD_PROXY_ADMIN, childProvider); } else { + // Check the current nonce matches the reserved nonce + let currentNonce = await childProvider.getTransactionCount(reservedDeployerAddr); + if (nonceReserved + 2 != currentNonce) { + throw("Nonce mismatch, expected " + (nonceReserved + 2) + " actual " + currentNonce); + } console.log("Deploy proxy admin..."); - proxyAdmin = await deployChildContract("ProxyAdmin", childDeployerWallet, null); + proxyAdmin = await deployChildContract("ProxyAdmin", reservedDeployerWallet, null); console.log("Transaction submitted: ", JSON.stringify(proxyAdmin.deployTransaction, null, 2)); await waitForReceipt(proxyAdmin.deployTransaction.hash, childProvider); } @@ -115,50 +151,25 @@ export async function deployChildContracts() { saveChildContracts(childContracts); console.log("Deployed to CHILD_PROXY_ADMIN: ", proxyAdmin.address); - // Deploy child bridge impl - let childBridgeImpl; - if (childContracts.CHILD_BRIDGE_IMPL_ADDRESS != "") { - console.log("Child bridge impl has already been deployed to: " + childContracts.CHILD_BRIDGE_IMPL_ADDRESS + ", skip."); - childBridgeImpl = getContract("ChildERC20Bridge", childContracts.CHILD_BRIDGE_IMPL_ADDRESS, childProvider); - } else { - console.log("Deploy child bridge impl..."); - childBridgeImpl = await deployChildContract("ChildERC20Bridge", childDeployerWallet, null); - console.log("Transaction submitted: ", JSON.stringify(childBridgeImpl.deployTransaction, null, 2)); - await waitForReceipt(childBridgeImpl.deployTransaction.hash, childProvider); - } - childContracts.CHILD_BRIDGE_IMPL_ADDRESS = childBridgeImpl.address; - saveChildContracts(childContracts); - console.log("Deployed to CHILD_BRIDGE_IMPL_ADDRESS: ", childBridgeImpl.address); - // Deploy child bridge proxy let childBridgeProxy; if (childContracts.CHILD_BRIDGE_PROXY_ADDRESS != "") { console.log("Child bridge proxy has already been deployed to: " + childContracts.CHILD_BRIDGE_PROXY_ADDRESS + ", skip."); childBridgeProxy = getContract("TransparentUpgradeableProxy", childContracts.CHILD_BRIDGE_PROXY_ADDRESS, childProvider); } else { + // Check the current nonce matches the reserved nonce + let currentNonce = await childProvider.getTransactionCount(reservedDeployerAddr); + if (nonceReserved + 3 != currentNonce) { + throw("Nonce mismatch, expected " + (nonceReserved + 3) + " actual " + currentNonce); + } console.log("Deploy child bridge proxy..."); - childBridgeProxy = await deployChildContract("TransparentUpgradeableProxy", childDeployerWallet, null, childBridgeImpl.address, proxyAdmin.address, []); + childBridgeProxy = await deployChildContract("TransparentUpgradeableProxy", reservedDeployerWallet, null, childBridgeImpl.address, proxyAdmin.address, []); console.log("Transaction submitted: ", JSON.stringify(childBridgeProxy.deployTransaction, null, 2)); await waitForReceipt(childBridgeProxy.deployTransaction.hash, childProvider); } childContracts.CHILD_BRIDGE_PROXY_ADDRESS = childBridgeProxy.address; saveChildContracts(childContracts); console.log("Deployed to CHILD_BRIDGE_PROXY_ADDRESS: ", childBridgeProxy.address); - - // Deploy child adaptor impl - let childAdaptorImpl; - if (childContracts.CHILD_ADAPTOR_IMPL_ADDRESS != "") { - console.log("Child adaptor impl has already been deployed to: " + childContracts.CHILD_ADAPTOR_IMPL_ADDRESS + ", skip."); - childAdaptorImpl = getContract("ChildAxelarBridgeAdaptor", childContracts.CHILD_ADAPTOR_IMPL_ADDRESS, childProvider); - } else { - console.log("Deploy child adaptor impl..."); - childAdaptorImpl = await deployChildContract("ChildAxelarBridgeAdaptor", childDeployerWallet, null, childGatewayAddr); - console.log("Transaction submitted: ", JSON.stringify(childAdaptorImpl.deployTransaction, null, 2)); - await waitForReceipt(childAdaptorImpl.deployTransaction.hash, childProvider); - } - childContracts.CHILD_ADAPTOR_IMPL_ADDRESS = childAdaptorImpl.address; - saveChildContracts(childContracts); - console.log("Deployed to CHILD_ADAPTOR_IMPL_ADDRESS: ", childAdaptorImpl.address); // Deploy child adaptor proxy let childAdaptorProxy; @@ -166,8 +177,13 @@ export async function deployChildContracts() { console.log("Child adaptor proxy has already been deployed to: " + childContracts.CHILD_ADAPTOR_PROXY_ADDRESS + ", skip."); childAdaptorProxy = getContract("TransparentUpgradeableProxy", childContracts.CHILD_ADAPTOR_PROXY_ADDRESS, childProvider); } else { + // Check the current nonce matches the reserved nonce + let currentNonce = await childProvider.getTransactionCount(reservedDeployerAddr); + if (nonceReserved + 4 != currentNonce) { + throw("Nonce mismatch, expected " + (nonceReserved + 4) + " actual " + currentNonce); + } console.log("Deploy child adaptor proxy..."); - childAdaptorProxy = await deployChildContract("TransparentUpgradeableProxy", childDeployerWallet, null, childAdaptorImpl.address, proxyAdmin.address, []); + childAdaptorProxy = await deployChildContract("TransparentUpgradeableProxy", reservedDeployerWallet, null, childAdaptorImpl.address, proxyAdmin.address, []); console.log("Transaction submitted: ", JSON.stringify(childAdaptorProxy.deployTransaction, null, 2)); await waitForReceipt(childAdaptorProxy.deployTransaction.hash, childProvider); } diff --git a/scripts/deploy/root_deployment.ts b/scripts/deploy/root_deployment.ts index c0561f04..59de8e83 100644 --- a/scripts/deploy/root_deployment.ts +++ b/scripts/deploy/root_deployment.ts @@ -18,6 +18,57 @@ export async function deployRootContracts() { let rootContracts = getRootContracts(); const rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); + + // Get deployer address + let rootDeployerWallet; + if (deployerSecret == "ledger") { + let index = requireEnv("DEPLOYER_LEDGER_INDEX"); + const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; + rootDeployerWallet = new LedgerSigner(rootProvider, derivationPath); + } else { + rootDeployerWallet = new ethers.Wallet(deployerSecret, rootProvider); + } + let deployerAddr = await rootDeployerWallet.getAddress(); + console.log("Deployer address is: ", deployerAddr); + + // Execute + console.log("Deploy root contracts in..."); + await waitForConfirmation(); + + // Deploy root bridge impl + let rootBridgeImpl; + if (rootContracts.ROOT_BRIDGE_IMPL_ADDRESS != "") { + console.log("Root bridge impl has already been deployed to: " + rootContracts.ROOT_BRIDGE_IMPL_ADDRESS + ", skip."); + rootBridgeImpl = getContract("RootERC20BridgeFlowRate", rootContracts.ROOT_BRIDGE_IMPL_ADDRESS, rootProvider); + } else { + console.log("Deploy root bridge impl..."); + rootBridgeImpl = await deployRootContract("RootERC20BridgeFlowRate", rootDeployerWallet, null); + console.log("Transaction submitted: ", JSON.stringify(rootBridgeImpl.deployTransaction, null, 2)); + await waitForReceipt(rootBridgeImpl.deployTransaction.hash, rootProvider); + } + rootContracts.ROOT_BRIDGE_IMPL_ADDRESS = rootBridgeImpl.address; + saveRootContracts(rootContracts); + console.log("Deployed to ROOT_BRIDGE_IMPL_ADDRESS: ", rootBridgeImpl.address); + + // Deploy root adaptor impl + let rootAdaptorImpl; + if (rootContracts.ROOT_ADAPTOR_IMPL_ADDRESS != "") { + console.log("Root adaptor impl has already been deployed to: " + rootContracts.ROOT_ADAPTOR_IMPL_ADDRESS + ", skip."); + rootAdaptorImpl = getContract("RootAxelarBridgeAdaptor", rootContracts.ROOT_ADAPTOR_IMPL_ADDRESS, rootProvider); + } else { + console.log("Deploy root adaptor impl..."); + rootAdaptorImpl = await deployRootContract("RootAxelarBridgeAdaptor", rootDeployerWallet, null, rootGatewayAddr); + console.log("Transaction submitted: ", JSON.stringify(rootAdaptorImpl.deployTransaction, null, 2)); + await waitForReceipt(rootAdaptorImpl.deployTransaction.hash, rootProvider); + } + rootContracts.ROOT_ADAPTOR_IMPL_ADDRESS = rootAdaptorImpl.address; + saveRootContracts(rootContracts); + console.log("Deployed to ROOT_ADAPTOR_IMPL_ADDRESS: ", rootAdaptorImpl.address); + + if (rootDeployerWallet instanceof LedgerSigner) { + rootDeployerWallet.close(); + } + // Get reserved wallet let reservedDeployerWallet; if (nonceReservedDeployerSecret == "ledger") { @@ -30,10 +81,6 @@ export async function deployRootContracts() { let reservedDeployerAddr = await reservedDeployerWallet.getAddress(); console.log("Reserved deployer address is: ", reservedDeployerAddr); - // Execute - console.log("Deploy root contracts in..."); - await waitForConfirmation(); - // Deploy root token template let rootTokenTemplate; if (rootContracts.ROOT_TOKEN_TEMPLATE != "") { @@ -65,60 +112,39 @@ export async function deployRootContracts() { } console.log("Deployed to ROOT_TOKEN_TEMPLATE: ", rootTokenTemplate.address); - if (reservedDeployerWallet instanceof LedgerSigner) { - reservedDeployerWallet.close(); - } - - // Get deployer address - let rootDeployerWallet; - if (deployerSecret == "ledger") { - let index = requireEnv("DEPLOYER_LEDGER_INDEX"); - const derivationPath = `m/44'/60'/${parseInt(index)}'/0/0`; - rootDeployerWallet = new LedgerSigner(rootProvider, derivationPath); - } else { - rootDeployerWallet = new ethers.Wallet(deployerSecret, rootProvider); - } - let deployerAddr = await rootDeployerWallet.getAddress(); - console.log("Deployer address is: ", deployerAddr); - // Deploy proxy admin let proxyAdmin; if (rootContracts.ROOT_PROXY_ADMIN != "") { console.log("Proxy admin has already been deployed to: " + rootContracts.ROOT_PROXY_ADMIN + ", skip."); proxyAdmin = getContract("ProxyAdmin", rootContracts.ROOT_PROXY_ADMIN, rootProvider); } else { + // Check the current nonce matches the reserved nonce + let currentNonce = await rootProvider.getTransactionCount(reservedDeployerAddr); + if (nonceReserved + 2 != currentNonce) { + throw("Nonce mismatch, expected " + (nonceReserved + 2) + " actual " + currentNonce); + } console.log("Deploy proxy admin..."); - proxyAdmin = await deployRootContract("ProxyAdmin", rootDeployerWallet, null); + proxyAdmin = await deployRootContract("ProxyAdmin", reservedDeployerWallet, null); console.log("Transaction submitted: ", JSON.stringify(proxyAdmin.deployTransaction, null, 2)); - await waitForReceipt(proxyAdmin.deployTransaction.hash, rootProvider); + await waitForReceipt(proxyAdmin.deployTransaction.hash, rootProvider); } rootContracts.ROOT_PROXY_ADMIN = proxyAdmin.address; saveRootContracts(rootContracts); console.log("Deployed to ROOT_PROXY_ADMIN: ", proxyAdmin.address); - // Deploy root bridge impl - let rootBridgeImpl; - if (rootContracts.ROOT_BRIDGE_IMPL_ADDRESS != "") { - console.log("Root bridge impl has already been deployed to: " + rootContracts.ROOT_BRIDGE_IMPL_ADDRESS + ", skip."); - rootBridgeImpl = getContract("RootERC20BridgeFlowRate", rootContracts.ROOT_BRIDGE_IMPL_ADDRESS, rootProvider); - } else { - console.log("Deploy root bridge impl..."); - rootBridgeImpl = await deployRootContract("RootERC20BridgeFlowRate", rootDeployerWallet, null); - console.log("Transaction submitted: ", JSON.stringify(rootBridgeImpl.deployTransaction, null, 2)); - await waitForReceipt(rootBridgeImpl.deployTransaction.hash, rootProvider); - } - rootContracts.ROOT_BRIDGE_IMPL_ADDRESS = rootBridgeImpl.address; - saveRootContracts(rootContracts); - console.log("Deployed to ROOT_BRIDGE_IMPL_ADDRESS: ", rootBridgeImpl.address); - // Deploy root bridge proxy let rootBridgeProxy; if (rootContracts.ROOT_BRIDGE_PROXY_ADDRESS != "") { console.log("Root bridge proxy has already been deployed to: " + rootContracts.ROOT_BRIDGE_PROXY_ADDRESS + ", skip."); rootBridgeProxy = getContract("TransparentUpgradeableProxy", rootContracts.ROOT_BRIDGE_PROXY_ADDRESS, rootProvider); } else { + // Check the current nonce matches the reserved nonce + let currentNonce = await rootProvider.getTransactionCount(reservedDeployerAddr); + if (nonceReserved + 3 != currentNonce) { + throw("Nonce mismatch, expected " + (nonceReserved + 3) + " actual " + currentNonce); + } console.log("Deploy root bridge proxy..."); - rootBridgeProxy = await deployRootContract("TransparentUpgradeableProxy", rootDeployerWallet, null, rootBridgeImpl.address, proxyAdmin.address, []); + rootBridgeProxy = await deployRootContract("TransparentUpgradeableProxy", reservedDeployerWallet, null, rootBridgeImpl.address, proxyAdmin.address, []); console.log("Transaction submitted: ", JSON.stringify(rootBridgeProxy.deployTransaction, null, 2)); await waitForReceipt(rootBridgeProxy.deployTransaction.hash, rootProvider); } @@ -126,29 +152,19 @@ export async function deployRootContracts() { saveRootContracts(rootContracts); console.log("Deployed to ROOT_BRIDGE_PROXY_ADDRESS: ", rootBridgeProxy.address); - // Deploy root adaptor impl - let rootAdaptorImpl; - if (rootContracts.ROOT_ADAPTOR_IMPL_ADDRESS != "") { - console.log("Root adaptor impl has already been deployed to: " + rootContracts.ROOT_ADAPTOR_IMPL_ADDRESS + ", skip."); - rootAdaptorImpl = getContract("RootAxelarBridgeAdaptor", rootContracts.ROOT_ADAPTOR_IMPL_ADDRESS, rootProvider); - } else { - console.log("Deploy root adaptor impl..."); - rootAdaptorImpl = await deployRootContract("RootAxelarBridgeAdaptor", rootDeployerWallet, null, rootGatewayAddr); - console.log("Transaction submitted: ", JSON.stringify(rootAdaptorImpl.deployTransaction, null, 2)); - await waitForReceipt(rootAdaptorImpl.deployTransaction.hash, rootProvider); - } - rootContracts.ROOT_ADAPTOR_IMPL_ADDRESS = rootAdaptorImpl.address; - saveRootContracts(rootContracts); - console.log("Deployed to ROOT_ADAPTOR_IMPL_ADDRESS: ", rootAdaptorImpl.address); - // Deploy root adaptor proxy let rootAdaptorProxy; if (rootContracts.ROOT_ADAPTOR_PROXY_ADDRESS != "") { console.log("Root adaptor proxy has already been deployed to: " + rootContracts.ROOT_ADAPTOR_PROXY_ADDRESS + ", skip."); rootAdaptorProxy = getContract("TransparentUpgradeableProxy", rootContracts.ROOT_ADAPTOR_PROXY_ADDRESS, rootProvider); } else { + // Check the current nonce matches the reserved nonce + let currentNonce = await rootProvider.getTransactionCount(reservedDeployerAddr); + if (nonceReserved + 4 != currentNonce) { + throw("Nonce mismatch, expected " + (nonceReserved + 4) + " actual " + currentNonce); + } console.log("Deploy root adaptor proxy..."); - rootAdaptorProxy = await deployRootContract("TransparentUpgradeableProxy", rootDeployerWallet, null, rootAdaptorImpl.address, proxyAdmin.address, []); + rootAdaptorProxy = await deployRootContract("TransparentUpgradeableProxy", reservedDeployerWallet, null, rootAdaptorImpl.address, proxyAdmin.address, []); console.log("Transaction submitted: ", JSON.stringify(rootAdaptorProxy.deployTransaction, null, 2)); await waitForReceipt(rootAdaptorProxy.deployTransaction.hash, rootProvider); } diff --git a/scripts/localdev/.env.local b/scripts/localdev/.env.local index fd3713fe..9b4c5fbd 100644 --- a/scripts/localdev/.env.local +++ b/scripts/localdev/.env.local @@ -34,9 +34,9 @@ AXELAR_EOA=0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC ## The amount of fund Axelar requested, unit is in IMX or 10^18 Wei. AXELAR_FUND=500 ## The amount of fund deployer to be left with after bootstrapping on L2, unit is in IMX or 10^18 Wei. -CHILD_DEPLOYER_FUND=500 +CHILD_DEPLOYER_FUND=250 ## The amount of fund nonce reserved deployer required on L2, unit is in IMX or 10^18 Wei. -CHILD_NONCE_RESERVED_DEPLOYER_FUND=10 +CHILD_NONCE_RESERVED_DEPLOYER_FUND=250 ## The maximum amount of IMX that can be deposited to L2, unit is in IMX or 10^18 Wei. IMX_DEPOSIT_LIMIT=100000000 ## The privileged transaction multisig address on the root chain. From 6e04ccb310ed5c04092db67775897f0cf6cffc44 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Fri, 8 Dec 2023 11:01:49 +1000 Subject: [PATCH 10/40] Update root_initialisation.ts --- scripts/deploy/root_initialisation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/deploy/root_initialisation.ts b/scripts/deploy/root_initialisation.ts index 4051d25e..e95dd67d 100644 --- a/scripts/deploy/root_initialisation.ts +++ b/scripts/deploy/root_initialisation.ts @@ -73,7 +73,7 @@ export async function initialiseRootContracts() { { defaultAdmin: deployerAddr, pauser: rootPauser, - unpauser: rootPauser, + unpauser: rootMultisigAddr, variableManager: rootMultisigAddr, adaptorManager: rootMultisigAddr, }, From 6384295727dd7651679727702f62c7eaa5e1f2a4 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Fri, 8 Dec 2023 12:00:17 +1000 Subject: [PATCH 11/40] Fix e2e --- package.json | 4 +- scripts/bootstrap/README.md | 2 +- scripts/e2e/README.md | 2 +- scripts/e2e/e2e.ts | 103 ++++++++++++++++++------------------ scripts/localdev/README.md | 2 +- 5 files changed, 57 insertions(+), 56 deletions(-) diff --git a/package.json b/package.json index 10de3b11..fd4617d7 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,8 @@ "lint": "forge fmt", "local:start": "cd scripts/localdev; ./start.sh", "local:setup": "cd scripts/localdev; rm -rf .child.bridge.contracts.json .root.bridge.contracts.json; ./deploy.sh", - "local:test": "cd scripts/localdev; LONG_WAIT=0 SHORT_WAIT=0 npx mocha --require mocha-suppress-logs ../e2e/e2e.ts", - "local:ci": "cd scripts/localdev; rm -rf .child.bridge.contracts.json .root.bridge.contracts.json; ./ci.sh && ./deploy.sh && LONG_WAIT=0 SHORT_WAIT=0 npx mocha --require mocha-suppress-logs ../e2e/e2e.ts && ./stop.sh", + "local:test": "cd scripts/localdev; AXELAR_API_URL=skip npx mocha --require mocha-suppress-logs ../e2e/e2e.ts", + "local:ci": "cd scripts/localdev; rm -rf .child.bridge.contracts.json .root.bridge.contracts.json; ./ci.sh && ./deploy.sh && AXELAR_API_URL=skip npx mocha --require mocha-suppress-logs ../e2e/e2e.ts && ./stop.sh", "local:chainonly": "cd scripts/localdev; LOCAL_CHAIN_ONLY=true ./start.sh", "local:axelaronly": "cd scripts/localdev; npx ts-node axelar_setup.ts", "stop": "cd scripts/localdev; ./stop.sh" diff --git a/scripts/bootstrap/README.md b/scripts/bootstrap/README.md index 0c106618..6113ca4c 100644 --- a/scripts/bootstrap/README.md +++ b/scripts/bootstrap/README.md @@ -169,5 +169,5 @@ npx ts-node 9_test_preparation.ts 2>&1 | tee -a bootstrap.out ``` 15. Test bridge functions ``` -LONG_WAIT=1200000 SHORT_WAIT=300000 npx mocha --require mocha-suppress-logs ../e2e/e2e.ts 2>&1 | tee -a bootstrap.out +AXELAR_API_URL=${Axelar API URL} npx mocha --require mocha-suppress-logs ../e2e/e2e.ts 2>&1 | tee -a bootstrap.out ``` \ No newline at end of file diff --git a/scripts/e2e/README.md b/scripts/e2e/README.md index 3989fbb5..914ea035 100644 --- a/scripts/e2e/README.md +++ b/scripts/e2e/README.md @@ -52,5 +52,5 @@ TEST_ACCOUNT_SECRET= 3. Run end to end tests ``` -LONG_WAIT=1200000 SHORT_WAIT=300000 npx mocha --require mocha-suppress-logs ./e2e.ts +AXELAR_API_URL=${Axelar API URL or "skip" if run on local} npx mocha --require mocha-suppress-logs ./e2e.ts ``` \ No newline at end of file diff --git a/scripts/e2e/e2e.ts b/scripts/e2e/e2e.ts index 7adba223..d6a0ba13 100644 --- a/scripts/e2e/e2e.ts +++ b/scripts/e2e/e2e.ts @@ -21,8 +21,7 @@ describe("Bridge e2e test", () => { let childWIMX: ethers.Contract; let rootCustomToken: ethers.Contract; let childCustomToken: ethers.Contract; - let longWait: number; - let shortWait: number; + let axelarAPI: string; before(async function () { this.timeout(300000); @@ -33,17 +32,7 @@ describe("Bridge e2e test", () => { let testAccountKey = requireEnv("TEST_ACCOUNT_SECRET"); let rootIMXAddr = requireEnv("ROOT_IMX_ADDR"); let rootWETHAddr = requireEnv("ROOT_WETH_ADDR"); - - if (process.env["LONG_WAIT"] == null || process.env["LONG_WAIT"] == "") { - longWait = 1200000; - } else { - longWait = Number(process.env["LONG_WAIT"]) - } - if (process.env["SHORT_WAIT"] == null || process.env["SHORT_WAIT"] == "") { - longWait = 300000; - } else { - longWait = Number(process.env["SHORT_WAIT"]) - } + axelarAPI = requireEnv("AXELAR_API_URL"); // Read from contract file. let childContracts = getChildContracts(); @@ -88,9 +77,7 @@ describe("Bridge e2e test", () => { let postBalL1 = await rootIMX.balanceOf(rootTestWallet.address); let postBalL2 = preBalL2; - console.log("Wait " + longWait + " ms"); - await delay(longWait); - console.log("Done"); + await waitUntilSucceed(axelarAPI, resp.hash); while (postBalL2.eq(preBalL2)) { postBalL2 = await childProvider.getBalance(childTestWallet.address); @@ -102,7 +89,7 @@ describe("Bridge e2e test", () => { let expectedPostL2 = preBalL2.add(amt); expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); - }).timeout(1800000) + }).timeout(2400000) it("should successfully withdraw IMX to self from L2 to L1", async() => { // Get IMX balance on root & child chains before withdraw @@ -124,9 +111,7 @@ describe("Bridge e2e test", () => { let postBalL1 = preBalL1; let postBalL2 = await childProvider.getBalance(childTestWallet.address); - console.log("Wait " + shortWait + " ms"); - await delay(shortWait); - console.log("Done"); + await waitUntilSucceed(axelarAPI, resp.hash); while (postBalL1.eq(preBalL1)) { postBalL1 = await rootIMX.balanceOf(rootTestWallet.address); @@ -140,7 +125,7 @@ describe("Bridge e2e test", () => { let expectedPostL2 = preBalL2.sub(txFee).sub(amt).sub(bridgeFee); expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); - }).timeout(1800000) + }).timeout(2400000) it("should successfully withdraw wIMX to self from L2 to L1", async() => { // Wrap 1 IMX @@ -179,9 +164,7 @@ describe("Bridge e2e test", () => { let postBalL1 = preBalL1; let postBalL2 = await childWIMX.balanceOf(childTestWallet.address); - console.log("Wait " + shortWait + " ms"); - await delay(shortWait); - console.log("Done"); + await waitUntilSucceed(axelarAPI, resp.hash); while (postBalL1.eq(preBalL1)) { postBalL1 = await rootIMX.balanceOf(rootTestWallet.address); @@ -193,7 +176,7 @@ describe("Bridge e2e test", () => { let expectedPostL2 = preBalL2.sub(amt); expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); - }).timeout(1800000) + }).timeout(2400000) it("should successfully deposit ETH to self from L1 to L2", async() => { // Get ETH balance on root & child chains before deposit @@ -212,9 +195,7 @@ describe("Bridge e2e test", () => { let postBalL1 = await rootProvider.getBalance(rootTestWallet.address); let postBalL2 = preBalL2; - console.log("Wait " + longWait + " ms"); - await delay(longWait); - console.log("Done"); + await waitUntilSucceed(axelarAPI, resp.hash); while (postBalL2.eq(preBalL2)) { postBalL2 = await childETH.balanceOf(childTestWallet.address); @@ -223,12 +204,12 @@ describe("Bridge e2e test", () => { // Verify let receipt = await rootProvider.getTransactionReceipt(resp.hash); - let txFee = receipt.cumulativeGasUsed.mul(receipt.effectiveGasPrice); + let txFee = receipt.gasUsed.mul(receipt.effectiveGasPrice); let expectedPostL1 = preBalL1.sub(txFee).sub(amt).sub(bridgeFee); let expectedPostL2 = preBalL2.add(amt); expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); - }).timeout(1800000) + }).timeout(2400000) it("should successfully deposit wETH to self from L1 to L2", async() => { // Wrap 0.01 ETH @@ -257,9 +238,7 @@ describe("Bridge e2e test", () => { let postBalL1 = await rootWETH.balanceOf(rootTestWallet.address); let postBalL2 = preBalL2; - console.log("Wait " + longWait + " ms"); - await delay(longWait); - console.log("Done"); + await waitUntilSucceed(axelarAPI, resp.hash); while (postBalL2.eq(preBalL2)) { postBalL2 = await childETH.balanceOf(childTestWallet.address); @@ -271,7 +250,7 @@ describe("Bridge e2e test", () => { let expectedPostL2 = preBalL2.add(amt); expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); - }).timeout(1800000) + }).timeout(2400000) it("should successfully withdraw ETH to self from L2 to L1", async() => { // Get ETH balance on root & child chains before withdraw @@ -293,9 +272,7 @@ describe("Bridge e2e test", () => { let postBalL1 = preBalL1; let postBalL2 = await childETH.balanceOf(childTestWallet.address); - console.log("Wait " + shortWait + " ms"); - await delay(shortWait); - console.log("Done"); + await waitUntilSucceed(axelarAPI, resp.hash); while (postBalL1.eq(preBalL1)) { postBalL1 = await rootProvider.getBalance(rootTestWallet.address); @@ -307,7 +284,7 @@ describe("Bridge e2e test", () => { let expectedPostL2 = preBalL2.sub(amt); expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); - }).timeout(1800000) + }).timeout(2400000) it("should successfully map a ERC20 Token", async() => { let childContracts = getChildContracts(); @@ -329,9 +306,7 @@ describe("Bridge e2e test", () => { let childTokenAddr = await childBridge.rootTokenToChildToken(rootCustomToken.address); - console.log("Wait " + longWait + " ms"); - await delay(longWait); - console.log("Done"); + await waitUntilSucceed(axelarAPI, resp.hash); while (childTokenAddr == ethers.constants.AddressZero) { childTokenAddr = await childBridge.rootTokenToChildToken(rootCustomToken.address); @@ -343,7 +318,7 @@ describe("Bridge e2e test", () => { // Verify expect(childTokenAddr).to.equal(expectedChildTokenAddr); - }).timeout(1800000) + }).timeout(2400000) it("should successfully deposit mapped ERC20 Token to self from L1 to L2", async() => { // Get token balance on root & child chains before deposit @@ -366,9 +341,7 @@ describe("Bridge e2e test", () => { let postBalL1 = await rootCustomToken.balanceOf(rootTestWallet.address); let postBalL2 = preBalL2; - console.log("Wait " + longWait + " ms"); - await delay(longWait); - console.log("Done"); + await waitUntilSucceed(axelarAPI, resp.hash); while (postBalL2.eq(preBalL2)) { postBalL2 = await childCustomToken.balanceOf(childTestWallet.address); @@ -380,7 +353,7 @@ describe("Bridge e2e test", () => { let expectedPostL2 = preBalL2.add(amt); expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); - }).timeout(1800000) + }).timeout(2400000) it("should successfully withdraw mapped ERC20 Token to self from L2 to L1", async() => { // Get token balance on root & child chains before deposit @@ -402,9 +375,7 @@ describe("Bridge e2e test", () => { let postBalL1 = preBalL1; let postBalL2 = await childCustomToken.balanceOf(childTestWallet.address); - console.log("Wait " + shortWait + " ms"); - await delay(shortWait); - console.log("Done"); + await waitUntilSucceed(axelarAPI, resp.hash); while (postBalL1.eq(preBalL1)) { postBalL1 = await rootCustomToken.balanceOf(rootTestWallet.address); @@ -416,5 +387,35 @@ describe("Bridge e2e test", () => { let expectedPostL2 = preBalL2.sub(amt); expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); - }).timeout(1800000) -}) \ No newline at end of file + }).timeout(2400000) +}) + +async function waitUntilSucceed(axelarURL: string, txHash: any) { + if (axelarURL == "skip") { + return; + } + console.log("Wait until succeed... tx hash: ", txHash) + let response; + let req = '{"method": "searchGMP", "txHash": "' + txHash + '"}' + while (true) { + response = await fetch(axelarURL, { + method: 'POST', + body: req, + headers: {'Content-Type': 'application/json; charset=UTF-8'} }); + if (!response.ok) {} + if (response.body !== null) { + const asString = new TextDecoder("utf-8").decode(await response.arrayBuffer()); + const asJSON = JSON.parse(asString); + if (asJSON.data[0] == undefined) { + console.log("Waiting for " + txHash + " to become available..."); + } else { + console.log("Current status of " + txHash + ": " + asJSON.data[0].status); + if (asJSON.data[0].status == "executed") { + console.log("Done"); + return; + } + } + } + await delay(60000); + } +} \ No newline at end of file diff --git a/scripts/localdev/README.md b/scripts/localdev/README.md index 407fee87..5d47f518 100644 --- a/scripts/localdev/README.md +++ b/scripts/localdev/README.md @@ -31,5 +31,5 @@ The addresses of deployed contracts will be saved in: To run end to end tests against local development network: ``` -LONG_WAIT=0 SHORT_WAIT=0 npx mocha --require mocha-suppress-logs ../e2e/e2e.ts +AXELAR_API_URL=skip npx mocha --require mocha-suppress-logs ../e2e/e2e.ts ``` \ No newline at end of file From 56672af1fe9610970397acbc970313afe024ebd3 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Fri, 8 Dec 2023 12:11:33 +1000 Subject: [PATCH 12/40] Fix CI --- scripts/e2e/e2e.ts | 34 ++-------------------------------- scripts/helpers/helpers.ts | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/scripts/e2e/e2e.ts b/scripts/e2e/e2e.ts index d6a0ba13..4ef15db9 100644 --- a/scripts/e2e/e2e.ts +++ b/scripts/e2e/e2e.ts @@ -2,7 +2,7 @@ import * as dotenv from "dotenv"; dotenv.config(); import { ethers, providers } from "ethers"; -import { requireEnv, waitForReceipt, getFee, getContract, delay, getChildContracts, getRootContracts, saveChildContracts } from "../helpers/helpers"; +import { requireEnv, waitForReceipt, getFee, getContract, delay, getChildContracts, getRootContracts, saveChildContracts, waitUntilSucceed } from "../helpers/helpers"; import { expect } from "chai"; // The contract ABI of IMX on L1. @@ -388,34 +388,4 @@ describe("Bridge e2e test", () => { expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); }).timeout(2400000) -}) - -async function waitUntilSucceed(axelarURL: string, txHash: any) { - if (axelarURL == "skip") { - return; - } - console.log("Wait until succeed... tx hash: ", txHash) - let response; - let req = '{"method": "searchGMP", "txHash": "' + txHash + '"}' - while (true) { - response = await fetch(axelarURL, { - method: 'POST', - body: req, - headers: {'Content-Type': 'application/json; charset=UTF-8'} }); - if (!response.ok) {} - if (response.body !== null) { - const asString = new TextDecoder("utf-8").decode(await response.arrayBuffer()); - const asJSON = JSON.parse(asString); - if (asJSON.data[0] == undefined) { - console.log("Waiting for " + txHash + " to become available..."); - } else { - console.log("Current status of " + txHash + ": " + asJSON.data[0].status); - if (asJSON.data[0].status == "executed") { - console.log("Done"); - return; - } - } - } - await delay(60000); - } -} \ No newline at end of file +}) \ No newline at end of file diff --git a/scripts/helpers/helpers.ts b/scripts/helpers/helpers.ts index 10f238ba..6ad30056 100644 --- a/scripts/helpers/helpers.ts +++ b/scripts/helpers/helpers.ts @@ -154,4 +154,34 @@ export function getRootContracts() { export function saveRootContracts(contractData: any) { fs.writeFileSync(".root.bridge.contracts.json", JSON.stringify(contractData, null, 2)); +} + +export async function waitUntilSucceed(axelarURL: string, txHash: any) { + if (axelarURL == "skip") { + return; + } + console.log("Wait until succeed... tx hash: ", txHash) + let response; + let req = '{"method": "searchGMP", "txHash": "' + txHash + '"}' + while (true) { + response = await fetch(axelarURL, { + method: 'POST', + body: req, + headers: {'Content-Type': 'application/json; charset=UTF-8'} }); + if (!response.ok) {} + if (response.body !== null) { + const asString = new TextDecoder("utf-8").decode(await response.arrayBuffer()); + const asJSON = JSON.parse(asString); + if (asJSON.data[0] == undefined) { + console.log("Waiting for " + txHash + " to become available..."); + } else { + console.log("Current status of " + txHash + ": " + asJSON.data[0].status); + if (asJSON.data[0].status == "executed") { + console.log("Done"); + return; + } + } + } + await delay(60000); + } } \ No newline at end of file From e90aa439bb6b897ee31d40d82a6148265de3660f Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Fri, 8 Dec 2023 12:54:41 +1000 Subject: [PATCH 13/40] Update bootstrap --- scripts/bootstrap/.env.example | 4 ++++ scripts/bootstrap/0_pre_validation.ts | 12 +++++++++--- scripts/bootstrap/1_deployer_funding.ts | 23 +++++++++++++++++++++-- scripts/bootstrap/README.md | 4 ++++ scripts/deploy/.env.example | 4 ++++ scripts/deploy/README.md | 4 ++++ scripts/localdev/.env.local | 8 ++++++-- 7 files changed, 52 insertions(+), 7 deletions(-) diff --git a/scripts/bootstrap/.env.example b/scripts/bootstrap/.env.example index 00e96827..5c6944ed 100644 --- a/scripts/bootstrap/.env.example +++ b/scripts/bootstrap/.env.example @@ -31,12 +31,16 @@ ROOT_IMX_ADDR= ROOT_WETH_ADDR= ## The Axelar address to receive initial funding on the child chain. AXELAR_EOA= +## The passport nonce reserver +PASSPORT_NONCE_RESERVER_ADDR= ## The amount of fund Axelar requested, unit is in IMX or 10^18 Wei. AXELAR_FUND= ## The amount of fund deployer to be left with after bootstrapping on L2, unit is in IMX or 10^18 Wei. CHILD_DEPLOYER_FUND= ## The amount of fund nonce reserved deployer required on L2, unit is in IMX or 10^18 Wei. CHILD_NONCE_RESERVED_DEPLOYER_FUND= +## The amount of fund passport reserver required on L2, unit is in IMX or 10^18 Wei. +PASSPORT_NONCE_RESERVER_FUND= ## The maximum amount of IMX that can be deposited to L2, unit is in IMX or 10^18 Wei. IMX_DEPOSIT_LIMIT= ## The privileged transaction Multisig address on the root chain. diff --git a/scripts/bootstrap/0_pre_validation.ts b/scripts/bootstrap/0_pre_validation.ts index 23cd044d..7b717d6d 100644 --- a/scripts/bootstrap/0_pre_validation.ts +++ b/scripts/bootstrap/0_pre_validation.ts @@ -37,9 +37,11 @@ async function run() { let rootIMXAddr = requireEnv("ROOT_IMX_ADDR"); let rootWETHAddr = requireEnv("ROOT_WETH_ADDR"); let axelarEOA = requireEnv("AXELAR_EOA"); + let passportDeployer = requireEnv("PASSPORT_NONCE_RESERVER_ADDR"); let axelarFund = requireEnv("AXELAR_FUND"); let childDeployerFund = requireEnv("CHILD_DEPLOYER_FUND"); let childReservedDeployerFund = requireEnv("CHILD_NONCE_RESERVED_DEPLOYER_FUND"); + let passportDeployerFund = requireEnv("PASSPORT_NONCE_RESERVER_FUND"); let imxDepositLimit = requireEnv("IMX_DEPOSIT_LIMIT"); requireEnv("RATE_LIMIT_IMX_CAPACITY"); requireEnv("RATE_LIMIT_IMX_REFILL_RATE"); @@ -98,7 +100,7 @@ async function run() { } // Check duplicates - if (hasDuplicates([actualDeployerAddress, actualReservedDeployerAddress, axelarEOA])) { + if (hasDuplicates([actualDeployerAddress, actualReservedDeployerAddress, axelarEOA, passportDeployer])) { throw("Duplicate address detected!"); } if (hasDuplicates([rootIMXAddr, rootWETHAddr])) { @@ -119,14 +121,18 @@ async function run() { let axelarRequiredIMX = ethers.utils.parseEther(axelarFund); let deployerRequiredIMX = ethers.utils.parseEther(childDeployerFund); let reservedDeployerRequiredIMX = ethers.utils.parseEther(childReservedDeployerFund); + let passportRequiredIMX = ethers.utils.parseEther(passportDeployerFund); if (axelarRequiredIMX.lt(ethers.utils.parseEther("500.0"))) { tryThrow("Axelar on child chain should request at least 500 IMX, got" + ethers.utils.formatEther(axelarRequiredIMX)); } if (deployerRequiredIMX.lt(ethers.utils.parseEther("250.0"))) { tryThrow("Deployer on child chain should request at least 500 IMX, got" + ethers.utils.formatEther(deployerRequiredIMX)); } - if (reservedDeployerRequiredIMX.lt(ethers.utils.parseEther("250.0"))) { - tryThrow("Reserved deployer on child chain should request at least 10 IMX, got" + ethers.utils.formatEther(reservedDeployerRequiredIMX)); + if (reservedDeployerRequiredIMX.lt(ethers.utils.parseEther("100.0"))) { + tryThrow("Reserved deployer on child chain should request at least 100 IMX, got" + ethers.utils.formatEther(reservedDeployerRequiredIMX)); + } + if (passportRequiredIMX.lt(ethers.utils.parseEther("100.0"))) { + tryThrow("Passport deployer on child chain should request at least 100 IMX, got" + ethers.utils.formatEther(passportRequiredIMX)); } let extraIMX = ethers.utils.parseEther("100.0"); let requiredIMX = axelarRequiredIMX.add(deployerRequiredIMX).add(reservedDeployerRequiredIMX).add(extraIMX); diff --git a/scripts/bootstrap/1_deployer_funding.ts b/scripts/bootstrap/1_deployer_funding.ts index a6a9aaca..08890323 100644 --- a/scripts/bootstrap/1_deployer_funding.ts +++ b/scripts/bootstrap/1_deployer_funding.ts @@ -15,7 +15,9 @@ async function run() { let reservedDeployerAddr = requireEnv("NONCE_RESERVED_DEPLOYER_ADDR"); let reservedDeployerFund = requireEnv("CHILD_NONCE_RESERVED_DEPLOYER_FUND"); let axelarEOA = requireEnv("AXELAR_EOA"); + let passportDeployer = requireEnv("PASSPORT_NONCE_RESERVER_ADDR"); let axelarFund = requireEnv("AXELAR_FUND"); + let passportDeployerFund = requireEnv("PASSPORT_NONCE_RESERVER_FUND"); // Get deployer address const childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); @@ -31,14 +33,15 @@ async function run() { console.log("Deployer address is: ", deployerAddr); // Check duplicates - if (hasDuplicates([deployerAddr, axelarEOA, reservedDeployerAddr])) { + if (hasDuplicates([deployerAddr, axelarEOA, reservedDeployerAddr, passportDeployer])) { throw("Duplicate address detected!"); } // Execute console.log("Nonce reserved deployer now has: ", ethers.utils.formatEther(await childProvider.getBalance(reservedDeployerAddr))); console.log("Axelar EOA now has: ", ethers.utils.formatEther(await childProvider.getBalance(axelarEOA))); - console.log("Fund Axelar and deployer on child chain in..."); + console.log("Passport deployer now has: ", ethers.utils.formatEther(await childProvider.getBalance(passportDeployer))); + console.log("Fund Axelar, deployers on child chain in..."); await waitForConfirmation(); if ((await childProvider.getBalance(reservedDeployerAddr)).gte(ethers.utils.parseEther(reservedDeployerFund))) { @@ -71,9 +74,25 @@ async function run() { await waitForReceipt(resp.hash, childProvider); } + if ((await childProvider.getBalance(passportDeployer)).gte(ethers.utils.parseEther(passportDeployerFund))) { + console.log("Passport deployer has already got requested amount, skip."); + } else { + let [priorityFee, maxFee] = await getFee(childProvider); + console.log("Transfer value to Passport deployer..."); + let resp = await childDeployerWallet.sendTransaction({ + to: passportDeployer, + value: ethers.utils.parseEther(passportDeployerFund), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }) + console.log("Transaction submitted: " + JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, childProvider); + } + // Print target balance console.log("Nonce reserved deployer now has: ", ethers.utils.formatEther(await childProvider.getBalance(reservedDeployerAddr))); console.log("Axelar EOA now has: ", ethers.utils.formatEther(await childProvider.getBalance(axelarEOA))); + console.log("Passport deployer now has: ", ethers.utils.formatEther(await childProvider.getBalance(passportDeployer))); console.log("=======End Deployer Funding======="); } diff --git a/scripts/bootstrap/README.md b/scripts/bootstrap/README.md index 6113ca4c..40f68e72 100644 --- a/scripts/bootstrap/README.md +++ b/scripts/bootstrap/README.md @@ -55,12 +55,16 @@ ROOT_IMX_ADDR= ROOT_WETH_ADDR= ## The Axelar address to receive initial funding on the child chain. AXELAR_EOA= +## The passport nonce reserver +PASSPORT_NONCE_RESERVER_ADDR= ## The amount of fund Axelar requested, unit is in IMX or 10^18 Wei. AXELAR_FUND= ## The amount of fund deployer to be left with after bootstrapping on L2, unit is in IMX or 10^18 Wei. CHILD_DEPLOYER_FUND= ## The amount of fund nonce reserved deployer required on L2, unit is in IMX or 10^18 Wei. CHILD_NONCE_RESERVED_DEPLOYER_FUND= +## The amount of fund passport reserver required on L2, unit is in IMX or 10^18 Wei. +PASSPORT_NONCE_RESERVER_FUND= ## The maximum amount of IMX that can be deposited to L2, unit is in IMX or 10^18 Wei. IMX_DEPOSIT_LIMIT= ## The privileged transaction Multisig address on the root chain. diff --git a/scripts/deploy/.env.example b/scripts/deploy/.env.example index 2fa71b06..d58b72f7 100644 --- a/scripts/deploy/.env.example +++ b/scripts/deploy/.env.example @@ -30,12 +30,16 @@ ROOT_IMX_ADDR= ROOT_WETH_ADDR= ## The Axelar address to receive initial funding on the child chain. AXELAR_EOA= +## The passport nonce reserver +PASSPORT_NONCE_RESERVER_ADDR= ## The amount of fund Axelar requested, unit is in IMX or 10^18 Wei. AXELAR_FUND= ## The amount of fund deployer to be left with after bootstrapping on L2, unit is in IMX or 10^18 Wei. CHILD_DEPLOYER_FUND= ## The amount of fund nonce reserved deployer required on L2, unit is in IMX or 10^18 Wei. CHILD_NONCE_RESERVED_DEPLOYER_FUND= +## The amount of fund passport reserver required on L2, unit is in IMX or 10^18 Wei. +PASSPORT_NONCE_RESERVER_FUND= ## The maximum amount of IMX that can be deposited to L2, unit is in IMX or 10^18 Wei. IMX_DEPOSIT_LIMIT= ## The privileged transaction Multisig address on the root chain. diff --git a/scripts/deploy/README.md b/scripts/deploy/README.md index 3c38f3f6..75789ae3 100644 --- a/scripts/deploy/README.md +++ b/scripts/deploy/README.md @@ -44,12 +44,16 @@ ROOT_IMX_ADDR= ROOT_WETH_ADDR= ## The Axelar address to receive initial funding on the child chain. AXELAR_EOA= +## The passport nonce reserver +PASSPORT_NONCE_RESERVER_ADDR= ## The amount of fund Axelar requested, unit is in IMX or 10^18 Wei. AXELAR_FUND= ## The amount of fund deployer to be left with after bootstrapping on L2, unit is in IMX or 10^18 Wei. CHILD_DEPLOYER_FUND= ## The amount of fund nonce reserved deployer required on L2, unit is in IMX or 10^18 Wei. CHILD_NONCE_RESERVED_DEPLOYER_FUND= +## The amount of fund passport reserver required on L2, unit is in IMX or 10^18 Wei. +PASSPORT_NONCE_RESERVER_FUND= ## The maximum amount of IMX that can be deposited to L2, unit is in IMX or 10^18 Wei. IMX_DEPOSIT_LIMIT= ## The privileged transaction Multisig address on the root chain. diff --git a/scripts/localdev/.env.local b/scripts/localdev/.env.local index 9b4c5fbd..da35f139 100644 --- a/scripts/localdev/.env.local +++ b/scripts/localdev/.env.local @@ -31,12 +31,16 @@ ROOT_IMX_ADDR=0x73511669fd4dE447feD18BB79bAFeAC93aB7F31f ROOT_WETH_ADDR=0xB581C9264f59BF0289fA76D61B2D0746dCE3C30D ## The Axelar address to receive initial funding on the child chain. AXELAR_EOA=0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC +## The passport nonce reserver +PASSPORT_NONCE_RESERVER_ADDR=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 ## The amount of fund Axelar requested, unit is in IMX or 10^18 Wei. AXELAR_FUND=500 ## The amount of fund deployer to be left with after bootstrapping on L2, unit is in IMX or 10^18 Wei. -CHILD_DEPLOYER_FUND=250 +CHILD_DEPLOYER_FUND=200 ## The amount of fund nonce reserved deployer required on L2, unit is in IMX or 10^18 Wei. -CHILD_NONCE_RESERVED_DEPLOYER_FUND=250 +CHILD_NONCE_RESERVED_DEPLOYER_FUND=200 +## The amount of fund passport reserver required on L2, unit is in IMX or 10^18 Wei. +PASSPORT_NONCE_RESERVER_FUND=100 ## The maximum amount of IMX that can be deposited to L2, unit is in IMX or 10^18 Wei. IMX_DEPOSIT_LIMIT=100000000 ## The privileged transaction multisig address on the root chain. From f9f1c8a71cf1d037445f404f4643411b58820d00 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Wed, 13 Dec 2023 08:28:35 +0800 Subject: [PATCH 14/40] Fix CI --- scripts/deploy/child_deployment.ts | 4 ++-- scripts/deploy/root_deployment.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/deploy/child_deployment.ts b/scripts/deploy/child_deployment.ts index ff17a8b6..a201f44e 100644 --- a/scripts/deploy/child_deployment.ts +++ b/scripts/deploy/child_deployment.ts @@ -57,7 +57,7 @@ export async function deployChildContracts() { childBridgeImpl = getContract("ChildERC20Bridge", childContracts.CHILD_BRIDGE_IMPL_ADDRESS, childProvider); } else { console.log("Deploy child bridge impl..."); - childBridgeImpl = await deployChildContract("ChildERC20Bridge", childDeployerWallet, null); + childBridgeImpl = await deployChildContract("ChildERC20Bridge", childDeployerWallet, null, deployerAddr); console.log("Transaction submitted: ", JSON.stringify(childBridgeImpl.deployTransaction, null, 2)); await waitForReceipt(childBridgeImpl.deployTransaction.hash, childProvider); } @@ -72,7 +72,7 @@ export async function deployChildContracts() { childAdaptorImpl = getContract("ChildAxelarBridgeAdaptor", childContracts.CHILD_ADAPTOR_IMPL_ADDRESS, childProvider); } else { console.log("Deploy child adaptor impl..."); - childAdaptorImpl = await deployChildContract("ChildAxelarBridgeAdaptor", childDeployerWallet, null, childGatewayAddr); + childAdaptorImpl = await deployChildContract("ChildAxelarBridgeAdaptor", childDeployerWallet, null, childGatewayAddr, deployerAddr); console.log("Transaction submitted: ", JSON.stringify(childAdaptorImpl.deployTransaction, null, 2)); await waitForReceipt(childAdaptorImpl.deployTransaction.hash, childProvider); } diff --git a/scripts/deploy/root_deployment.ts b/scripts/deploy/root_deployment.ts index 59de8e83..9a56f0cc 100644 --- a/scripts/deploy/root_deployment.ts +++ b/scripts/deploy/root_deployment.ts @@ -42,7 +42,7 @@ export async function deployRootContracts() { rootBridgeImpl = getContract("RootERC20BridgeFlowRate", rootContracts.ROOT_BRIDGE_IMPL_ADDRESS, rootProvider); } else { console.log("Deploy root bridge impl..."); - rootBridgeImpl = await deployRootContract("RootERC20BridgeFlowRate", rootDeployerWallet, null); + rootBridgeImpl = await deployRootContract("RootERC20BridgeFlowRate", rootDeployerWallet, null, deployerAddr); console.log("Transaction submitted: ", JSON.stringify(rootBridgeImpl.deployTransaction, null, 2)); await waitForReceipt(rootBridgeImpl.deployTransaction.hash, rootProvider); } @@ -57,7 +57,7 @@ export async function deployRootContracts() { rootAdaptorImpl = getContract("RootAxelarBridgeAdaptor", rootContracts.ROOT_ADAPTOR_IMPL_ADDRESS, rootProvider); } else { console.log("Deploy root adaptor impl..."); - rootAdaptorImpl = await deployRootContract("RootAxelarBridgeAdaptor", rootDeployerWallet, null, rootGatewayAddr); + rootAdaptorImpl = await deployRootContract("RootAxelarBridgeAdaptor", rootDeployerWallet, null, rootGatewayAddr, deployerAddr); console.log("Transaction submitted: ", JSON.stringify(rootAdaptorImpl.deployTransaction, null, 2)); await waitForReceipt(rootAdaptorImpl.deployTransaction.hash, rootProvider); } From e5e48a87fd9309f391ebdbe3d7e7062aa20af373 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Wed, 13 Dec 2023 14:56:29 +0800 Subject: [PATCH 15/40] Add role on L2 --- scripts/bootstrap/.env.example | 10 ++++++--- scripts/bootstrap/9_test_preparation.ts | 6 +++--- scripts/bootstrap/README.md | 10 ++++++--- scripts/deploy/.env.example | 10 ++++++--- scripts/deploy/README.md | 10 ++++++--- scripts/deploy/child_initialisation.ts | 18 +++++++++------- scripts/deploy/root_initialisation.ts | 28 ++++++++++++------------- scripts/localdev/.env.local | 10 ++++++--- 8 files changed, 62 insertions(+), 40 deletions(-) diff --git a/scripts/bootstrap/.env.example b/scripts/bootstrap/.env.example index 5c6944ed..2afff782 100644 --- a/scripts/bootstrap/.env.example +++ b/scripts/bootstrap/.env.example @@ -44,9 +44,13 @@ PASSPORT_NONCE_RESERVER_FUND= ## The maximum amount of IMX that can be deposited to L2, unit is in IMX or 10^18 Wei. IMX_DEPOSIT_LIMIT= ## The privileged transaction Multisig address on the root chain. -PRIVILEGED_ROOT_MULTISIG_ADDR= -# The pauser address on the root chain. -ROOT_PAUSER_ADDR= +ROOT_PRIVILEGED_MULTISIG_ADDR= +# The break glass signer address on the root chain. +ROOT_BREAKGLASS_ADDR= +## The privileged transaction Multisig address on the child chain. +CHILD_PRIVILEGED_MULTISIG_ADDR= +# The break glass signer address on the child chain. +CHILD_BREAKGLASS_ADDR= ## The capacity of the rate limit policy of IMX token, unit is in 10^18. RATE_LIMIT_IMX_CAPACITY= ## The refill rate of the rate limit policy of IMX token, unit is in 10^18. diff --git a/scripts/bootstrap/9_test_preparation.ts b/scripts/bootstrap/9_test_preparation.ts index d0c3a72a..01ed75c2 100644 --- a/scripts/bootstrap/9_test_preparation.ts +++ b/scripts/bootstrap/9_test_preparation.ts @@ -13,7 +13,7 @@ async function run() { let rootChainID = requireEnv("ROOT_CHAIN_ID"); let deployerSecret = requireEnv("DEPLOYER_SECRET"); let testAccountKey = requireEnv("TEST_ACCOUNT_SECRET"); - let rootMultisigAddr = requireEnv("PRIVILEGED_ROOT_MULTISIG_ADDR"); + let rootPrivilegedMultisig = requireEnv("ROOT_PRIVILEGED_MULTISIG_ADDR"); // Get deployer address const rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); @@ -79,9 +79,9 @@ async function run() { await waitForReceipt(resp.hash, rootProvider); // Print summary - console.log("Does multisig have DEFAULT_ADMIN: ", await rootBridge.hasRole(await rootBridge.DEFAULT_ADMIN_ROLE(), rootMultisigAddr)); + console.log("Does multisig have DEFAULT_ADMIN: ", await rootBridge.hasRole(await rootBridge.DEFAULT_ADMIN_ROLE(), rootPrivilegedMultisig)); console.log("Does deployer have DEFAULT_ADMIN: ", await rootBridge.hasRole(await rootBridge.DEFAULT_ADMIN_ROLE(), deployerAddr)); - console.log("Does multisig have RATE_ADMIN: ", await rootBridge.hasRole(utils.keccak256(utils.toUtf8Bytes("RATE")), rootMultisigAddr)); + console.log("Does multisig have RATE_ADMIN: ", await rootBridge.hasRole(utils.keccak256(utils.toUtf8Bytes("RATE")), rootPrivilegedMultisig)); console.log("Does deployer have RATE_ADMIN: ", await rootBridge.hasRole(utils.keccak256(utils.toUtf8Bytes("RATE")), deployerAddr)); console.log("=======End Test Preparation======="); diff --git a/scripts/bootstrap/README.md b/scripts/bootstrap/README.md index 40f68e72..078950e7 100644 --- a/scripts/bootstrap/README.md +++ b/scripts/bootstrap/README.md @@ -68,9 +68,13 @@ PASSPORT_NONCE_RESERVER_FUND= ## The maximum amount of IMX that can be deposited to L2, unit is in IMX or 10^18 Wei. IMX_DEPOSIT_LIMIT= ## The privileged transaction Multisig address on the root chain. -PRIVILEGED_ROOT_MULTISIG_ADDR= -# The pauser address on the root chain. -ROOT_PAUSER_ADDR= +ROOT_PRIVILEGED_MULTISIG_ADDR= +# The break glass signer address on the root chain. +ROOT_BREAKGLASS_ADDR= +## The privileged transaction Multisig address on the child chain. +CHILD_PRIVILEGED_MULTISIG_ADDR= +# The break glass signer address on the child chain. +CHILD_BREAKGLASS_ADDR= ## The capacity of the rate limit policy of IMX token, unit is in 10^18. RATE_LIMIT_IMX_CAPACITY= ## The refill rate of the rate limit policy of IMX token, unit is in 10^18. diff --git a/scripts/deploy/.env.example b/scripts/deploy/.env.example index d58b72f7..3e5f83a2 100644 --- a/scripts/deploy/.env.example +++ b/scripts/deploy/.env.example @@ -43,9 +43,13 @@ PASSPORT_NONCE_RESERVER_FUND= ## The maximum amount of IMX that can be deposited to L2, unit is in IMX or 10^18 Wei. IMX_DEPOSIT_LIMIT= ## The privileged transaction Multisig address on the root chain. -PRIVILEGED_ROOT_MULTISIG_ADDR= -# The pauser address on the root chain. -ROOT_PAUSER_ADDR= +ROOT_PRIVILEGED_MULTISIG_ADDR= +# The break glass signer address on the root chain. +ROOT_BREAKGLASS_ADDR= +## The privileged transaction Multisig address on the child chain. +CHILD_PRIVILEGED_MULTISIG_ADDR= +# The break glass signer address on the child chain. +CHILD_BREAKGLASS_ADDR= ## The capacity of the rate limit policy of IMX token, unit is in 10^18. RATE_LIMIT_IMX_CAPACITY= ## The refill rate of the rate limit policy of IMX token, unit is in 10^18. diff --git a/scripts/deploy/README.md b/scripts/deploy/README.md index 75789ae3..6884186f 100644 --- a/scripts/deploy/README.md +++ b/scripts/deploy/README.md @@ -57,9 +57,13 @@ PASSPORT_NONCE_RESERVER_FUND= ## The maximum amount of IMX that can be deposited to L2, unit is in IMX or 10^18 Wei. IMX_DEPOSIT_LIMIT= ## The privileged transaction Multisig address on the root chain. -PRIVILEGED_ROOT_MULTISIG_ADDR= -# The pauser address on the root chain. -ROOT_PAUSER_ADDR= +ROOT_PRIVILEGED_MULTISIG_ADDR= +# The break glass signer address on the root chain. +ROOT_BREAKGLASS_ADDR= +## The privileged transaction Multisig address on the child chain. +CHILD_PRIVILEGED_MULTISIG_ADDR= +# The break glass signer address on the child chain. +CHILD_BREAKGLASS_ADDR= ## The capacity of the rate limit policy of IMX token, unit is in 10^18. RATE_LIMIT_IMX_CAPACITY= ## The refill rate of the rate limit policy of IMX token, unit is in 10^18. diff --git a/scripts/deploy/child_initialisation.ts b/scripts/deploy/child_initialisation.ts index c7d8cc0d..8be95c1a 100644 --- a/scripts/deploy/child_initialisation.ts +++ b/scripts/deploy/child_initialisation.ts @@ -14,6 +14,8 @@ export async function initialiseChildContracts() { let childGasServiceAddr = requireEnv("CHILD_GAS_SERVICE_ADDRESS"); let multisigAddr = requireEnv("MULTISIG_CONTRACT_ADDRESS"); let rootIMXAddr = requireEnv("ROOT_IMX_ADDR"); + let childPrivilegedMultisig = requireEnv("CHILD_PRIVILEGED_MULTISIG_ADDR"); + let childBreakglass = requireEnv("CHILD_BREAKGLASS_ADDR"); // Read from contract file. let childContracts = getChildContracts(); @@ -53,10 +55,10 @@ export async function initialiseChildContracts() { let [priorityFee, maxFee] = await getFee(childProvider); let resp = await childBridge.connect(childDeployerWallet).initialize( { - defaultAdmin: deployerAddr, - pauser: deployerAddr, - unpauser: deployerAddr, - adaptorManager: deployerAddr, + defaultAdmin: childPrivilegedMultisig, + pauser: childBreakglass, + unpauser: childPrivilegedMultisig, + adaptorManager: childPrivilegedMultisig, initialDepositor: deployerAddr, treasuryManager: multisigAddr, }, @@ -77,10 +79,10 @@ export async function initialiseChildContracts() { [priorityFee, maxFee] = await getFee(childProvider); resp = await childAdaptor.connect(childDeployerWallet).initialize( { - defaultAdmin: deployerAddr, - bridgeManager: deployerAddr, - gasServiceManager: deployerAddr, - targetManager: deployerAddr, + defaultAdmin: childPrivilegedMultisig, + bridgeManager: childPrivilegedMultisig, + gasServiceManager: childPrivilegedMultisig, + targetManager: childPrivilegedMultisig, }, childBridgeAddr, rootChainName, diff --git a/scripts/deploy/root_initialisation.ts b/scripts/deploy/root_initialisation.ts index e95dd67d..4b542e8f 100644 --- a/scripts/deploy/root_initialisation.ts +++ b/scripts/deploy/root_initialisation.ts @@ -15,8 +15,8 @@ export async function initialiseRootContracts() { let rootIMXAddr = requireEnv("ROOT_IMX_ADDR"); let rootWETHAddr = requireEnv("ROOT_WETH_ADDR"); let imxDepositLimit = requireEnv("IMX_DEPOSIT_LIMIT"); - let rootMultisigAddr = requireEnv("PRIVILEGED_ROOT_MULTISIG_ADDR"); - let rootPauser = requireEnv("ROOT_PAUSER_ADDR"); + let rootPrivilegedMultisig = requireEnv("ROOT_PRIVILEGED_MULTISIG_ADDR"); + let rootBreakglass = requireEnv("ROOT_BREAKGLASS_ADDR"); let rateLimitIMXCap = requireEnv("RATE_LIMIT_IMX_CAPACITY"); let rateLimitIMXRefill = requireEnv("RATE_LIMIT_IMX_REFILL_RATE"); let rateLimitIMXLargeThreshold = requireEnv("RATE_LIMIT_IMX_LARGE_THRESHOLD"); @@ -72,10 +72,10 @@ export async function initialiseRootContracts() { let resp = await rootBridge.connect(rootDeployerWallet)["initialize((address,address,address,address,address),address,address,address,address,address,uint256,address)"]( { defaultAdmin: deployerAddr, - pauser: rootPauser, - unpauser: rootMultisigAddr, - variableManager: rootMultisigAddr, - adaptorManager: rootMultisigAddr, + pauser: rootBreakglass, + unpauser: rootPrivilegedMultisig, + variableManager: rootPrivilegedMultisig, + adaptorManager: rootPrivilegedMultisig, }, rootAdaptorAddr, childBridgeAddr, @@ -156,19 +156,19 @@ export async function initialiseRootContracts() { // Grant roles console.log("Grant RATE_CONTROL_ROLE to multisig...") - resp = await rootBridge.connect(rootDeployerWallet).grantRole(utils.keccak256(utils.toUtf8Bytes("RATE")), rootMultisigAddr); + resp = await rootBridge.connect(rootDeployerWallet).grantRole(utils.keccak256(utils.toUtf8Bytes("RATE")), rootPrivilegedMultisig); console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); await waitForReceipt(resp.hash, rootProvider); console.log("Grant DEFAULT_ADMIN to multisig...") - resp = await rootBridge.connect(rootDeployerWallet).grantRole(await rootBridge.DEFAULT_ADMIN_ROLE(), rootMultisigAddr); + resp = await rootBridge.connect(rootDeployerWallet).grantRole(await rootBridge.DEFAULT_ADMIN_ROLE(), rootPrivilegedMultisig); console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); await waitForReceipt(resp.hash, rootProvider); // Print summary - console.log("Does multisig have DEFAULT_ADMIN: ", await rootBridge.hasRole(await rootBridge.DEFAULT_ADMIN_ROLE(), rootMultisigAddr)); + console.log("Does multisig have DEFAULT_ADMIN: ", await rootBridge.hasRole(await rootBridge.DEFAULT_ADMIN_ROLE(), rootPrivilegedMultisig)); console.log("Does deployer have DEFAULT_ADMIN: ", await rootBridge.hasRole(await rootBridge.DEFAULT_ADMIN_ROLE(), deployerAddr)); - console.log("Does multisig have RATE_ADMIN: ", await rootBridge.hasRole(utils.keccak256(utils.toUtf8Bytes("RATE")), rootMultisigAddr)); + console.log("Does multisig have RATE_ADMIN: ", await rootBridge.hasRole(utils.keccak256(utils.toUtf8Bytes("RATE")), rootPrivilegedMultisig)); console.log("Does deployer have RATE_ADMIN: ", await rootBridge.hasRole(utils.keccak256(utils.toUtf8Bytes("RATE")), deployerAddr)); // Initialise root adaptor @@ -176,10 +176,10 @@ export async function initialiseRootContracts() { let rootAdaptor = getContract("RootAxelarBridgeAdaptor", rootAdaptorAddr, rootProvider); resp = await rootAdaptor.connect(rootDeployerWallet).initialize( { - defaultAdmin: rootMultisigAddr, - bridgeManager: rootMultisigAddr, - gasServiceManager: rootMultisigAddr, - targetManager: rootMultisigAddr, + defaultAdmin: rootPrivilegedMultisig, + bridgeManager: rootPrivilegedMultisig, + gasServiceManager: rootPrivilegedMultisig, + targetManager: rootPrivilegedMultisig, }, rootBridgeAddr, childChainName, diff --git a/scripts/localdev/.env.local b/scripts/localdev/.env.local index da35f139..8633ddc7 100644 --- a/scripts/localdev/.env.local +++ b/scripts/localdev/.env.local @@ -44,9 +44,13 @@ PASSPORT_NONCE_RESERVER_FUND=100 ## The maximum amount of IMX that can be deposited to L2, unit is in IMX or 10^18 Wei. IMX_DEPOSIT_LIMIT=100000000 ## The privileged transaction multisig address on the root chain. -PRIVILEGED_ROOT_MULTISIG_ADDR=0x14dC79964da2C08b23698B3D3cc7Ca32193d9955 -# The pauser address on the root chain. -ROOT_PAUSER_ADDR=0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f +ROOT_PRIVILEGED_MULTISIG_ADDR=0x14dC79964da2C08b23698B3D3cc7Ca32193d9955 +# The break glass signer address on the root chain. +ROOT_BREAKGLASS_ADDR=0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f +## The privileged transaction Multisig address on the child chain. +CHILD_PRIVILEGED_MULTISIG_ADDR=0x14dC79964da2C08b23698B3D3cc7Ca32193d9955 +# The break glass signer address on the child chain. +CHILD_BREAKGLASS_ADDR=0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f ## The capacity of the rate limit policy of IMX token, unit is in 10^18. RATE_LIMIT_IMX_CAPACITY=15516 ## The refill rate of the rate limit policy of IMX token, unit is in 10^18. From 78ac63d12fa9b5d0e531e0020cdcf9d353798736 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Wed, 13 Dec 2023 22:25:16 +0800 Subject: [PATCH 16/40] Update e2e.yml --- .github/workflows/e2e.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 2f261b0e..17a3cdde 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -13,10 +13,10 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set Node.js 18.x + - name: Set Node.js 18.18.x uses: actions/setup-node@v3 with: - node-version: 18.x + node-version: 18.18.x - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 From b58a234ccf75e59433b8f5d081628ec1f390451c Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Thu, 14 Dec 2023 07:33:59 +0800 Subject: [PATCH 17/40] Add verification of contracts --- scripts/bootstrap/.env.example | 4 ++++ scripts/bootstrap/README.md | 4 ++++ scripts/bootstrap/verify.txt | 1 + scripts/deploy/child_deployment.ts | 9 ++++++++- scripts/deploy/root_deployment.ts | 10 ++++++++-- scripts/helpers/helpers.ts | 30 ++++++++++++++++++++++++++++++ 6 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 scripts/bootstrap/verify.txt diff --git a/scripts/bootstrap/.env.example b/scripts/bootstrap/.env.example index 2afff782..a8ed55a2 100644 --- a/scripts/bootstrap/.env.example +++ b/scripts/bootstrap/.env.example @@ -102,6 +102,10 @@ CHILD_GAS_SERVICE_ADDRESS= MULTISIG_CONTRACT_ADDRESS= ROOT_GATEWAY_ADDRESS= ROOT_GAS_SERVICE_ADDRESS= +## (Optional) to verify child contract after deployment +CHILD_CHAIN_BLOCKSCOUT_API_URL= +## (Optional) to verify root contract after deployment +ROOT_CHAIN_ETHERSCAN_API_KEY= # Set prior to bridge testing TEST_ACCOUNT_SECRET= \ No newline at end of file diff --git a/scripts/bootstrap/README.md b/scripts/bootstrap/README.md index 078950e7..e510bb20 100644 --- a/scripts/bootstrap/README.md +++ b/scripts/bootstrap/README.md @@ -136,6 +136,10 @@ CHILD_GAS_SERVICE_ADDRESS= MULTISIG_CONTRACT_ADDRESS= ROOT_GATEWAY_ADDRESS= ROOT_GAS_SERVICE_ADDRESS= +## (Optional) to verify child contract after deployment +CHILD_CHAIN_BLOCKSCOUT_API_URL= +## (Optional) to verify root contract after deployment +ROOT_CHAIN_ETHERSCAN_API_KEY= ``` 7. Basic contract validation If multisig is deployed: diff --git a/scripts/bootstrap/verify.txt b/scripts/bootstrap/verify.txt new file mode 100644 index 00000000..902153db --- /dev/null +++ b/scripts/bootstrap/verify.txt @@ -0,0 +1 @@ +forge verify-contract --verifier blockscout --verifier-url https://explorer.testnet.immutable.com/api
SomeContract \ No newline at end of file diff --git a/scripts/deploy/child_deployment.ts b/scripts/deploy/child_deployment.ts index a201f44e..ce8201f1 100644 --- a/scripts/deploy/child_deployment.ts +++ b/scripts/deploy/child_deployment.ts @@ -2,7 +2,7 @@ import * as dotenv from "dotenv"; dotenv.config(); import { ethers } from "ethers"; -import { requireEnv, waitForConfirmation, deployChildContract, waitForReceipt, getFee, getChildContracts, getContract, saveChildContracts } from "../helpers/helpers"; +import { requireEnv, waitForConfirmation, deployChildContract, waitForReceipt, getFee, getChildContracts, getContract, saveChildContracts, verifyChildContract } from "../helpers/helpers"; import { LedgerSigner } from "../helpers/ledger_signer"; export async function deployChildContracts() { @@ -45,6 +45,7 @@ export async function deployChildContracts() { wrappedIMX = await deployChildContract("WIMX", childDeployerWallet, null); console.log("Transaction submitted: ", JSON.stringify(wrappedIMX.deployTransaction, null, 2)); await waitForReceipt(wrappedIMX.deployTransaction.hash, childProvider); + verifyChildContract("WIMX", wrappedIMX.address); } childContracts.WRAPPED_IMX_ADDRESS = wrappedIMX.address; saveChildContracts(childContracts); @@ -60,6 +61,7 @@ export async function deployChildContracts() { childBridgeImpl = await deployChildContract("ChildERC20Bridge", childDeployerWallet, null, deployerAddr); console.log("Transaction submitted: ", JSON.stringify(childBridgeImpl.deployTransaction, null, 2)); await waitForReceipt(childBridgeImpl.deployTransaction.hash, childProvider); + verifyChildContract("ChildERC20Bridge", childBridgeImpl.address); } childContracts.CHILD_BRIDGE_IMPL_ADDRESS = childBridgeImpl.address; saveChildContracts(childContracts); @@ -75,6 +77,7 @@ export async function deployChildContracts() { childAdaptorImpl = await deployChildContract("ChildAxelarBridgeAdaptor", childDeployerWallet, null, childGatewayAddr, deployerAddr); console.log("Transaction submitted: ", JSON.stringify(childAdaptorImpl.deployTransaction, null, 2)); await waitForReceipt(childAdaptorImpl.deployTransaction.hash, childProvider); + verifyChildContract("ChildAxelarBridgeAdaptor", childAdaptorImpl.address); } childContracts.CHILD_ADAPTOR_IMPL_ADDRESS = childAdaptorImpl.address; saveChildContracts(childContracts); @@ -111,6 +114,7 @@ export async function deployChildContracts() { childTokenTemplate = await deployChildContract("ChildERC20", reservedDeployerWallet, nonceReserved); console.log("Transaction submitted: ", JSON.stringify(childTokenTemplate.deployTransaction, null, 2)); await waitForReceipt(childTokenTemplate.deployTransaction.hash, childProvider); + verifyChildContract("ChildERC20", childTokenTemplate.address); } childContracts.CHILD_TOKEN_TEMPLATE = childTokenTemplate.address; saveChildContracts(childContracts); @@ -146,6 +150,7 @@ export async function deployChildContracts() { proxyAdmin = await deployChildContract("ProxyAdmin", reservedDeployerWallet, null); console.log("Transaction submitted: ", JSON.stringify(proxyAdmin.deployTransaction, null, 2)); await waitForReceipt(proxyAdmin.deployTransaction.hash, childProvider); + verifyChildContract("ProxyAdmin", proxyAdmin.address); } childContracts.CHILD_PROXY_ADMIN = proxyAdmin.address; saveChildContracts(childContracts); @@ -166,6 +171,7 @@ export async function deployChildContracts() { childBridgeProxy = await deployChildContract("TransparentUpgradeableProxy", reservedDeployerWallet, null, childBridgeImpl.address, proxyAdmin.address, []); console.log("Transaction submitted: ", JSON.stringify(childBridgeProxy.deployTransaction, null, 2)); await waitForReceipt(childBridgeProxy.deployTransaction.hash, childProvider); + verifyChildContract("TransparentUpgradeableProxy", childBridgeProxy.address); } childContracts.CHILD_BRIDGE_PROXY_ADDRESS = childBridgeProxy.address; saveChildContracts(childContracts); @@ -186,6 +192,7 @@ export async function deployChildContracts() { childAdaptorProxy = await deployChildContract("TransparentUpgradeableProxy", reservedDeployerWallet, null, childAdaptorImpl.address, proxyAdmin.address, []); console.log("Transaction submitted: ", JSON.stringify(childAdaptorProxy.deployTransaction, null, 2)); await waitForReceipt(childAdaptorProxy.deployTransaction.hash, childProvider); + verifyChildContract("TransparentUpgradeableProxy", childAdaptorProxy.address); } childContracts.CHILD_ADAPTOR_PROXY_ADDRESS = childAdaptorProxy.address; saveChildContracts(childContracts); diff --git a/scripts/deploy/root_deployment.ts b/scripts/deploy/root_deployment.ts index 9a56f0cc..da7b01ef 100644 --- a/scripts/deploy/root_deployment.ts +++ b/scripts/deploy/root_deployment.ts @@ -2,7 +2,7 @@ import * as dotenv from "dotenv"; dotenv.config(); import { ethers } from "ethers"; -import { requireEnv, waitForConfirmation, deployRootContract, waitForReceipt, getRootContracts, getContract, saveRootContracts } from "../helpers/helpers"; +import { requireEnv, waitForConfirmation, deployRootContract, waitForReceipt, getRootContracts, getContract, saveRootContracts, verifyRootContract } from "../helpers/helpers"; import { LedgerSigner } from "../helpers/ledger_signer"; export async function deployRootContracts() { @@ -45,6 +45,7 @@ export async function deployRootContracts() { rootBridgeImpl = await deployRootContract("RootERC20BridgeFlowRate", rootDeployerWallet, null, deployerAddr); console.log("Transaction submitted: ", JSON.stringify(rootBridgeImpl.deployTransaction, null, 2)); await waitForReceipt(rootBridgeImpl.deployTransaction.hash, rootProvider); + verifyRootContract("RootERC20BridgeFlowRate", rootBridgeImpl.address); } rootContracts.ROOT_BRIDGE_IMPL_ADDRESS = rootBridgeImpl.address; saveRootContracts(rootContracts); @@ -59,7 +60,8 @@ export async function deployRootContracts() { console.log("Deploy root adaptor impl..."); rootAdaptorImpl = await deployRootContract("RootAxelarBridgeAdaptor", rootDeployerWallet, null, rootGatewayAddr, deployerAddr); console.log("Transaction submitted: ", JSON.stringify(rootAdaptorImpl.deployTransaction, null, 2)); - await waitForReceipt(rootAdaptorImpl.deployTransaction.hash, rootProvider); + await waitForReceipt(rootAdaptorImpl.deployTransaction.hash, rootProvider); + verifyRootContract("RootAxelarBridgeAdaptor", rootAdaptorImpl.address); } rootContracts.ROOT_ADAPTOR_IMPL_ADDRESS = rootAdaptorImpl.address; saveRootContracts(rootContracts); @@ -96,6 +98,7 @@ export async function deployRootContracts() { rootTokenTemplate = await deployRootContract("ChildERC20", reservedDeployerWallet, nonceReserved); console.log("Transaction submitted: ", JSON.stringify(rootTokenTemplate.deployTransaction, null, 2)); await waitForReceipt(rootTokenTemplate.deployTransaction.hash, rootProvider); + verifyRootContract("ChildERC20", rootTokenTemplate.address); } rootContracts.ROOT_TOKEN_TEMPLATE = rootTokenTemplate.address; saveRootContracts(rootContracts); @@ -127,6 +130,7 @@ export async function deployRootContracts() { proxyAdmin = await deployRootContract("ProxyAdmin", reservedDeployerWallet, null); console.log("Transaction submitted: ", JSON.stringify(proxyAdmin.deployTransaction, null, 2)); await waitForReceipt(proxyAdmin.deployTransaction.hash, rootProvider); + verifyRootContract("ProxyAdmin", proxyAdmin.address); } rootContracts.ROOT_PROXY_ADMIN = proxyAdmin.address; saveRootContracts(rootContracts); @@ -147,6 +151,7 @@ export async function deployRootContracts() { rootBridgeProxy = await deployRootContract("TransparentUpgradeableProxy", reservedDeployerWallet, null, rootBridgeImpl.address, proxyAdmin.address, []); console.log("Transaction submitted: ", JSON.stringify(rootBridgeProxy.deployTransaction, null, 2)); await waitForReceipt(rootBridgeProxy.deployTransaction.hash, rootProvider); + verifyRootContract("TransparentUpgradeableProxy", rootBridgeProxy.address); } rootContracts.ROOT_BRIDGE_PROXY_ADDRESS = rootBridgeProxy.address; saveRootContracts(rootContracts); @@ -167,6 +172,7 @@ export async function deployRootContracts() { rootAdaptorProxy = await deployRootContract("TransparentUpgradeableProxy", reservedDeployerWallet, null, rootAdaptorImpl.address, proxyAdmin.address, []); console.log("Transaction submitted: ", JSON.stringify(rootAdaptorProxy.deployTransaction, null, 2)); await waitForReceipt(rootAdaptorProxy.deployTransaction.hash, rootProvider); + verifyRootContract("TransparentUpgradeableProxy", rootAdaptorProxy.address); } rootContracts.ROOT_ADAPTOR_PROXY_ADDRESS = rootAdaptorProxy.address; saveRootContracts(rootContracts); diff --git a/scripts/helpers/helpers.ts b/scripts/helpers/helpers.ts index 6ad30056..81c936b9 100644 --- a/scripts/helpers/helpers.ts +++ b/scripts/helpers/helpers.ts @@ -1,6 +1,8 @@ import { ContractFactory, providers, ethers } from "ethers"; import { LedgerSigner } from "./ledger_signer"; import * as fs from "fs"; +import util from 'util'; +const exec = util.promisify(require('child_process').exec); export function delay(time: number) { return new Promise(resolve => setTimeout(resolve, time)); @@ -184,4 +186,32 @@ export async function waitUntilSucceed(axelarURL: string, txHash: any) { } await delay(60000); } +} + +export async function verifyChildContract(contract: string, contractAddr: string) { + let url = process.env["CHILD_CHAIN_BLOCKSCOUT_API_URL"]; + if (url == null) { + console.log("CHILD_CHAIN_BLOCKSCOUT_API_URL not set, skip contract verification..."); + return; + } + let cmd = `forge verify-contract --verifier blockscout --verifier-url ${url} ${contractAddr} ${contract}`; + const { stdout, stderr } = await exec(cmd); + if (stderr != "") { + throw(stderr); + } + console.log(stdout); +} + +export async function verifyRootContract(contract: string, contractAddr: string) { + let key = process.env["ROOT_CHAIN_ETHERSCAN_API_KEY"]; + if (key == null) { + console.log("ROOT_CHAIN_ETHERSCAN_API_KEY not set, skip contract verification..."); + } + let chainID = requireEnv("ROOT_CHAIN_ID"); + let cmd = `ETHER_SCAN_API_KEY=${key} forge verify-contract ${contractAddr} ${contract} --chain-id ${chainID}`; + const { stdout, stderr } = await exec(cmd); + if (stderr != "") { + throw(stderr); + } + console.log(stdout); } \ No newline at end of file From 3ea5908f100daafbd974d35b909ac96aa98ae809 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Thu, 14 Dec 2023 07:46:47 +0800 Subject: [PATCH 18/40] Fix CI --- scripts/deploy/child_deployment.ts | 14 +++++++------- scripts/deploy/root_deployment.ts | 12 ++++++------ scripts/helpers/helpers.ts | 5 +++-- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/scripts/deploy/child_deployment.ts b/scripts/deploy/child_deployment.ts index ce8201f1..d25c287c 100644 --- a/scripts/deploy/child_deployment.ts +++ b/scripts/deploy/child_deployment.ts @@ -45,7 +45,7 @@ export async function deployChildContracts() { wrappedIMX = await deployChildContract("WIMX", childDeployerWallet, null); console.log("Transaction submitted: ", JSON.stringify(wrappedIMX.deployTransaction, null, 2)); await waitForReceipt(wrappedIMX.deployTransaction.hash, childProvider); - verifyChildContract("WIMX", wrappedIMX.address); + await verifyChildContract("WIMX", wrappedIMX.address); } childContracts.WRAPPED_IMX_ADDRESS = wrappedIMX.address; saveChildContracts(childContracts); @@ -61,7 +61,7 @@ export async function deployChildContracts() { childBridgeImpl = await deployChildContract("ChildERC20Bridge", childDeployerWallet, null, deployerAddr); console.log("Transaction submitted: ", JSON.stringify(childBridgeImpl.deployTransaction, null, 2)); await waitForReceipt(childBridgeImpl.deployTransaction.hash, childProvider); - verifyChildContract("ChildERC20Bridge", childBridgeImpl.address); + await verifyChildContract("ChildERC20Bridge", childBridgeImpl.address); } childContracts.CHILD_BRIDGE_IMPL_ADDRESS = childBridgeImpl.address; saveChildContracts(childContracts); @@ -77,7 +77,7 @@ export async function deployChildContracts() { childAdaptorImpl = await deployChildContract("ChildAxelarBridgeAdaptor", childDeployerWallet, null, childGatewayAddr, deployerAddr); console.log("Transaction submitted: ", JSON.stringify(childAdaptorImpl.deployTransaction, null, 2)); await waitForReceipt(childAdaptorImpl.deployTransaction.hash, childProvider); - verifyChildContract("ChildAxelarBridgeAdaptor", childAdaptorImpl.address); + await verifyChildContract("ChildAxelarBridgeAdaptor", childAdaptorImpl.address); } childContracts.CHILD_ADAPTOR_IMPL_ADDRESS = childAdaptorImpl.address; saveChildContracts(childContracts); @@ -114,7 +114,7 @@ export async function deployChildContracts() { childTokenTemplate = await deployChildContract("ChildERC20", reservedDeployerWallet, nonceReserved); console.log("Transaction submitted: ", JSON.stringify(childTokenTemplate.deployTransaction, null, 2)); await waitForReceipt(childTokenTemplate.deployTransaction.hash, childProvider); - verifyChildContract("ChildERC20", childTokenTemplate.address); + await verifyChildContract("ChildERC20", childTokenTemplate.address); } childContracts.CHILD_TOKEN_TEMPLATE = childTokenTemplate.address; saveChildContracts(childContracts); @@ -150,7 +150,7 @@ export async function deployChildContracts() { proxyAdmin = await deployChildContract("ProxyAdmin", reservedDeployerWallet, null); console.log("Transaction submitted: ", JSON.stringify(proxyAdmin.deployTransaction, null, 2)); await waitForReceipt(proxyAdmin.deployTransaction.hash, childProvider); - verifyChildContract("ProxyAdmin", proxyAdmin.address); + await verifyChildContract("ProxyAdmin", proxyAdmin.address); } childContracts.CHILD_PROXY_ADMIN = proxyAdmin.address; saveChildContracts(childContracts); @@ -171,7 +171,7 @@ export async function deployChildContracts() { childBridgeProxy = await deployChildContract("TransparentUpgradeableProxy", reservedDeployerWallet, null, childBridgeImpl.address, proxyAdmin.address, []); console.log("Transaction submitted: ", JSON.stringify(childBridgeProxy.deployTransaction, null, 2)); await waitForReceipt(childBridgeProxy.deployTransaction.hash, childProvider); - verifyChildContract("TransparentUpgradeableProxy", childBridgeProxy.address); + await verifyChildContract("TransparentUpgradeableProxy", childBridgeProxy.address); } childContracts.CHILD_BRIDGE_PROXY_ADDRESS = childBridgeProxy.address; saveChildContracts(childContracts); @@ -192,7 +192,7 @@ export async function deployChildContracts() { childAdaptorProxy = await deployChildContract("TransparentUpgradeableProxy", reservedDeployerWallet, null, childAdaptorImpl.address, proxyAdmin.address, []); console.log("Transaction submitted: ", JSON.stringify(childAdaptorProxy.deployTransaction, null, 2)); await waitForReceipt(childAdaptorProxy.deployTransaction.hash, childProvider); - verifyChildContract("TransparentUpgradeableProxy", childAdaptorProxy.address); + await verifyChildContract("TransparentUpgradeableProxy", childAdaptorProxy.address); } childContracts.CHILD_ADAPTOR_PROXY_ADDRESS = childAdaptorProxy.address; saveChildContracts(childContracts); diff --git a/scripts/deploy/root_deployment.ts b/scripts/deploy/root_deployment.ts index da7b01ef..3690c963 100644 --- a/scripts/deploy/root_deployment.ts +++ b/scripts/deploy/root_deployment.ts @@ -45,7 +45,7 @@ export async function deployRootContracts() { rootBridgeImpl = await deployRootContract("RootERC20BridgeFlowRate", rootDeployerWallet, null, deployerAddr); console.log("Transaction submitted: ", JSON.stringify(rootBridgeImpl.deployTransaction, null, 2)); await waitForReceipt(rootBridgeImpl.deployTransaction.hash, rootProvider); - verifyRootContract("RootERC20BridgeFlowRate", rootBridgeImpl.address); + await verifyRootContract("RootERC20BridgeFlowRate", rootBridgeImpl.address); } rootContracts.ROOT_BRIDGE_IMPL_ADDRESS = rootBridgeImpl.address; saveRootContracts(rootContracts); @@ -61,7 +61,7 @@ export async function deployRootContracts() { rootAdaptorImpl = await deployRootContract("RootAxelarBridgeAdaptor", rootDeployerWallet, null, rootGatewayAddr, deployerAddr); console.log("Transaction submitted: ", JSON.stringify(rootAdaptorImpl.deployTransaction, null, 2)); await waitForReceipt(rootAdaptorImpl.deployTransaction.hash, rootProvider); - verifyRootContract("RootAxelarBridgeAdaptor", rootAdaptorImpl.address); + await verifyRootContract("RootAxelarBridgeAdaptor", rootAdaptorImpl.address); } rootContracts.ROOT_ADAPTOR_IMPL_ADDRESS = rootAdaptorImpl.address; saveRootContracts(rootContracts); @@ -98,7 +98,7 @@ export async function deployRootContracts() { rootTokenTemplate = await deployRootContract("ChildERC20", reservedDeployerWallet, nonceReserved); console.log("Transaction submitted: ", JSON.stringify(rootTokenTemplate.deployTransaction, null, 2)); await waitForReceipt(rootTokenTemplate.deployTransaction.hash, rootProvider); - verifyRootContract("ChildERC20", rootTokenTemplate.address); + await verifyRootContract("ChildERC20", rootTokenTemplate.address); } rootContracts.ROOT_TOKEN_TEMPLATE = rootTokenTemplate.address; saveRootContracts(rootContracts); @@ -130,7 +130,7 @@ export async function deployRootContracts() { proxyAdmin = await deployRootContract("ProxyAdmin", reservedDeployerWallet, null); console.log("Transaction submitted: ", JSON.stringify(proxyAdmin.deployTransaction, null, 2)); await waitForReceipt(proxyAdmin.deployTransaction.hash, rootProvider); - verifyRootContract("ProxyAdmin", proxyAdmin.address); + await verifyRootContract("ProxyAdmin", proxyAdmin.address); } rootContracts.ROOT_PROXY_ADMIN = proxyAdmin.address; saveRootContracts(rootContracts); @@ -151,7 +151,7 @@ export async function deployRootContracts() { rootBridgeProxy = await deployRootContract("TransparentUpgradeableProxy", reservedDeployerWallet, null, rootBridgeImpl.address, proxyAdmin.address, []); console.log("Transaction submitted: ", JSON.stringify(rootBridgeProxy.deployTransaction, null, 2)); await waitForReceipt(rootBridgeProxy.deployTransaction.hash, rootProvider); - verifyRootContract("TransparentUpgradeableProxy", rootBridgeProxy.address); + await verifyRootContract("TransparentUpgradeableProxy", rootBridgeProxy.address); } rootContracts.ROOT_BRIDGE_PROXY_ADDRESS = rootBridgeProxy.address; saveRootContracts(rootContracts); @@ -172,7 +172,7 @@ export async function deployRootContracts() { rootAdaptorProxy = await deployRootContract("TransparentUpgradeableProxy", reservedDeployerWallet, null, rootAdaptorImpl.address, proxyAdmin.address, []); console.log("Transaction submitted: ", JSON.stringify(rootAdaptorProxy.deployTransaction, null, 2)); await waitForReceipt(rootAdaptorProxy.deployTransaction.hash, rootProvider); - verifyRootContract("TransparentUpgradeableProxy", rootAdaptorProxy.address); + await verifyRootContract("TransparentUpgradeableProxy", rootAdaptorProxy.address); } rootContracts.ROOT_ADAPTOR_PROXY_ADDRESS = rootAdaptorProxy.address; saveRootContracts(rootContracts); diff --git a/scripts/helpers/helpers.ts b/scripts/helpers/helpers.ts index 81c936b9..e7d27981 100644 --- a/scripts/helpers/helpers.ts +++ b/scripts/helpers/helpers.ts @@ -190,7 +190,7 @@ export async function waitUntilSucceed(axelarURL: string, txHash: any) { export async function verifyChildContract(contract: string, contractAddr: string) { let url = process.env["CHILD_CHAIN_BLOCKSCOUT_API_URL"]; - if (url == null) { + if (url == null || url == undefined) { console.log("CHILD_CHAIN_BLOCKSCOUT_API_URL not set, skip contract verification..."); return; } @@ -204,8 +204,9 @@ export async function verifyChildContract(contract: string, contractAddr: string export async function verifyRootContract(contract: string, contractAddr: string) { let key = process.env["ROOT_CHAIN_ETHERSCAN_API_KEY"]; - if (key == null) { + if (key == null || key == undefined) { console.log("ROOT_CHAIN_ETHERSCAN_API_KEY not set, skip contract verification..."); + return; } let chainID = requireEnv("ROOT_CHAIN_ID"); let cmd = `ETHER_SCAN_API_KEY=${key} forge verify-contract ${contractAddr} ${contract} --chain-id ${chainID}`; From 25a5a75b2d1b8f15ce8d20a6229ec793d3874815 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Thu, 14 Dec 2023 08:02:19 +0800 Subject: [PATCH 19/40] Delete verify.txt --- scripts/bootstrap/verify.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 scripts/bootstrap/verify.txt diff --git a/scripts/bootstrap/verify.txt b/scripts/bootstrap/verify.txt deleted file mode 100644 index 902153db..00000000 --- a/scripts/bootstrap/verify.txt +++ /dev/null @@ -1 +0,0 @@ -forge verify-contract --verifier blockscout --verifier-url https://explorer.testnet.immutable.com/api
SomeContract \ No newline at end of file From 44cf8046c67a5cadcd88f3fb6b4bc8b238d9a5d1 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Thu, 14 Dec 2023 08:45:30 +0800 Subject: [PATCH 20/40] Update helpers.ts --- scripts/helpers/helpers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/helpers/helpers.ts b/scripts/helpers/helpers.ts index e7d27981..bb5846cd 100644 --- a/scripts/helpers/helpers.ts +++ b/scripts/helpers/helpers.ts @@ -190,7 +190,7 @@ export async function waitUntilSucceed(axelarURL: string, txHash: any) { export async function verifyChildContract(contract: string, contractAddr: string) { let url = process.env["CHILD_CHAIN_BLOCKSCOUT_API_URL"]; - if (url == null || url == undefined) { + if (url == null || url == "") { console.log("CHILD_CHAIN_BLOCKSCOUT_API_URL not set, skip contract verification..."); return; } @@ -204,7 +204,7 @@ export async function verifyChildContract(contract: string, contractAddr: string export async function verifyRootContract(contract: string, contractAddr: string) { let key = process.env["ROOT_CHAIN_ETHERSCAN_API_KEY"]; - if (key == null || key == undefined) { + if (key == null || key == "") { console.log("ROOT_CHAIN_ETHERSCAN_API_KEY not set, skip contract verification..."); return; } From e989792821311afe3e50e959cad9d7d3e8ae5871 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Thu, 14 Dec 2023 08:48:42 +0800 Subject: [PATCH 21/40] Update helpers.ts --- scripts/helpers/helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/helpers/helpers.ts b/scripts/helpers/helpers.ts index bb5846cd..a982e899 100644 --- a/scripts/helpers/helpers.ts +++ b/scripts/helpers/helpers.ts @@ -209,7 +209,7 @@ export async function verifyRootContract(contract: string, contractAddr: string) return; } let chainID = requireEnv("ROOT_CHAIN_ID"); - let cmd = `ETHER_SCAN_API_KEY=${key} forge verify-contract ${contractAddr} ${contract} --chain-id ${chainID}`; + let cmd = `ETHERSCAN_API_KEY=${key} forge verify-contract ${contractAddr} ${contract} --chain-id ${chainID}`; const { stdout, stderr } = await exec(cmd); if (stderr != "") { throw(stderr); From 156005a1b561f91b53e442595c329a24b5de4f41 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Thu, 14 Dec 2023 09:39:51 +0800 Subject: [PATCH 22/40] Improve stability --- scripts/deploy/child_initialisation.ts | 86 ++++----- scripts/deploy/root_initialisation.ts | 230 +++++++++++++++---------- 2 files changed, 182 insertions(+), 134 deletions(-) diff --git a/scripts/deploy/child_initialisation.ts b/scripts/deploy/child_initialisation.ts index 8be95c1a..f42d3ce2 100644 --- a/scripts/deploy/child_initialisation.ts +++ b/scripts/deploy/child_initialisation.ts @@ -50,48 +50,56 @@ export async function initialiseChildContracts() { await waitForConfirmation(); // Initialise child bridge - console.log("Initialise child bridge..."); let childBridge = getContract("ChildERC20Bridge", childBridgeAddr, childProvider); - let [priorityFee, maxFee] = await getFee(childProvider); - let resp = await childBridge.connect(childDeployerWallet).initialize( + if (await childBridge.rootIMXToken() != "0x0000000000000000000000000000000000000000") { + console.log("Child bridge has already been initialised, skip."); + } else { + console.log("Initialise child bridge..."); + let [priorityFee, maxFee] = await getFee(childProvider); + let resp = await childBridge.connect(childDeployerWallet).initialize( + { + defaultAdmin: childPrivilegedMultisig, + pauser: childBreakglass, + unpauser: childPrivilegedMultisig, + adaptorManager: childPrivilegedMultisig, + initialDepositor: deployerAddr, + treasuryManager: multisigAddr, + }, + childAdaptorAddr, + childTemplateAddr, + rootIMXAddr, + childWIMXAddr, { - defaultAdmin: childPrivilegedMultisig, - pauser: childBreakglass, - unpauser: childPrivilegedMultisig, - adaptorManager: childPrivilegedMultisig, - initialDepositor: deployerAddr, - treasuryManager: multisigAddr, - }, - childAdaptorAddr, - childTemplateAddr, - rootIMXAddr, - childWIMXAddr, - { - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, childProvider); - + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, childProvider); + } + // Initialise child adaptor - console.log("Initialise child adaptor..."); let childAdaptor = getContract("ChildAxelarBridgeAdaptor", childAdaptorAddr, childProvider); - [priorityFee, maxFee] = await getFee(childProvider); - resp = await childAdaptor.connect(childDeployerWallet).initialize( + if (await childAdaptor.gasService() != "0x0000000000000000000000000000000000000000") { + console.log("Child adaptor has already been initialized, skip."); + } else { + console.log("Initialise child adaptor..."); + let [priorityFee, maxFee] = await getFee(childProvider); + let resp = await childAdaptor.connect(childDeployerWallet).initialize( + { + defaultAdmin: childPrivilegedMultisig, + bridgeManager: childPrivilegedMultisig, + gasServiceManager: childPrivilegedMultisig, + targetManager: childPrivilegedMultisig, + }, + childBridgeAddr, + rootChainName, + rootAdaptorAddr, + childGasServiceAddr, { - defaultAdmin: childPrivilegedMultisig, - bridgeManager: childPrivilegedMultisig, - gasServiceManager: childPrivilegedMultisig, - targetManager: childPrivilegedMultisig, - }, - childBridgeAddr, - rootChainName, - rootAdaptorAddr, - childGasServiceAddr, - { - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, childProvider); + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, childProvider); + } } \ No newline at end of file diff --git a/scripts/deploy/root_initialisation.ts b/scripts/deploy/root_initialisation.ts index 4b542e8f..17d1c4d9 100644 --- a/scripts/deploy/root_initialisation.ts +++ b/scripts/deploy/root_initialisation.ts @@ -67,103 +67,139 @@ export async function initialiseRootContracts() { await waitForConfirmation(); // Initialise root bridge - console.log("Initialise root bridge..."); let rootBridge = getContract("RootERC20BridgeFlowRate", rootBridgeAddr, rootProvider); - let resp = await rootBridge.connect(rootDeployerWallet)["initialize((address,address,address,address,address),address,address,address,address,address,uint256,address)"]( - { - defaultAdmin: deployerAddr, - pauser: rootBreakglass, - unpauser: rootPrivilegedMultisig, - variableManager: rootPrivilegedMultisig, - adaptorManager: rootPrivilegedMultisig, - }, - rootAdaptorAddr, - childBridgeAddr, - rootTemplateAddr, - rootIMXAddr, - rootWETHAddr, - ethers.utils.parseEther(imxDepositLimit), - deployerAddr); - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, rootProvider); + if (await rootBridge.rootIMXToken() != "0x0000000000000000000000000000000000000000") { + console.log("Root bridge has already been initialised, skip."); + } else { + console.log("Initialise root bridge..."); + let resp = await rootBridge.connect(rootDeployerWallet)["initialize((address,address,address,address,address),address,address,address,address,address,uint256,address)"]( + { + defaultAdmin: deployerAddr, + pauser: rootBreakglass, + unpauser: rootPrivilegedMultisig, + variableManager: rootPrivilegedMultisig, + adaptorManager: rootPrivilegedMultisig, + }, + rootAdaptorAddr, + childBridgeAddr, + rootTemplateAddr, + rootIMXAddr, + rootWETHAddr, + ethers.utils.parseEther(imxDepositLimit), + deployerAddr); + console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, rootProvider); + } // Configure rate // IMX - console.log("Configure rate limiting for IMX...") - resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( - rootIMXAddr, - ethers.utils.parseEther(rateLimitIMXCap), - ethers.utils.parseEther(rateLimitIMXRefill), - ethers.utils.parseEther(rateLimitIMXLargeThreshold) - ); - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, rootProvider); + if ((await rootBridge.largeTransferThresholds(rootIMXAddr)).toString() != "0") { + console.log("IMX rate limiting has already been configured, skip."); + } else { + console.log("Configure rate limiting for IMX...") + let resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( + rootIMXAddr, + ethers.utils.parseEther(rateLimitIMXCap), + ethers.utils.parseEther(rateLimitIMXRefill), + ethers.utils.parseEther(rateLimitIMXLargeThreshold) + ); + console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, rootProvider); + } // ETH - console.log("Configure rate limiting for ETH...") - resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( - await rootBridge.NATIVE_ETH(), - ethers.utils.parseEther(rateLimitETHCap), - ethers.utils.parseEther(rateLimitETHRefill), - ethers.utils.parseEther(rateLimitETHLargeThreshold) - ); - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, rootProvider); + if ((await rootBridge.largeTransferThresholds(await rootBridge.NATIVE_ETH())).toString() != "0") { + console.log("ETH rate limiting has already been configured, skip."); + } else { + console.log("Configure rate limiting for ETH...") + let resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( + await rootBridge.NATIVE_ETH(), + ethers.utils.parseEther(rateLimitETHCap), + ethers.utils.parseEther(rateLimitETHRefill), + ethers.utils.parseEther(rateLimitETHLargeThreshold) + ); + console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, rootProvider); + } // USDC - console.log("Configure rate limiting for USDC...") - resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( - rateLimitUSDCAddr, - ethers.utils.parseEther(rateLimitUSDCCap), - ethers.utils.parseEther(rateLimitUSDCRefill), - ethers.utils.parseEther(rateLimitUSDCLargeThreshold) - ); - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, rootProvider); + if ((await rootBridge.largeTransferThresholds(rateLimitUSDCAddr)).toString() != "0") { + console.log("USDC rate limiting has already been configured, skip."); + } else { + console.log("Configure rate limiting for USDC...") + let resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( + rateLimitUSDCAddr, + ethers.utils.parseEther(rateLimitUSDCCap), + ethers.utils.parseEther(rateLimitUSDCRefill), + ethers.utils.parseEther(rateLimitUSDCLargeThreshold) + ); + console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, rootProvider); + } // GU - console.log("Configure rate limiting for GU...") - resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( - rateLimitGUAddr, - ethers.utils.parseEther(rateLimitGUCap), - ethers.utils.parseEther(rateLimitGURefill), - ethers.utils.parseEther(rateLimitGULargeThreshold) - ); - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, rootProvider); + if ((await rootBridge.largeTransferThresholds(rateLimitGUAddr)).toString() != "0") { + console.log("GU rate limiting has already been configured, skip."); + } else { + console.log("Configure rate limiting for GU...") + let resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( + rateLimitGUAddr, + ethers.utils.parseEther(rateLimitGUCap), + ethers.utils.parseEther(rateLimitGURefill), + ethers.utils.parseEther(rateLimitGULargeThreshold) + ); + console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, rootProvider); + } // Checkmate - console.log("Configure rate limiting for CheckMate...") - resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( - rateLimitCheckMateAddr, - ethers.utils.parseEther(rateLimitCheckMateCap), - ethers.utils.parseEther(rateLimitCheckMateRefill), - ethers.utils.parseEther(rateLimitCheckMateLargeThreshold) - ); - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, rootProvider); + if ((await rootBridge.largeTransferThresholds(rateLimitCheckMateAddr)).toString() != "0") { + console.log("CheckMate rate limiting has already been configured, skip."); + } else { + console.log("Configure rate limiting for CheckMate...") + let resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( + rateLimitCheckMateAddr, + ethers.utils.parseEther(rateLimitCheckMateCap), + ethers.utils.parseEther(rateLimitCheckMateRefill), + ethers.utils.parseEther(rateLimitCheckMateLargeThreshold) + ); + console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, rootProvider); + } // GOG - console.log("Configure rate limiting for GOG...") - resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( - rateLimitGOGAddr, - ethers.utils.parseEther(rateLimitGOGCap), - ethers.utils.parseEther(rateLimitGOGRefill), - ethers.utils.parseEther(rateLimitGOGLargeThreshold) - ); - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, rootProvider); - + if ((await rootBridge.largeTransferThresholds(rateLimitGOGAddr)).toString() != "0") { + console.log("GOG rate limiting has already been configured, skip."); + } else { + console.log("Configure rate limiting for GOG...") + let resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( + rateLimitGOGAddr, + ethers.utils.parseEther(rateLimitGOGCap), + ethers.utils.parseEther(rateLimitGOGRefill), + ethers.utils.parseEther(rateLimitGOGLargeThreshold) + ); + console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, rootProvider); + } + // Grant roles - console.log("Grant RATE_CONTROL_ROLE to multisig...") - resp = await rootBridge.connect(rootDeployerWallet).grantRole(utils.keccak256(utils.toUtf8Bytes("RATE")), rootPrivilegedMultisig); - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, rootProvider); + if (await rootBridge.hasRole(utils.keccak256(utils.toUtf8Bytes("RATE")), rootPrivilegedMultisig)) { + console.log("Multisig has already obtained RATE_CONTROL_ROLE..., skip."); + } else { + console.log("Grant RATE_CONTROL_ROLE to multisig..."); + let resp = await rootBridge.connect(rootDeployerWallet).grantRole(utils.keccak256(utils.toUtf8Bytes("RATE")), rootPrivilegedMultisig); + console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, rootProvider); + } - console.log("Grant DEFAULT_ADMIN to multisig...") - resp = await rootBridge.connect(rootDeployerWallet).grantRole(await rootBridge.DEFAULT_ADMIN_ROLE(), rootPrivilegedMultisig); - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, rootProvider); + if (await rootBridge.hasRole(await rootBridge.DEFAULT_ADMIN_ROLE(), rootPrivilegedMultisig)) { + console.log("Multisig has already obtained DEFAULT_ADMIN..., skip."); + } else { + console.log("Grant DEFAULT_ADMIN to multisig...") + let resp = await rootBridge.connect(rootDeployerWallet).grantRole(await rootBridge.DEFAULT_ADMIN_ROLE(), rootPrivilegedMultisig); + console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, rootProvider); + } // Print summary console.log("Does multisig have DEFAULT_ADMIN: ", await rootBridge.hasRole(await rootBridge.DEFAULT_ADMIN_ROLE(), rootPrivilegedMultisig)); @@ -172,19 +208,23 @@ export async function initialiseRootContracts() { console.log("Does deployer have RATE_ADMIN: ", await rootBridge.hasRole(utils.keccak256(utils.toUtf8Bytes("RATE")), deployerAddr)); // Initialise root adaptor - console.log("Initialise root adaptor..."); let rootAdaptor = getContract("RootAxelarBridgeAdaptor", rootAdaptorAddr, rootProvider); - resp = await rootAdaptor.connect(rootDeployerWallet).initialize( - { - defaultAdmin: rootPrivilegedMultisig, - bridgeManager: rootPrivilegedMultisig, - gasServiceManager: rootPrivilegedMultisig, - targetManager: rootPrivilegedMultisig, - }, - rootBridgeAddr, - childChainName, - childAdaptorAddr, - rootGasServiceAddr); - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, rootProvider); + if (await rootAdaptor.gasService() != "0x0000000000000000000000000000000000000000") { + console.log("Root adaptor has already been initialized, skip."); + } else { + console.log("Initialise root adaptor..."); + let resp = await rootAdaptor.connect(rootDeployerWallet).initialize( + { + defaultAdmin: rootPrivilegedMultisig, + bridgeManager: rootPrivilegedMultisig, + gasServiceManager: rootPrivilegedMultisig, + targetManager: rootPrivilegedMultisig, + }, + rootBridgeAddr, + childChainName, + childAdaptorAddr, + rootGasServiceAddr); + console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, rootProvider); + } } \ No newline at end of file From bcac3a94f25a611ab0e530759175394e49896b32 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Thu, 14 Dec 2023 09:47:07 +0800 Subject: [PATCH 23/40] Improve stability again --- scripts/bootstrap/9_test_preparation.ts | 56 ++++++++++++++++--------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/scripts/bootstrap/9_test_preparation.ts b/scripts/bootstrap/9_test_preparation.ts index 01ed75c2..b4e2b4ab 100644 --- a/scripts/bootstrap/9_test_preparation.ts +++ b/scripts/bootstrap/9_test_preparation.ts @@ -53,30 +53,46 @@ async function run() { console.log("Deployed to ROOT_TEST_CUSTOM_TOKEN: ", rootCustomToken.address); // Mint tokens - console.log("Mint tokens..."); - let resp = await rootCustomToken.connect(rootDeployerWallet).mint(rootTestWallet.address, ethers.utils.parseEther("1000.0").toBigInt()); - await waitForReceipt(resp.hash, rootProvider); + if ((await rootCustomToken.balanceOf(rootTestWallet.address)).toString() != "0") { + console.log("Test account has already been given test tokens, skip."); + } else { + console.log("Mint tokens..."); + let resp = await rootCustomToken.connect(rootDeployerWallet).mint(rootTestWallet.address, ethers.utils.parseEther("1000.0").toBigInt()); + await waitForReceipt(resp.hash, rootProvider); + } - console.log("Set rate control..."); - // Set rate control - resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( - rootCustomToken.address, - ethers.utils.parseEther("20016.0"), - ethers.utils.parseEther("5.56"), - ethers.utils.parseEther("10008.0") - ); - await waitForReceipt(resp.hash, rootProvider); + if ((await rootBridge.largeTransferThresholds(rootCustomToken.address)).toString() != "0") { + console.log("Rate limiting has already been configured for custom token, skip."); + } else { + console.log("Set rate control..."); + // Set rate control + let resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( + rootCustomToken.address, + ethers.utils.parseEther("20016.0"), + ethers.utils.parseEther("5.56"), + ethers.utils.parseEther("10008.0") + ); + await waitForReceipt(resp.hash, rootProvider); + } // Revoke roles - console.log("Revoke RATE_CONTROL_ROLE of deployer...") - resp = await rootBridge.connect(rootDeployerWallet).revokeRole(utils.keccak256(utils.toUtf8Bytes("RATE")), deployerAddr); - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, rootProvider); + if (!await rootBridge.hasRole(utils.keccak256(utils.toUtf8Bytes("RATE")), deployerAddr)) { + console.log("Deployer has already revoked RATE_CONTROL_ROLE..., skip."); + } else { + console.log("Revoke RATE_CONTROL_ROLE of deployer...") + let resp = await rootBridge.connect(rootDeployerWallet).revokeRole(utils.keccak256(utils.toUtf8Bytes("RATE")), deployerAddr); + console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, rootProvider); + } - console.log("Revoke DEFAULT_ADMIN of deployer...") - resp = await rootBridge.connect(rootDeployerWallet).revokeRole(await rootBridge.DEFAULT_ADMIN_ROLE(), deployerAddr); - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, rootProvider); + if (!await rootBridge.hasRole(await rootBridge.DEFAULT_ADMIN_ROLE(), deployerAddr)) { + console.log("Deployer has already revoked DEFAULT_ADMIN..., skip."); + } else { + console.log("Revoke DEFAULT_ADMIN of deployer...") + let resp = await rootBridge.connect(rootDeployerWallet).revokeRole(await rootBridge.DEFAULT_ADMIN_ROLE(), deployerAddr); + console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + await waitForReceipt(resp.hash, rootProvider); + } // Print summary console.log("Does multisig have DEFAULT_ADMIN: ", await rootBridge.hasRole(await rootBridge.DEFAULT_ADMIN_ROLE(), rootPrivilegedMultisig)); From 11359b3e009fdcf4e09a44d725f672efbfddf7a4 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Fri, 15 Dec 2023 13:25:28 +0800 Subject: [PATCH 24/40] Update verification --- scripts/deploy/child_deployment.ts | 44 +++++++++++++------------- scripts/deploy/root_deployment.ts | 50 +++++++++++++++--------------- scripts/helpers/helpers.ts | 7 ++++- 3 files changed, 53 insertions(+), 48 deletions(-) diff --git a/scripts/deploy/child_deployment.ts b/scripts/deploy/child_deployment.ts index d25c287c..beb62976 100644 --- a/scripts/deploy/child_deployment.ts +++ b/scripts/deploy/child_deployment.ts @@ -45,11 +45,11 @@ export async function deployChildContracts() { wrappedIMX = await deployChildContract("WIMX", childDeployerWallet, null); console.log("Transaction submitted: ", JSON.stringify(wrappedIMX.deployTransaction, null, 2)); await waitForReceipt(wrappedIMX.deployTransaction.hash, childProvider); + childContracts.WRAPPED_IMX_ADDRESS = wrappedIMX.address; + saveChildContracts(childContracts); + console.log("Deployed to WRAPPED_IMX_ADDRESS: ", wrappedIMX.address); await verifyChildContract("WIMX", wrappedIMX.address); } - childContracts.WRAPPED_IMX_ADDRESS = wrappedIMX.address; - saveChildContracts(childContracts); - console.log("Deployed to WRAPPED_IMX_ADDRESS: ", wrappedIMX.address); // Deploy child bridge impl let childBridgeImpl; @@ -61,11 +61,11 @@ export async function deployChildContracts() { childBridgeImpl = await deployChildContract("ChildERC20Bridge", childDeployerWallet, null, deployerAddr); console.log("Transaction submitted: ", JSON.stringify(childBridgeImpl.deployTransaction, null, 2)); await waitForReceipt(childBridgeImpl.deployTransaction.hash, childProvider); + childContracts.CHILD_BRIDGE_IMPL_ADDRESS = childBridgeImpl.address; + saveChildContracts(childContracts); + console.log("Deployed to CHILD_BRIDGE_IMPL_ADDRESS: ", childBridgeImpl.address); await verifyChildContract("ChildERC20Bridge", childBridgeImpl.address); } - childContracts.CHILD_BRIDGE_IMPL_ADDRESS = childBridgeImpl.address; - saveChildContracts(childContracts); - console.log("Deployed to CHILD_BRIDGE_IMPL_ADDRESS: ", childBridgeImpl.address); // Deploy child adaptor impl let childAdaptorImpl; @@ -77,11 +77,11 @@ export async function deployChildContracts() { childAdaptorImpl = await deployChildContract("ChildAxelarBridgeAdaptor", childDeployerWallet, null, childGatewayAddr, deployerAddr); console.log("Transaction submitted: ", JSON.stringify(childAdaptorImpl.deployTransaction, null, 2)); await waitForReceipt(childAdaptorImpl.deployTransaction.hash, childProvider); + childContracts.CHILD_ADAPTOR_IMPL_ADDRESS = childAdaptorImpl.address; + saveChildContracts(childContracts); + console.log("Deployed to CHILD_ADAPTOR_IMPL_ADDRESS: ", childAdaptorImpl.address); await verifyChildContract("ChildAxelarBridgeAdaptor", childAdaptorImpl.address); } - childContracts.CHILD_ADAPTOR_IMPL_ADDRESS = childAdaptorImpl.address; - saveChildContracts(childContracts); - console.log("Deployed to CHILD_ADAPTOR_IMPL_ADDRESS: ", childAdaptorImpl.address); if (childDeployerWallet instanceof LedgerSigner) { childDeployerWallet.close(); @@ -114,11 +114,11 @@ export async function deployChildContracts() { childTokenTemplate = await deployChildContract("ChildERC20", reservedDeployerWallet, nonceReserved); console.log("Transaction submitted: ", JSON.stringify(childTokenTemplate.deployTransaction, null, 2)); await waitForReceipt(childTokenTemplate.deployTransaction.hash, childProvider); + childContracts.CHILD_TOKEN_TEMPLATE = childTokenTemplate.address; + saveChildContracts(childContracts); + console.log("Deployed to CHILD_TOKEN_TEMPLATE: ", childTokenTemplate.address); await verifyChildContract("ChildERC20", childTokenTemplate.address); } - childContracts.CHILD_TOKEN_TEMPLATE = childTokenTemplate.address; - saveChildContracts(childContracts); - console.log("Deployed to CHILD_TOKEN_TEMPLATE: ", childTokenTemplate.address); // Initialise template if (await childTokenTemplate.name() == "TEMPLATE") { @@ -132,8 +132,8 @@ export async function deployChildContracts() { }); console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); await waitForReceipt(resp.hash, childProvider); + console.log("Initialised CHILD_TOKEN_TEMPLATE at: ", childTokenTemplate.address); } - console.log("Initialised CHILD_TOKEN_TEMPLATE at: ", childTokenTemplate.address); // Deploy proxy admin let proxyAdmin; @@ -150,11 +150,11 @@ export async function deployChildContracts() { proxyAdmin = await deployChildContract("ProxyAdmin", reservedDeployerWallet, null); console.log("Transaction submitted: ", JSON.stringify(proxyAdmin.deployTransaction, null, 2)); await waitForReceipt(proxyAdmin.deployTransaction.hash, childProvider); + childContracts.CHILD_PROXY_ADMIN = proxyAdmin.address; + saveChildContracts(childContracts); + console.log("Deployed to CHILD_PROXY_ADMIN: ", proxyAdmin.address); await verifyChildContract("ProxyAdmin", proxyAdmin.address); } - childContracts.CHILD_PROXY_ADMIN = proxyAdmin.address; - saveChildContracts(childContracts); - console.log("Deployed to CHILD_PROXY_ADMIN: ", proxyAdmin.address); // Deploy child bridge proxy let childBridgeProxy; @@ -171,11 +171,11 @@ export async function deployChildContracts() { childBridgeProxy = await deployChildContract("TransparentUpgradeableProxy", reservedDeployerWallet, null, childBridgeImpl.address, proxyAdmin.address, []); console.log("Transaction submitted: ", JSON.stringify(childBridgeProxy.deployTransaction, null, 2)); await waitForReceipt(childBridgeProxy.deployTransaction.hash, childProvider); + childContracts.CHILD_BRIDGE_PROXY_ADDRESS = childBridgeProxy.address; + saveChildContracts(childContracts); + console.log("Deployed to CHILD_BRIDGE_PROXY_ADDRESS: ", childBridgeProxy.address); await verifyChildContract("TransparentUpgradeableProxy", childBridgeProxy.address); } - childContracts.CHILD_BRIDGE_PROXY_ADDRESS = childBridgeProxy.address; - saveChildContracts(childContracts); - console.log("Deployed to CHILD_BRIDGE_PROXY_ADDRESS: ", childBridgeProxy.address); // Deploy child adaptor proxy let childAdaptorProxy; @@ -192,11 +192,11 @@ export async function deployChildContracts() { childAdaptorProxy = await deployChildContract("TransparentUpgradeableProxy", reservedDeployerWallet, null, childAdaptorImpl.address, proxyAdmin.address, []); console.log("Transaction submitted: ", JSON.stringify(childAdaptorProxy.deployTransaction, null, 2)); await waitForReceipt(childAdaptorProxy.deployTransaction.hash, childProvider); + childContracts.CHILD_ADAPTOR_PROXY_ADDRESS = childAdaptorProxy.address; + saveChildContracts(childContracts); + console.log("Deployed to CHILD_ADAPTOR_PROXY_ADDRESS: ", childAdaptorProxy.address); await verifyChildContract("TransparentUpgradeableProxy", childAdaptorProxy.address); } - childContracts.CHILD_ADAPTOR_PROXY_ADDRESS = childAdaptorProxy.address; - saveChildContracts(childContracts); - console.log("Deployed to CHILD_ADAPTOR_PROXY_ADDRESS: ", childAdaptorProxy.address); childContracts.CHILD_BRIDGE_ADDRESS = childBridgeProxy.address; childContracts.CHILD_ADAPTOR_ADDRESS = childAdaptorProxy.address, diff --git a/scripts/deploy/root_deployment.ts b/scripts/deploy/root_deployment.ts index 3690c963..cd19f6df 100644 --- a/scripts/deploy/root_deployment.ts +++ b/scripts/deploy/root_deployment.ts @@ -45,11 +45,11 @@ export async function deployRootContracts() { rootBridgeImpl = await deployRootContract("RootERC20BridgeFlowRate", rootDeployerWallet, null, deployerAddr); console.log("Transaction submitted: ", JSON.stringify(rootBridgeImpl.deployTransaction, null, 2)); await waitForReceipt(rootBridgeImpl.deployTransaction.hash, rootProvider); - await verifyRootContract("RootERC20BridgeFlowRate", rootBridgeImpl.address); + rootContracts.ROOT_BRIDGE_IMPL_ADDRESS = rootBridgeImpl.address; + saveRootContracts(rootContracts); + console.log("Deployed to ROOT_BRIDGE_IMPL_ADDRESS: ", rootBridgeImpl.address); + await verifyRootContract("RootERC20BridgeFlowRate", rootBridgeImpl.address, `"constructor(address)" "${deployerAddr}"`); } - rootContracts.ROOT_BRIDGE_IMPL_ADDRESS = rootBridgeImpl.address; - saveRootContracts(rootContracts); - console.log("Deployed to ROOT_BRIDGE_IMPL_ADDRESS: ", rootBridgeImpl.address); // Deploy root adaptor impl let rootAdaptorImpl; @@ -61,11 +61,11 @@ export async function deployRootContracts() { rootAdaptorImpl = await deployRootContract("RootAxelarBridgeAdaptor", rootDeployerWallet, null, rootGatewayAddr, deployerAddr); console.log("Transaction submitted: ", JSON.stringify(rootAdaptorImpl.deployTransaction, null, 2)); await waitForReceipt(rootAdaptorImpl.deployTransaction.hash, rootProvider); - await verifyRootContract("RootAxelarBridgeAdaptor", rootAdaptorImpl.address); + rootContracts.ROOT_ADAPTOR_IMPL_ADDRESS = rootAdaptorImpl.address; + saveRootContracts(rootContracts); + console.log("Deployed to ROOT_ADAPTOR_IMPL_ADDRESS: ", rootAdaptorImpl.address); + await verifyRootContract("RootAxelarBridgeAdaptor", rootAdaptorImpl.address, `"constructor(address,address)" "${rootGatewayAddr}" "${deployerAddr}"`); } - rootContracts.ROOT_ADAPTOR_IMPL_ADDRESS = rootAdaptorImpl.address; - saveRootContracts(rootContracts); - console.log("Deployed to ROOT_ADAPTOR_IMPL_ADDRESS: ", rootAdaptorImpl.address); if (rootDeployerWallet instanceof LedgerSigner) { rootDeployerWallet.close(); @@ -98,11 +98,11 @@ export async function deployRootContracts() { rootTokenTemplate = await deployRootContract("ChildERC20", reservedDeployerWallet, nonceReserved); console.log("Transaction submitted: ", JSON.stringify(rootTokenTemplate.deployTransaction, null, 2)); await waitForReceipt(rootTokenTemplate.deployTransaction.hash, rootProvider); - await verifyRootContract("ChildERC20", rootTokenTemplate.address); + rootContracts.ROOT_TOKEN_TEMPLATE = rootTokenTemplate.address; + saveRootContracts(rootContracts); + console.log("Deployed to ROOT_TOKEN_TEMPLATE: ", rootTokenTemplate.address); + await verifyRootContract("ChildERC20", rootTokenTemplate.address, null); } - rootContracts.ROOT_TOKEN_TEMPLATE = rootTokenTemplate.address; - saveRootContracts(rootContracts); - console.log("Deployed to ROOT_TOKEN_TEMPLATE: ", rootTokenTemplate.address); // Initialise template if (await rootTokenTemplate.name() == "TEMPLATE") { @@ -113,7 +113,7 @@ export async function deployRootContracts() { console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); await waitForReceipt(resp.hash, rootProvider); } - console.log("Deployed to ROOT_TOKEN_TEMPLATE: ", rootTokenTemplate.address); + console.log("Initialized ROOT_TOKEN_TEMPLATE at: ", rootTokenTemplate.address); // Deploy proxy admin let proxyAdmin; @@ -130,11 +130,11 @@ export async function deployRootContracts() { proxyAdmin = await deployRootContract("ProxyAdmin", reservedDeployerWallet, null); console.log("Transaction submitted: ", JSON.stringify(proxyAdmin.deployTransaction, null, 2)); await waitForReceipt(proxyAdmin.deployTransaction.hash, rootProvider); - await verifyRootContract("ProxyAdmin", proxyAdmin.address); + rootContracts.ROOT_PROXY_ADMIN = proxyAdmin.address; + saveRootContracts(rootContracts); + console.log("Deployed to ROOT_PROXY_ADMIN: ", proxyAdmin.address); + await verifyRootContract("ProxyAdmin", proxyAdmin.address, null); } - rootContracts.ROOT_PROXY_ADMIN = proxyAdmin.address; - saveRootContracts(rootContracts); - console.log("Deployed to ROOT_PROXY_ADMIN: ", proxyAdmin.address); // Deploy root bridge proxy let rootBridgeProxy; @@ -151,11 +151,11 @@ export async function deployRootContracts() { rootBridgeProxy = await deployRootContract("TransparentUpgradeableProxy", reservedDeployerWallet, null, rootBridgeImpl.address, proxyAdmin.address, []); console.log("Transaction submitted: ", JSON.stringify(rootBridgeProxy.deployTransaction, null, 2)); await waitForReceipt(rootBridgeProxy.deployTransaction.hash, rootProvider); - await verifyRootContract("TransparentUpgradeableProxy", rootBridgeProxy.address); + rootContracts.ROOT_BRIDGE_PROXY_ADDRESS = rootBridgeProxy.address; + saveRootContracts(rootContracts); + console.log("Deployed to ROOT_BRIDGE_PROXY_ADDRESS: ", rootBridgeProxy.address); + await verifyRootContract("TransparentUpgradeableProxy", rootBridgeProxy.address, `"constructor(address,address,bytes)" "${rootBridgeImpl.address}" "${proxyAdmin.address}" ""`); } - rootContracts.ROOT_BRIDGE_PROXY_ADDRESS = rootBridgeProxy.address; - saveRootContracts(rootContracts); - console.log("Deployed to ROOT_BRIDGE_PROXY_ADDRESS: ", rootBridgeProxy.address); // Deploy root adaptor proxy let rootAdaptorProxy; @@ -172,11 +172,11 @@ export async function deployRootContracts() { rootAdaptorProxy = await deployRootContract("TransparentUpgradeableProxy", reservedDeployerWallet, null, rootAdaptorImpl.address, proxyAdmin.address, []); console.log("Transaction submitted: ", JSON.stringify(rootAdaptorProxy.deployTransaction, null, 2)); await waitForReceipt(rootAdaptorProxy.deployTransaction.hash, rootProvider); - await verifyRootContract("TransparentUpgradeableProxy", rootAdaptorProxy.address); + rootContracts.ROOT_ADAPTOR_PROXY_ADDRESS = rootAdaptorProxy.address; + saveRootContracts(rootContracts); + console.log("Deployed to ROOT_ADAPTOR_PROXY_ADDRESS: ", rootAdaptorProxy.address); + await verifyRootContract("TransparentUpgradeableProxy", rootAdaptorProxy.address, `"constructor(address,address,bytes)" "${rootAdaptorImpl.address}" "${proxyAdmin.address}" ""`); } - rootContracts.ROOT_ADAPTOR_PROXY_ADDRESS = rootAdaptorProxy.address; - saveRootContracts(rootContracts); - console.log("Deployed to ROOT_ADAPTOR_PROXY_ADDRESS: ", rootAdaptorProxy.address); rootContracts.ROOT_BRIDGE_ADDRESS = rootBridgeProxy.address; rootContracts.ROOT_ADAPTOR_ADDRESS = rootAdaptorProxy.address; diff --git a/scripts/helpers/helpers.ts b/scripts/helpers/helpers.ts index a982e899..015478b2 100644 --- a/scripts/helpers/helpers.ts +++ b/scripts/helpers/helpers.ts @@ -189,6 +189,7 @@ export async function waitUntilSucceed(axelarURL: string, txHash: any) { } export async function verifyChildContract(contract: string, contractAddr: string) { + console.log("Verifying " + contract + " at " + contractAddr + " on child chain..."); let url = process.env["CHILD_CHAIN_BLOCKSCOUT_API_URL"]; if (url == null || url == "") { console.log("CHILD_CHAIN_BLOCKSCOUT_API_URL not set, skip contract verification..."); @@ -202,7 +203,8 @@ export async function verifyChildContract(contract: string, contractAddr: string console.log(stdout); } -export async function verifyRootContract(contract: string, contractAddr: string) { +export async function verifyRootContract(contract: string, contractAddr: string, args: string | null) { + console.log("Verifying " + contract + " at " + contractAddr + " on root chain..."); let key = process.env["ROOT_CHAIN_ETHERSCAN_API_KEY"]; if (key == null || key == "") { console.log("ROOT_CHAIN_ETHERSCAN_API_KEY not set, skip contract verification..."); @@ -210,6 +212,9 @@ export async function verifyRootContract(contract: string, contractAddr: string) } let chainID = requireEnv("ROOT_CHAIN_ID"); let cmd = `ETHERSCAN_API_KEY=${key} forge verify-contract ${contractAddr} ${contract} --chain-id ${chainID}`; + if (args != null) { + cmd += ` --constructor-args $(cast abi-encode ${args})` + } const { stdout, stderr } = await exec(cmd); if (stderr != "") { throw(stderr); From 480d7daf3941741621aeb81d3be49f736fafff9c Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Tue, 19 Dec 2023 08:26:40 +0800 Subject: [PATCH 25/40] Use Retry provider & Improve stability --- scripts/bootstrap/0_pre_validation.ts | 5 ++- scripts/bootstrap/1_deployer_funding.ts | 3 +- scripts/bootstrap/2_deployment_validation.ts | 5 ++- scripts/bootstrap/6_imx_burning.ts | 3 +- scripts/bootstrap/7_imx_rebalancing.ts | 5 ++- scripts/bootstrap/9_test_preparation.ts | 3 +- scripts/deploy/child_deployment.ts | 3 +- scripts/deploy/child_initialisation.ts | 3 +- scripts/deploy/root_deployment.ts | 3 +- scripts/deploy/root_initialisation.ts | 3 +- scripts/e2e/e2e.ts | 5 ++- scripts/helpers/helpers.ts | 24 ++++++++---- scripts/helpers/retry.ts | 39 ++++++++++++++++++++ scripts/localdev/axelar_setup.ts | 5 ++- scripts/localdev/childchain_setup.ts | 3 +- scripts/localdev/rootchain_setup.ts | 3 +- 16 files changed, 88 insertions(+), 27 deletions(-) create mode 100644 scripts/helpers/retry.ts diff --git a/scripts/bootstrap/0_pre_validation.ts b/scripts/bootstrap/0_pre_validation.ts index 7b717d6d..34b5dd42 100644 --- a/scripts/bootstrap/0_pre_validation.ts +++ b/scripts/bootstrap/0_pre_validation.ts @@ -4,6 +4,7 @@ dotenv.config(); import { ethers } from "ethers"; import { requireEnv, hasDuplicates } from "../helpers/helpers"; import { LedgerSigner } from "../helpers/ledger_signer"; +import { RetryProvider } from "../helpers/retry"; // The total supply of IMX const TOTAL_SUPPLY = "2000000000"; @@ -66,8 +67,8 @@ async function run() { requireEnv("RATE_LIMIT_GOG_REFILL_RATE"); requireEnv("RATE_LIMIT_GOG_LARGE_THRESHOLD"); - const childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); - const rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); + const childProvider = new RetryProvider(childRPCURL, Number(childChainID)); + const rootProvider = new RetryProvider(rootRPCURL, Number(rootChainID)); let deployerWallet; if (deployerSecret == "ledger") { let index = requireEnv("DEPLOYER_LEDGER_INDEX"); diff --git a/scripts/bootstrap/1_deployer_funding.ts b/scripts/bootstrap/1_deployer_funding.ts index 08890323..2d1622a5 100644 --- a/scripts/bootstrap/1_deployer_funding.ts +++ b/scripts/bootstrap/1_deployer_funding.ts @@ -4,6 +4,7 @@ dotenv.config(); import { ethers } from "ethers"; import { requireEnv, waitForConfirmation, waitForReceipt, getFee, hasDuplicates } from "../helpers/helpers"; import { LedgerSigner } from "../helpers/ledger_signer"; +import { RetryProvider } from "../helpers/retry"; async function run() { console.log("=======Start Deployer Funding======="); @@ -20,7 +21,7 @@ async function run() { let passportDeployerFund = requireEnv("PASSPORT_NONCE_RESERVER_FUND"); // Get deployer address - const childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); + const childProvider = new RetryProvider(childRPCURL, Number(childChainID)); let childDeployerWallet; if (deployerSecret == "ledger") { let index = requireEnv("DEPLOYER_LEDGER_INDEX"); diff --git a/scripts/bootstrap/2_deployment_validation.ts b/scripts/bootstrap/2_deployment_validation.ts index d49d8469..a006067c 100644 --- a/scripts/bootstrap/2_deployment_validation.ts +++ b/scripts/bootstrap/2_deployment_validation.ts @@ -3,6 +3,7 @@ import * as dotenv from "dotenv"; dotenv.config(); import { ethers } from "ethers"; import { requireEnv, hasDuplicates, requireNonEmptyCode } from "../helpers/helpers"; +import { RetryProvider } from "../helpers/retry"; async function run() { console.log("=======Start Deployment Validation======="); @@ -26,8 +27,8 @@ async function run() { throw("Duplicate address detected!"); } - const childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); - const rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); + const childProvider = new RetryProvider(childRPCURL, Number(childChainID)); + const rootProvider = new RetryProvider(rootRPCURL, Number(rootChainID)); // Check child chain. console.log("Check contracts on child chain..."); diff --git a/scripts/bootstrap/6_imx_burning.ts b/scripts/bootstrap/6_imx_burning.ts index 6dd1b16d..632d23da 100644 --- a/scripts/bootstrap/6_imx_burning.ts +++ b/scripts/bootstrap/6_imx_burning.ts @@ -4,6 +4,7 @@ dotenv.config(); import { ethers } from "ethers"; import { requireEnv, waitForConfirmation, hasDuplicates, waitForReceipt, getFee, getContract, getChildContracts } from "../helpers/helpers"; import { LedgerSigner } from "../helpers/ledger_signer"; +import { RetryProvider } from "../helpers/retry"; async function run() { console.log("=======Start IMX Burning======="); @@ -21,7 +22,7 @@ async function run() { let childBridgeAddr = childContracts.CHILD_BRIDGE_ADDRESS; // Get deployer address - const childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); + const childProvider = new RetryProvider(childRPCURL, Number(childChainID)); let childDeployerWallet; if (deployerSecret == "ledger") { let index = requireEnv("DEPLOYER_LEDGER_INDEX"); diff --git a/scripts/bootstrap/7_imx_rebalancing.ts b/scripts/bootstrap/7_imx_rebalancing.ts index 4d18e9c3..27c43ea6 100644 --- a/scripts/bootstrap/7_imx_rebalancing.ts +++ b/scripts/bootstrap/7_imx_rebalancing.ts @@ -4,6 +4,7 @@ dotenv.config(); import { ethers } from "ethers"; import { requireEnv, waitForConfirmation, waitForReceipt, getFee, hasDuplicates, getChildContracts, getRootContracts } from "../helpers/helpers"; import { LedgerSigner } from "../helpers/ledger_signer"; +import { RetryProvider } from "../helpers/retry"; // The total supply of IMX const TOTAL_SUPPLY = "2000000000"; @@ -30,8 +31,8 @@ async function run() { let rootBridgeAddr = rootContracts.ROOT_BRIDGE_ADDRESS; // Get deployer address - const childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); - const rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); + const childProvider = new RetryProvider(childRPCURL, Number(childChainID)); + const rootProvider = new RetryProvider(rootRPCURL, Number(rootChainID)); let rootDeployerWallet; if (deployerSecret == "ledger") { let index = requireEnv("DEPLOYER_LEDGER_INDEX"); diff --git a/scripts/bootstrap/9_test_preparation.ts b/scripts/bootstrap/9_test_preparation.ts index b4e2b4ab..7fcbac83 100644 --- a/scripts/bootstrap/9_test_preparation.ts +++ b/scripts/bootstrap/9_test_preparation.ts @@ -4,6 +4,7 @@ dotenv.config(); import { ethers, utils } from "ethers"; import { deployRootContract, getContract, getRootContracts, requireEnv, saveRootContracts, waitForConfirmation, waitForReceipt } from "../helpers/helpers"; import { LedgerSigner } from "../helpers/ledger_signer"; +import { RetryProvider } from "../helpers/retry"; async function run() { console.log("=======Start Test Preparation======="); @@ -16,7 +17,7 @@ async function run() { let rootPrivilegedMultisig = requireEnv("ROOT_PRIVILEGED_MULTISIG_ADDR"); // Get deployer address - const rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); + const rootProvider = new RetryProvider(rootRPCURL, Number(rootChainID)); let rootDeployerWallet; if (deployerSecret == "ledger") { let index = requireEnv("DEPLOYER_LEDGER_INDEX"); diff --git a/scripts/deploy/child_deployment.ts b/scripts/deploy/child_deployment.ts index beb62976..c62f3b42 100644 --- a/scripts/deploy/child_deployment.ts +++ b/scripts/deploy/child_deployment.ts @@ -4,6 +4,7 @@ dotenv.config(); import { ethers } from "ethers"; import { requireEnv, waitForConfirmation, deployChildContract, waitForReceipt, getFee, getChildContracts, getContract, saveChildContracts, verifyChildContract } from "../helpers/helpers"; import { LedgerSigner } from "../helpers/ledger_signer"; +import { RetryProvider } from "../helpers/retry"; export async function deployChildContracts() { // Check environment variables @@ -17,7 +18,7 @@ export async function deployChildContracts() { // Read from contract file. let childContracts = getChildContracts(); - const childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); + const childProvider = new RetryProvider(childRPCURL, Number(childChainID)); // Get deployer address let childDeployerWallet; diff --git a/scripts/deploy/child_initialisation.ts b/scripts/deploy/child_initialisation.ts index f42d3ce2..50154a7a 100644 --- a/scripts/deploy/child_initialisation.ts +++ b/scripts/deploy/child_initialisation.ts @@ -5,6 +5,7 @@ dotenv.config(); import { ethers } from "ethers"; import { requireEnv, waitForConfirmation, waitForReceipt, getFee, getContract, getChildContracts, getRootContracts } from "../helpers/helpers"; import { LedgerSigner } from "../helpers/ledger_signer"; +import { RetryProvider } from "../helpers/retry"; export async function initialiseChildContracts() { let rootChainName = requireEnv("ROOT_CHAIN_NAME"); @@ -33,7 +34,7 @@ export async function initialiseChildContracts() { } // Get deployer address - const childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); + const childProvider = new RetryProvider(childRPCURL, Number(childChainID)); let childDeployerWallet; if (deployerSecret == "ledger") { let index = requireEnv("DEPLOYER_LEDGER_INDEX"); diff --git a/scripts/deploy/root_deployment.ts b/scripts/deploy/root_deployment.ts index cd19f6df..a519d8e5 100644 --- a/scripts/deploy/root_deployment.ts +++ b/scripts/deploy/root_deployment.ts @@ -4,6 +4,7 @@ dotenv.config(); import { ethers } from "ethers"; import { requireEnv, waitForConfirmation, deployRootContract, waitForReceipt, getRootContracts, getContract, saveRootContracts, verifyRootContract } from "../helpers/helpers"; import { LedgerSigner } from "../helpers/ledger_signer"; +import { RetryProvider } from "../helpers/retry"; export async function deployRootContracts() { // Check environment variables @@ -17,7 +18,7 @@ export async function deployRootContracts() { // Read from contract file. let rootContracts = getRootContracts(); - const rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); + const rootProvider = new RetryProvider(rootRPCURL, Number(rootChainID)); // Get deployer address let rootDeployerWallet; diff --git a/scripts/deploy/root_initialisation.ts b/scripts/deploy/root_initialisation.ts index 17d1c4d9..8e7bf7d2 100644 --- a/scripts/deploy/root_initialisation.ts +++ b/scripts/deploy/root_initialisation.ts @@ -4,6 +4,7 @@ dotenv.config(); import { ethers, utils } from "ethers"; import { requireEnv, waitForConfirmation, waitForReceipt, getContract, getChildContracts, getRootContracts } from "../helpers/helpers"; import { LedgerSigner } from "../helpers/ledger_signer"; +import { RetryProvider } from "../helpers/retry"; export async function initialiseRootContracts() { // Check environment variables @@ -50,7 +51,7 @@ export async function initialiseRootContracts() { let rootTemplateAddr = rootContracts.ROOT_TOKEN_TEMPLATE; // Get deployer address - const rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); + const rootProvider = new RetryProvider(rootRPCURL, Number(rootChainID)); let rootDeployerWallet; if (deployerSecret == "ledger") { let index = requireEnv("DEPLOYER_LEDGER_INDEX"); diff --git a/scripts/e2e/e2e.ts b/scripts/e2e/e2e.ts index 4ef15db9..7cf347da 100644 --- a/scripts/e2e/e2e.ts +++ b/scripts/e2e/e2e.ts @@ -4,6 +4,7 @@ dotenv.config(); import { ethers, providers } from "ethers"; import { requireEnv, waitForReceipt, getFee, getContract, delay, getChildContracts, getRootContracts, saveChildContracts, waitUntilSucceed } from "../helpers/helpers"; import { expect } from "chai"; +import { RetryProvider } from "../helpers/retry"; // The contract ABI of IMX on L1. const IMX_ABI = `[{"inputs":[{"internalType":"address","name":"minter","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]`; @@ -42,8 +43,8 @@ describe("Bridge e2e test", () => { let rootBridgeAddr = rootContracts.ROOT_BRIDGE_ADDRESS; let rootCustomTokenAddr = rootContracts.ROOT_TEST_CUSTOM_TOKEN; - rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); - childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); + rootProvider = new RetryProvider(rootRPCURL, Number(rootChainID)); + childProvider = new RetryProvider(childRPCURL, Number(childChainID)); rootTestWallet = new ethers.Wallet(testAccountKey, rootProvider); childTestWallet = new ethers.Wallet(testAccountKey, childProvider); diff --git a/scripts/helpers/helpers.ts b/scripts/helpers/helpers.ts index 015478b2..c0ed555e 100644 --- a/scripts/helpers/helpers.ts +++ b/scripts/helpers/helpers.ts @@ -196,11 +196,15 @@ export async function verifyChildContract(contract: string, contractAddr: string return; } let cmd = `forge verify-contract --verifier blockscout --verifier-url ${url} ${contractAddr} ${contract}`; - const { stdout, stderr } = await exec(cmd); - if (stderr != "") { - throw(stderr); + try { + const { stdout, stderr } = await exec(cmd); + if (stderr != "") { + console.log(stderr); + } + console.log(stdout); + } catch (e) { + console.log(e); } - console.log(stdout); } export async function verifyRootContract(contract: string, contractAddr: string, args: string | null) { @@ -215,9 +219,13 @@ export async function verifyRootContract(contract: string, contractAddr: string, if (args != null) { cmd += ` --constructor-args $(cast abi-encode ${args})` } - const { stdout, stderr } = await exec(cmd); - if (stderr != "") { - throw(stderr); + try { + const { stdout, stderr } = await exec(cmd); + if (stderr != "") { + console.log(stderr); + } + console.log(stdout); + } catch (e) { + console.log(e); } - console.log(stdout); } \ No newline at end of file diff --git a/scripts/helpers/retry.ts b/scripts/helpers/retry.ts new file mode 100644 index 00000000..9e0d4cb5 --- /dev/null +++ b/scripts/helpers/retry.ts @@ -0,0 +1,39 @@ +import { providers, utils } from "ethers"; + +const MAX_ATTEMPT = 20; + +export class RetryProvider extends providers.JsonRpcProvider { + + constructor( + url?: utils.ConnectionInfo | string, + network?: providers.Networkish + ) { + super(url, network); + } + + public perform(method: string, params: any) { + let attempts = 0; + return utils.poll(() => { + if (attempts != 0) { + console.log("Retry RPC Request: " + attempts); + } + attempts++; + return super.perform(method, params) + .then(result => { + return result; + }, (error: any) => { + if (error.statusCode !== 429) { + return Promise.reject(error); + } else { + return Promise.resolve(undefined); + } + }) + .catch(error => { + console.log(error); + return Promise.resolve(undefined); + }) + }, { + retryLimit: MAX_ATTEMPT, + }); + } +} diff --git a/scripts/localdev/axelar_setup.ts b/scripts/localdev/axelar_setup.ts index b8b0c6f1..4450f3bd 100644 --- a/scripts/localdev/axelar_setup.ts +++ b/scripts/localdev/axelar_setup.ts @@ -4,6 +4,7 @@ import { Network, networks, EvmRelayer, relay } from '@axelar-network/axelar-loc import { requireEnv, waitForReceipt } from "../helpers/helpers"; import { ethers } from "ethers"; import * as fs from "fs"; +import { RetryProvider } from "../helpers/retry"; let relaying = false; const defaultEvmRelayer = new EvmRelayer(); @@ -20,7 +21,7 @@ async function main() { let axelarDeployerKey = requireEnv("AXELAR_DEPLOYER_SECRET"); // Create root chain. - let rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); + let rootProvider = new RetryProvider(rootRPCURL, Number(rootChainID)); let rootChain = new Network(); rootChain.name = rootChainName; rootChain.chainId = Number(rootChainID); @@ -45,7 +46,7 @@ async function main() { rootChain.lastExpressedBlock = rootChain.lastRelayedBlock; // Create child chain. - let childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); + let childProvider = new RetryProvider(childRPCURL, Number(childChainID)); let childChain = new Network(); childChain.name = childChainName; childChain.chainId = Number(childChainID); diff --git a/scripts/localdev/childchain_setup.ts b/scripts/localdev/childchain_setup.ts index 93a0abd1..6a0db0a8 100644 --- a/scripts/localdev/childchain_setup.ts +++ b/scripts/localdev/childchain_setup.ts @@ -3,6 +3,7 @@ dotenv.config(); import { ethers as hardhat } from "hardhat"; import { ethers } from "ethers"; import { requireEnv } from "../helpers/helpers"; +import { RetryProvider } from "../helpers/retry"; async function main() { let childRPCURL = requireEnv("CHILD_RPC_URL"); @@ -10,7 +11,7 @@ async function main() { let deployerAddr = requireEnv("DEPLOYER_ADDR"); // Get child provider. - let childProvider = new ethers.providers.JsonRpcProvider(childRPCURL, Number(childChainID)); + let childProvider = new RetryProvider(childRPCURL, Number(childChainID)); // Give admin EOA account 2B IMX. await hardhat.provider.send("hardhat_setBalance", [ diff --git a/scripts/localdev/rootchain_setup.ts b/scripts/localdev/rootchain_setup.ts index abaaa503..c31fe00f 100644 --- a/scripts/localdev/rootchain_setup.ts +++ b/scripts/localdev/rootchain_setup.ts @@ -4,6 +4,7 @@ import { ethers as hardhat } from "hardhat"; import { ethers } from "ethers"; import { requireEnv, deployRootContract, waitForReceipt, saveRootContracts } from "../helpers/helpers"; import * as fs from "fs"; +import { RetryProvider } from "../helpers/retry"; async function main() { let rootRPCURL = requireEnv("ROOT_RPC_URL"); @@ -15,7 +16,7 @@ async function main() { let rootTestKey = requireEnv("TEST_ACCOUNT_SECRET"); // Get root provider. - let rootProvider = new ethers.providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); + let rootProvider = new RetryProvider(rootRPCURL, Number(rootChainID)); // Get test wwallet on the root chain. let testWallet = new ethers.Wallet(rootTestKey, rootProvider); From 6f5d84ad9325f73a49a3140eacb78fc36d7cded7 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Tue, 19 Dec 2023 11:22:57 +0800 Subject: [PATCH 26/40] Update rate configuration --- scripts/bootstrap/0_pre_validation.ts | 24 +++--- scripts/deploy/root_initialisation.ts | 108 +++++++++++++------------- 2 files changed, 66 insertions(+), 66 deletions(-) diff --git a/scripts/bootstrap/0_pre_validation.ts b/scripts/bootstrap/0_pre_validation.ts index 34b5dd42..d909b1ec 100644 --- a/scripts/bootstrap/0_pre_validation.ts +++ b/scripts/bootstrap/0_pre_validation.ts @@ -54,18 +54,18 @@ async function run() { requireEnv("RATE_LIMIT_USDC_CAPACITY"); requireEnv("RATE_LIMIT_USDC_REFILL_RATE"); requireEnv("RATE_LIMIT_USDC_LARGE_THRESHOLD"); - requireEnv("RATE_LIMIT_GU_ADDR"); - requireEnv("RATE_LIMIT_GU_CAPACITY"); - requireEnv("RATE_LIMIT_GU_REFILL_RATE"); - requireEnv("RATE_LIMIT_GU_LARGE_THRESHOLD"); - requireEnv("RATE_LIMIT_CHECKMATE_ADDR"); - requireEnv("RATE_LIMIT_CHECKMATE_CAPACITY"); - requireEnv("RATE_LIMIT_CHECKMATE_REFILL_RATE"); - requireEnv("RATE_LIMIT_CHECKMATE_LARGE_THRESHOLD"); - requireEnv("RATE_LIMIT_GOG_ADDR"); - requireEnv("RATE_LIMIT_GOG_CAPACITY"); - requireEnv("RATE_LIMIT_GOG_REFILL_RATE"); - requireEnv("RATE_LIMIT_GOG_LARGE_THRESHOLD"); + // requireEnv("RATE_LIMIT_GU_ADDR"); + // requireEnv("RATE_LIMIT_GU_CAPACITY"); + // requireEnv("RATE_LIMIT_GU_REFILL_RATE"); + // requireEnv("RATE_LIMIT_GU_LARGE_THRESHOLD"); + // requireEnv("RATE_LIMIT_CHECKMATE_ADDR"); + // requireEnv("RATE_LIMIT_CHECKMATE_CAPACITY"); + // requireEnv("RATE_LIMIT_CHECKMATE_REFILL_RATE"); + // requireEnv("RATE_LIMIT_CHECKMATE_LARGE_THRESHOLD"); + // requireEnv("RATE_LIMIT_GOG_ADDR"); + // requireEnv("RATE_LIMIT_GOG_CAPACITY"); + // requireEnv("RATE_LIMIT_GOG_REFILL_RATE"); + // requireEnv("RATE_LIMIT_GOG_LARGE_THRESHOLD"); const childProvider = new RetryProvider(childRPCURL, Number(childChainID)); const rootProvider = new RetryProvider(rootRPCURL, Number(rootChainID)); diff --git a/scripts/deploy/root_initialisation.ts b/scripts/deploy/root_initialisation.ts index 8e7bf7d2..1d550ffe 100644 --- a/scripts/deploy/root_initialisation.ts +++ b/scripts/deploy/root_initialisation.ts @@ -28,18 +28,18 @@ export async function initialiseRootContracts() { let rateLimitUSDCCap = requireEnv("RATE_LIMIT_USDC_CAPACITY"); let rateLimitUSDCRefill = requireEnv("RATE_LIMIT_USDC_REFILL_RATE"); let rateLimitUSDCLargeThreshold = requireEnv("RATE_LIMIT_USDC_LARGE_THRESHOLD"); - let rateLimitGUAddr = requireEnv("RATE_LIMIT_GU_ADDR"); - let rateLimitGUCap = requireEnv("RATE_LIMIT_GU_CAPACITY"); - let rateLimitGURefill = requireEnv("RATE_LIMIT_GU_REFILL_RATE"); - let rateLimitGULargeThreshold = requireEnv("RATE_LIMIT_GU_LARGE_THRESHOLD"); - let rateLimitCheckMateAddr = requireEnv("RATE_LIMIT_CHECKMATE_ADDR"); - let rateLimitCheckMateCap = requireEnv("RATE_LIMIT_CHECKMATE_CAPACITY"); - let rateLimitCheckMateRefill = requireEnv("RATE_LIMIT_CHECKMATE_REFILL_RATE"); - let rateLimitCheckMateLargeThreshold = requireEnv("RATE_LIMIT_CHECKMATE_LARGE_THRESHOLD"); - let rateLimitGOGAddr = requireEnv("RATE_LIMIT_GOG_ADDR"); - let rateLimitGOGCap = requireEnv("RATE_LIMIT_GOG_CAPACITY"); - let rateLimitGOGRefill = requireEnv("RATE_LIMIT_GOG_REFILL_RATE"); - let rateLimitGOGLargeThreshold = requireEnv("RATE_LIMIT_GOG_LARGE_THRESHOLD"); + // let rateLimitGUAddr = requireEnv("RATE_LIMIT_GU_ADDR"); + // let rateLimitGUCap = requireEnv("RATE_LIMIT_GU_CAPACITY"); + // let rateLimitGURefill = requireEnv("RATE_LIMIT_GU_REFILL_RATE"); + // let rateLimitGULargeThreshold = requireEnv("RATE_LIMIT_GU_LARGE_THRESHOLD"); + // let rateLimitCheckMateAddr = requireEnv("RATE_LIMIT_CHECKMATE_ADDR"); + // let rateLimitCheckMateCap = requireEnv("RATE_LIMIT_CHECKMATE_CAPACITY"); + // let rateLimitCheckMateRefill = requireEnv("RATE_LIMIT_CHECKMATE_REFILL_RATE"); + // let rateLimitCheckMateLargeThreshold = requireEnv("RATE_LIMIT_CHECKMATE_LARGE_THRESHOLD"); + // let rateLimitGOGAddr = requireEnv("RATE_LIMIT_GOG_ADDR"); + // let rateLimitGOGCap = requireEnv("RATE_LIMIT_GOG_CAPACITY"); + // let rateLimitGOGRefill = requireEnv("RATE_LIMIT_GOG_REFILL_RATE"); + // let rateLimitGOGLargeThreshold = requireEnv("RATE_LIMIT_GOG_LARGE_THRESHOLD"); // Read from contract file. let childContracts = getChildContracts(); @@ -138,50 +138,50 @@ export async function initialiseRootContracts() { await waitForReceipt(resp.hash, rootProvider); } - // GU - if ((await rootBridge.largeTransferThresholds(rateLimitGUAddr)).toString() != "0") { - console.log("GU rate limiting has already been configured, skip."); - } else { - console.log("Configure rate limiting for GU...") - let resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( - rateLimitGUAddr, - ethers.utils.parseEther(rateLimitGUCap), - ethers.utils.parseEther(rateLimitGURefill), - ethers.utils.parseEther(rateLimitGULargeThreshold) - ); - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, rootProvider); - } + // // GU + // if ((await rootBridge.largeTransferThresholds(rateLimitGUAddr)).toString() != "0") { + // console.log("GU rate limiting has already been configured, skip."); + // } else { + // console.log("Configure rate limiting for GU...") + // let resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( + // rateLimitGUAddr, + // ethers.utils.parseEther(rateLimitGUCap), + // ethers.utils.parseEther(rateLimitGURefill), + // ethers.utils.parseEther(rateLimitGULargeThreshold) + // ); + // console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + // await waitForReceipt(resp.hash, rootProvider); + // } - // Checkmate - if ((await rootBridge.largeTransferThresholds(rateLimitCheckMateAddr)).toString() != "0") { - console.log("CheckMate rate limiting has already been configured, skip."); - } else { - console.log("Configure rate limiting for CheckMate...") - let resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( - rateLimitCheckMateAddr, - ethers.utils.parseEther(rateLimitCheckMateCap), - ethers.utils.parseEther(rateLimitCheckMateRefill), - ethers.utils.parseEther(rateLimitCheckMateLargeThreshold) - ); - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, rootProvider); - } + // // Checkmate + // if ((await rootBridge.largeTransferThresholds(rateLimitCheckMateAddr)).toString() != "0") { + // console.log("CheckMate rate limiting has already been configured, skip."); + // } else { + // console.log("Configure rate limiting for CheckMate...") + // let resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( + // rateLimitCheckMateAddr, + // ethers.utils.parseEther(rateLimitCheckMateCap), + // ethers.utils.parseEther(rateLimitCheckMateRefill), + // ethers.utils.parseEther(rateLimitCheckMateLargeThreshold) + // ); + // console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + // await waitForReceipt(resp.hash, rootProvider); + // } - // GOG - if ((await rootBridge.largeTransferThresholds(rateLimitGOGAddr)).toString() != "0") { - console.log("GOG rate limiting has already been configured, skip."); - } else { - console.log("Configure rate limiting for GOG...") - let resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( - rateLimitGOGAddr, - ethers.utils.parseEther(rateLimitGOGCap), - ethers.utils.parseEther(rateLimitGOGRefill), - ethers.utils.parseEther(rateLimitGOGLargeThreshold) - ); - console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); - await waitForReceipt(resp.hash, rootProvider); - } + // // GOG + // if ((await rootBridge.largeTransferThresholds(rateLimitGOGAddr)).toString() != "0") { + // console.log("GOG rate limiting has already been configured, skip."); + // } else { + // console.log("Configure rate limiting for GOG...") + // let resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( + // rateLimitGOGAddr, + // ethers.utils.parseEther(rateLimitGOGCap), + // ethers.utils.parseEther(rateLimitGOGRefill), + // ethers.utils.parseEther(rateLimitGOGLargeThreshold) + // ); + // console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); + // await waitForReceipt(resp.hash, rootProvider); + // } // Grant roles if (await rootBridge.hasRole(utils.keccak256(utils.toUtf8Bytes("RATE")), rootPrivilegedMultisig)) { From 49f7af91516a587e122352f50adb2563600f3e29 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Tue, 19 Dec 2023 12:22:11 +0800 Subject: [PATCH 27/40] Fix rate limit for USDC --- scripts/deploy/root_initialisation.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/deploy/root_initialisation.ts b/scripts/deploy/root_initialisation.ts index 1d550ffe..4f826bf5 100644 --- a/scripts/deploy/root_initialisation.ts +++ b/scripts/deploy/root_initialisation.ts @@ -130,9 +130,9 @@ export async function initialiseRootContracts() { console.log("Configure rate limiting for USDC...") let resp = await rootBridge.connect(rootDeployerWallet).setRateControlThreshold( rateLimitUSDCAddr, - ethers.utils.parseEther(rateLimitUSDCCap), - ethers.utils.parseEther(rateLimitUSDCRefill), - ethers.utils.parseEther(rateLimitUSDCLargeThreshold) + ethers.utils.parseUnits(rateLimitUSDCCap, 6), + ethers.utils.parseUnits(rateLimitUSDCRefill, 6), + ethers.utils.parseUnits(rateLimitUSDCLargeThreshold, 6) ); console.log("Transaction submitted: ", JSON.stringify(resp, null, 2)); await waitForReceipt(resp.hash, rootProvider); From b8a2603d6e20a852ebc6d9a434e880b2354d51be Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Mon, 29 Jan 2024 12:50:15 +1000 Subject: [PATCH 28/40] Add more e2e test cases --- package.json | 1 + scripts/e2e/e2e.ts | 1678 +++++++++++++++++++++++++-- scripts/localdev/.env.local | 4 +- scripts/localdev/rootchain_setup.ts | 10 +- yarn.lock | 2 +- 5 files changed, 1598 insertions(+), 97 deletions(-) diff --git a/package.json b/package.json index fd4617d7..aa213c2b 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@ledgerhq/hw-app-eth": "^6.35.0", "@ledgerhq/hw-transport-node-hid": "^6.28.0", "@openzeppelin/contracts": "^4.5.0", + "@types/chai-as-promised": "^7.1.8", "axios": "^0.27.2", "bip39": "^3.0.4", "config": "^3.3.9", diff --git a/scripts/e2e/e2e.ts b/scripts/e2e/e2e.ts index 7cf347da..7e0175bb 100644 --- a/scripts/e2e/e2e.ts +++ b/scripts/e2e/e2e.ts @@ -3,8 +3,9 @@ import * as dotenv from "dotenv"; dotenv.config(); import { ethers, providers } from "ethers"; import { requireEnv, waitForReceipt, getFee, getContract, delay, getChildContracts, getRootContracts, saveChildContracts, waitUntilSucceed } from "../helpers/helpers"; -import { expect } from "chai"; -import { RetryProvider } from "../helpers/retry"; +import * as chai from "chai"; +chai.use(require('chai-as-promised')); +const { expect } = chai; // The contract ABI of IMX on L1. const IMX_ABI = `[{"inputs":[{"internalType":"address","name":"minter","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]`; @@ -14,6 +15,10 @@ describe("Bridge e2e test", () => { let childProvider: providers.JsonRpcProvider; let rootTestWallet: ethers.Wallet; let childTestWallet: ethers.Wallet; + let rootPauserWallet: ethers.Wallet; + let childPauserWallet: ethers.Wallet; + let rootPrivilegedWallet: ethers.Wallet; + let childPrivilegedWallet: ethers.Wallet; let rootBridge: ethers.Contract; let rootWETH: ethers.Contract; let rootIMX: ethers.Contract; @@ -30,6 +35,8 @@ describe("Bridge e2e test", () => { let rootChainID = requireEnv("ROOT_CHAIN_ID"); let childRPCURL = requireEnv("CHILD_RPC_URL"); let childChainID = requireEnv("CHILD_CHAIN_ID"); + let testBreakGlassKey = requireEnv("BREAKGLASS_EOA_SECRET"); + let testPriviledgeKey = requireEnv("PRIVILEGED_EOA_SECRET"); let testAccountKey = requireEnv("TEST_ACCOUNT_SECRET"); let rootIMXAddr = requireEnv("ROOT_IMX_ADDR"); let rootWETHAddr = requireEnv("ROOT_WETH_ADDR"); @@ -43,10 +50,14 @@ describe("Bridge e2e test", () => { let rootBridgeAddr = rootContracts.ROOT_BRIDGE_ADDRESS; let rootCustomTokenAddr = rootContracts.ROOT_TEST_CUSTOM_TOKEN; - rootProvider = new RetryProvider(rootRPCURL, Number(rootChainID)); - childProvider = new RetryProvider(childRPCURL, Number(childChainID)); + rootProvider = new providers.JsonRpcProvider(rootRPCURL, Number(rootChainID)); + childProvider = new providers.JsonRpcProvider(childRPCURL, Number(childChainID)); rootTestWallet = new ethers.Wallet(testAccountKey, rootProvider); childTestWallet = new ethers.Wallet(testAccountKey, childProvider); + rootPauserWallet = new ethers.Wallet(testBreakGlassKey, rootProvider); + childPauserWallet = new ethers.Wallet(testBreakGlassKey, childProvider); + rootPrivilegedWallet = new ethers.Wallet(testPriviledgeKey, rootProvider); + childPrivilegedWallet = new ethers.Wallet(testPriviledgeKey, childProvider); rootBridge = getContract("RootERC20BridgeFlowRate", rootBridgeAddr, rootProvider); rootWETH = getContract("WETH", rootWETHAddr, rootProvider); @@ -57,12 +68,101 @@ describe("Bridge e2e test", () => { childWIMX = getContract("WIMX", childWIMXAddr, childProvider); }) + it("should not deposit IMX if allowance is insufficient", async() => { + let amt = ethers.utils.parseEther("50.0"); + let bridgeFee = ethers.utils.parseEther("0.001"); + + // Approve + let resp = await rootIMX.connect(rootTestWallet).approve(rootBridge.address, amt.sub(1)); + await waitForReceipt(resp.hash, rootProvider); + + // Fail to deposit on L1 + await expect(rootBridge.connect(rootTestWallet).deposit(rootIMX.address, amt, { + value: bridgeFee, + })).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); + }).timeout(2400000) + + it("should not deposit IMX if balance is insufficient", async() => { + let balance = await rootIMX.balanceOf(rootTestWallet.address); + + let amt = balance.add(1); + let bridgeFee = ethers.utils.parseEther("0.001"); + + // Approve + let resp = await rootIMX.connect(rootTestWallet).approve(rootBridge.address, amt); + await waitForReceipt(resp.hash, rootProvider); + + // Fail to deposit on L1 + await expect(rootBridge.connect(rootTestWallet).deposit(rootIMX.address, amt, { + value: bridgeFee, + })).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); + }).timeout(2400000) + + // Local only + it("should not deposit IMX if root bridge is paused", async() => { + // Transfer 0.1 ETH to root pauser + let resp = await rootTestWallet.sendTransaction({ + to: rootPauserWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, rootProvider); + + // Transfer 0.1 ETH to root unpauser + resp = await rootTestWallet.sendTransaction({ + to: rootPrivilegedWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, rootProvider); + + // Pause root bridge + if (!await rootBridge.paused()) { + resp = await rootBridge.connect(rootPauserWallet).pause(); + await waitForReceipt(resp.hash, rootProvider); + expect(await rootBridge.paused()).to.true; + } + + // Try to deposit. + let amt = ethers.utils.parseEther("10.0"); + let bridgeFee = ethers.utils.parseEther("0.001"); + + // Approve + resp = await rootIMX.connect(rootTestWallet).approve(rootBridge.address, amt); + await waitForReceipt(resp.hash, rootProvider); + + // Fail to deposit on L1 + await expect(rootBridge.connect(rootTestWallet).deposit(rootIMX.address, amt, { + value: bridgeFee, + })).to.be.rejectedWith("Pausable: paused"); + + // Unpause root bridge + resp = await rootBridge.connect(rootPrivilegedWallet).unpause(); + await waitForReceipt(resp.hash, rootProvider); + expect(await rootBridge.paused()).to.false; + }).timeout(2400000) + + // Local only + it("should not deposit IMX if deposit limit is reached", async() => { + let limit = ethers.utils.parseEther("100000000.0"); + + let amt = limit.add(1); + let bridgeFee = ethers.utils.parseEther("0.001"); + + // Approve + let resp = await rootIMX.connect(rootTestWallet).approve(rootBridge.address, amt); + await waitForReceipt(resp.hash, rootProvider); + + // Fail to deposit on L1 + await expect(rootBridge.connect(rootTestWallet).deposit(rootIMX.address, amt, { + value: bridgeFee, + })).to.be.rejectedWith(rootBridge.interface.getSighash('ImxDepositLimitExceeded()')); + }).timeout(2400000) + it("should successfully deposit IMX to self from L1 to L2", async() => { // Get IMX balance on root & child chains before deposit let preBalL1 = await rootIMX.balanceOf(rootTestWallet.address); let preBalL2 = await childProvider.getBalance(childTestWallet.address); - let amt = ethers.utils.parseEther("10.0"); + let amt = ethers.utils.parseEther("50.0"); let bridgeFee = ethers.utils.parseEther("0.001"); // Approve @@ -92,6 +192,155 @@ describe("Bridge e2e test", () => { expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); }).timeout(2400000) + it("should successfully deposit IMX to others from L1 to L2", async() => { + let childRecipient = childPrivilegedWallet.address; + // Get IMX balance on root & child chains before deposit + let preBalL1 = await rootIMX.balanceOf(rootTestWallet.address); + let preBalL2 = await childProvider.getBalance(childRecipient); + + let amt = ethers.utils.parseEther("50.0"); + let bridgeFee = ethers.utils.parseEther("0.001"); + + // Approve + let resp = await rootIMX.connect(rootTestWallet).approve(rootBridge.address, amt); + await waitForReceipt(resp.hash, rootProvider); + + // IMX deposit L1 to L2 + resp = await rootBridge.connect(rootTestWallet).depositTo(rootIMX.address, childRecipient, amt, { + value: bridgeFee, + }); + await waitForReceipt(resp.hash, rootProvider); + + let postBalL1 = await rootIMX.balanceOf(rootTestWallet.address); + let postBalL2 = preBalL2; + + await waitUntilSucceed(axelarAPI, resp.hash); + + while (postBalL2.eq(preBalL2)) { + postBalL2 = await childProvider.getBalance(childRecipient); + await delay(1000); + } + + // Verify + let expectedPostL1 = preBalL1.sub(amt); + let expectedPostL2 = preBalL2.add(amt); + expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); + expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); + }).timeout(2400000) + + // Local only + it("should not deposit IMX on L2 if child bridge is paused", async() => { + // Transfer 0.1 IMX to child pauser + let resp = await childTestWallet.sendTransaction({ + to: childPauserWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, childProvider); + + // Transfer 0.1 IMX to child unpauser + resp = await childTestWallet.sendTransaction({ + to: childPrivilegedWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, childProvider); + + // Pause child bridge + if (!await childBridge.paused()) { + resp = await childBridge.connect(childPauserWallet).pause(); + await waitForReceipt(resp.hash, childProvider); + expect(await childBridge.paused()).to.true; + } + + // Get IMX balance on root & child chains before deposit + let preBalL1 = await rootIMX.balanceOf(rootTestWallet.address); + let preBalL2 = await childProvider.getBalance(childTestWallet.address); + + let amt = ethers.utils.parseEther("10.0"); + let bridgeFee = ethers.utils.parseEther("0.001"); + + // Approve + resp = await rootIMX.connect(rootTestWallet).approve(rootBridge.address, amt); + await waitForReceipt(resp.hash, rootProvider); + + // Try to deposit + resp = await rootBridge.connect(rootTestWallet).deposit(rootIMX.address, amt, { + value: bridgeFee, + }); + await waitForReceipt(resp.hash, rootProvider); + await waitUntilSucceed(axelarAPI, resp.hash); + + // Balance on L2 should not change. + await delay(10000); + let postBalL1 = await rootIMX.balanceOf(rootTestWallet.address); + let postBalL2 = await childProvider.getBalance(childTestWallet.address); + + // Verify + let expectedPostL1 = preBalL1.sub(amt); + let expectedPostL2 = preBalL2; + expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); + expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); + + // Unpause child bridge + resp = await childBridge.connect(childPrivilegedWallet).unpause(); + await waitForReceipt(resp.hash, childProvider); + expect(await childBridge.paused()).to.false; + }).timeout(2400000) + + // Local only + it("should not withdraw IMX if child bridge is paused", async() => { + // Transfer 0.1 IMX to child pauser + let resp = await childTestWallet.sendTransaction({ + to: childPauserWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, childProvider); + + // Transfer 0.1 IMX to child unpauser + resp = await childTestWallet.sendTransaction({ + to: childPrivilegedWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, childProvider); + + // Pause child bridge + if (!await childBridge.paused()) { + resp = await childBridge.connect(childPauserWallet).pause(); + await waitForReceipt(resp.hash, childProvider); + expect(await childBridge.paused()).to.true; + } + + let amt = ethers.utils.parseEther("1.0"); + let bridgeFee = ethers.utils.parseEther("1.0"); + + // IMX withdraw L2 to L1 + let [priorityFee, maxFee] = await getFee(childProvider); + await expect(childBridge.connect(childTestWallet).withdrawIMX(amt, { + value: amt.add(bridgeFee), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + })).to.be.rejectedWith("Pausable: paused"); + + // Unpause child bridge + resp = await childBridge.connect(childPrivilegedWallet).unpause(); + await waitForReceipt(resp.hash, childProvider); + expect(await childBridge.paused()).to.false; + }).timeout(2400000) + + it("should not withdraw IMX if balance is insufficient", async() => { + let balance = await childProvider.getBalance(childTestWallet.address); + + let amt = balance; + let bridgeFee = ethers.utils.parseEther("1.0"); + + // IMX withdraw L2 to L1 + let [priorityFee, maxFee] = await getFee(childProvider); + await expect(childBridge.connect(childTestWallet).withdrawIMX(amt, { + value: amt.add(bridgeFee), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + })).to.be.rejectedWith("sender doesn't have enough funds to send tx"); + }).timeout(2400000) + it("should successfully withdraw IMX to self from L2 to L1", async() => { // Get IMX balance on root & child chains before withdraw let preBalL1 = await rootIMX.balanceOf(rootTestWallet.address); @@ -128,163 +377,1104 @@ describe("Bridge e2e test", () => { expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); }).timeout(2400000) - it("should successfully withdraw wIMX to self from L2 to L1", async() => { - // Wrap 1 IMX - let [priorityFee, maxFee] = await getFee(childProvider); - let resp = await childWIMX.connect(childTestWallet).deposit({ - value: ethers.utils.parseEther("1.0"), - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); - await waitForReceipt(resp.hash, childProvider); - + it("should successfully withdraw IMX to others from L2 to L1", async() => { + let rootRecipient = rootPrivilegedWallet.address; // Get IMX balance on root & child chains before withdraw - let preBalL1 = await rootIMX.balanceOf(rootTestWallet.address); - let preBalL2 = await childWIMX.balanceOf(childTestWallet.address); + let preBalL1 = await rootIMX.balanceOf(rootRecipient); + let preBalL2 = await childProvider.getBalance(childTestWallet.address); - let amt = ethers.utils.parseEther("0.5"); + let amt = ethers.utils.parseEther("1.0"); let bridgeFee = ethers.utils.parseEther("1.0"); - // Approve - [priorityFee, maxFee] = await getFee(childProvider); - resp = await childWIMX.connect(childTestWallet).approve(childBridge.address, amt, { - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); - await waitForReceipt(resp.hash, childProvider); - - // wIMX withdraw L2 to L1 - [priorityFee, maxFee] = await getFee(childProvider); - resp = await childBridge.connect(childTestWallet).withdrawWIMX(amt, { - value: bridgeFee, + // IMX withdraw L2 to L1 + let [priorityFee, maxFee] = await getFee(childProvider); + let resp = await childBridge.connect(childTestWallet).withdrawIMXTo(rootRecipient, amt, { + value: amt.add(bridgeFee), maxPriorityFeePerGas: priorityFee, maxFeePerGas: maxFee, }); await waitForReceipt(resp.hash, childProvider); let postBalL1 = preBalL1; - let postBalL2 = await childWIMX.balanceOf(childTestWallet.address); + let postBalL2 = await childProvider.getBalance(childTestWallet.address); await waitUntilSucceed(axelarAPI, resp.hash); while (postBalL1.eq(preBalL1)) { - postBalL1 = await rootIMX.balanceOf(rootTestWallet.address); + postBalL1 = await rootIMX.balanceOf(rootRecipient); await delay(1000); } // Verify + let receipt = await childProvider.getTransactionReceipt(resp.hash); + let txFee = receipt.gasUsed.mul(receipt.effectiveGasPrice); let expectedPostL1 = preBalL1.add(amt); - let expectedPostL2 = preBalL2.sub(amt); + let expectedPostL2 = preBalL2.sub(txFee).sub(amt).sub(bridgeFee); expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); }).timeout(2400000) - it("should successfully deposit ETH to self from L1 to L2", async() => { - // Get ETH balance on root & child chains before deposit - let preBalL1 = await rootProvider.getBalance(rootTestWallet.address); - let preBalL2 = await childETH.balanceOf(childTestWallet.address); - - let amt = ethers.utils.parseEther("0.001"); - let bridgeFee = ethers.utils.parseEther("0.001"); + // Local only + it("should not withdraw IMX on L1 if root bridge is paused", async() => { + // Transfer 0.1 ETH to root pauser + let resp = await rootTestWallet.sendTransaction({ + to: rootPauserWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, rootProvider); - // ETH deposit L1 to L2 - let resp = await rootBridge.connect(rootTestWallet).depositETH(amt, { - value: amt.add(bridgeFee), - }); + // Transfer 0.1 ETH to root unpauser + resp = await rootTestWallet.sendTransaction({ + to: rootPrivilegedWallet.address, + value: ethers.utils.parseEther("0.1"), + }) await waitForReceipt(resp.hash, rootProvider); - let postBalL1 = await rootProvider.getBalance(rootTestWallet.address); - let postBalL2 = preBalL2; + // Pause root bridge + if (!await rootBridge.paused()) { + resp = await rootBridge.connect(rootPauserWallet).pause(); + await waitForReceipt(resp.hash, rootProvider); + expect(await rootBridge.paused()).to.true; + } + + // Get IMX balance on root & child chains before withdraw + let preBalL1 = await rootIMX.balanceOf(rootTestWallet.address); + let preBalL2 = await childProvider.getBalance(childTestWallet.address); + let amt = ethers.utils.parseEther("1.0"); + let bridgeFee = ethers.utils.parseEther("1.0"); + + // IMX withdraw L2 to L1 + let [priorityFee, maxFee] = await getFee(childProvider); + resp = await childBridge.connect(childTestWallet).withdrawIMX(amt, { + value: amt.add(bridgeFee), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); await waitUntilSucceed(axelarAPI, resp.hash); - while (postBalL2.eq(preBalL2)) { - postBalL2 = await childETH.balanceOf(childTestWallet.address); - await delay(1000); - } + // Balance on L1 should not change. + await delay(10000); + let postBalL1 = await rootIMX.balanceOf(rootTestWallet.address); + let postBalL2 = await childProvider.getBalance(childTestWallet.address); // Verify - let receipt = await rootProvider.getTransactionReceipt(resp.hash); + let receipt = await childProvider.getTransactionReceipt(resp.hash); let txFee = receipt.gasUsed.mul(receipt.effectiveGasPrice); - let expectedPostL1 = preBalL1.sub(txFee).sub(amt).sub(bridgeFee); - let expectedPostL2 = preBalL2.add(amt); + let expectedPostL1 = preBalL1; + let expectedPostL2 = preBalL2.sub(txFee).sub(amt).sub(bridgeFee); expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); + + // Unpause root bridge + resp = await rootBridge.connect(rootPrivilegedWallet).unpause(); + await waitForReceipt(resp.hash, rootProvider); + expect(await rootBridge.paused()).to.false; }).timeout(2400000) - it("should successfully deposit wETH to self from L1 to L2", async() => { - // Wrap 0.01 ETH - let resp = await rootWETH.connect(rootTestWallet).deposit({ - value: ethers.utils.parseEther("0.01"), - }) + // Local only + it("should put IMX withdrawal in pending when violating rate limit policy", async() => { + // Set new rate limit + let resp = await rootBridge.connect(rootPrivilegedWallet).setRateControlThreshold(rootIMX.address, ethers.utils.parseEther("2.016"), ethers.utils.parseEther("0.00056"), ethers.utils.parseEther("1.008")); await waitForReceipt(resp.hash, rootProvider); - // Get ETH balance on root & child chains before withdraw - let preBalL1 = await rootWETH.balanceOf(rootTestWallet.address); - let preBalL2 = await childETH.balanceOf(childTestWallet.address); + // Withdraw of IMX exceeding large threshold + // Get IMX balance on root & child chains before withdraw + let preBalL1 = await rootIMX.balanceOf(rootTestWallet.address); + let preBalL2 = await childProvider.getBalance(childTestWallet.address); + let preLength = await rootBridge.getPendingWithdrawalsLength(rootTestWallet.address); - let amt = ethers.utils.parseEther("0.001"); - let bridgeFee = ethers.utils.parseEther("0.001"); + let amt1 = ethers.utils.parseEther("1.1"); + let bridgeFee1 = ethers.utils.parseEther("1.0"); - // Approve - resp = await rootWETH.connect(rootTestWallet).approve(rootBridge.address, amt); - await waitForReceipt(resp.hash, rootProvider); + // IMX withdraw L2 to L1 + let [priorityFee, maxFee] = await getFee(childProvider); + resp = await childBridge.connect(childTestWallet).withdrawIMX(amt1, { + value: amt1.add(bridgeFee1), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + await waitUntilSucceed(axelarAPI, resp.hash); - // wETH deposit L1 to L2 - resp = await rootBridge.connect(rootTestWallet).deposit(rootWETH.address, amt, { - value: bridgeFee, - }) - await waitForReceipt(resp.hash, rootProvider); + let receipt = await childProvider.getTransactionReceipt(resp.hash); + let txFee1 = receipt.gasUsed.mul(receipt.effectiveGasPrice); - let postBalL1 = await rootWETH.balanceOf(rootTestWallet.address); - let postBalL2 = preBalL2; + while ((await rootBridge.getPendingWithdrawalsLength(rootTestWallet.address)).eq(preLength)) { + await delay(1000); + } + // Withdraw of IMX exceeding rate limit + let amt2 = ethers.utils.parseEther("1.0"); + let bridgeFee2 = ethers.utils.parseEther("1.0"); + + // IMX withdraw L2 to L1 + [priorityFee, maxFee] = await getFee(childProvider); + resp = await childBridge.connect(childTestWallet).withdrawIMX(amt2, { + value: amt2.add(bridgeFee2), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + await waitUntilSucceed(axelarAPI, resp.hash); + receipt = await childProvider.getTransactionReceipt(resp.hash); + let txFee2 = receipt.gasUsed.mul(receipt.effectiveGasPrice); + + while ((await rootBridge.getPendingWithdrawalsLength(rootTestWallet.address)).eq(preLength.add(1))) { + await delay(1000); + } + + // Try to withdraw + await expect(rootBridge.connect(rootTestWallet).finaliseQueuedWithdrawal(rootTestWallet.address, preLength.add(1))).to.be.rejectedWith( + "UNPREDICTABLE_GAS_LIMIT" + ); + + // Fast-forward to 24 hours later. + await rootProvider.send( + "hardhat_mine", [ + "0x15181", // 24 hours + ]); + + // Withdraw again + resp = await rootBridge.connect(rootTestWallet).finaliseQueuedWithdrawal(rootTestWallet.address, preLength.add(1)) + await waitForReceipt(resp.hash, rootProvider); + + resp = await rootBridge.connect(rootTestWallet).finaliseQueuedWithdrawal(rootTestWallet.address, preLength) + await waitForReceipt(resp.hash, rootProvider); + + let postBalL1 = await rootIMX.balanceOf(rootTestWallet.address); + let postBalL2 = await childProvider.getBalance(childTestWallet.address); + + // Verify + let expectedPostL1 = preBalL1.add(amt1).add(amt2); + let expectedPostL2 = preBalL2.sub(amt1).sub(amt2).sub(txFee1).sub(txFee2).sub(bridgeFee1).sub(bridgeFee2); + expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); + expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); + + // Recover rate limit + resp = await rootBridge.connect(rootPrivilegedWallet).setRateControlThreshold(rootIMX.address, ethers.utils.parseEther("15516"), ethers.utils.parseEther("4.31"), ethers.utils.parseEther("7758")); + await waitForReceipt(resp.hash, rootProvider); + + // Deactive withdraw queue + resp = await rootBridge.connect(rootPrivilegedWallet).deactivateWithdrawalQueue(); + await waitForReceipt(resp.hash, rootProvider); + }).timeout(2400000) + + // Local only + it("should not withdraw WIMX if child bridge is paused", async() => { + // Transfer 0.1 IMX to child pauser + let resp = await childTestWallet.sendTransaction({ + to: childPauserWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, childProvider); + + // Transfer 0.1 IMX to child unpauser + resp = await childTestWallet.sendTransaction({ + to: childPrivilegedWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, childProvider); + + // Pause child bridge + if (!await childBridge.paused()) { + resp = await childBridge.connect(childPauserWallet).pause(); + await waitForReceipt(resp.hash, childProvider); + expect(await childBridge.paused()).to.true; + } + + // Wrap 1 IMX + let [priorityFee, maxFee] = await getFee(childProvider); + resp = await childWIMX.connect(childTestWallet).deposit({ + value: ethers.utils.parseEther("1.0"), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + let amt = ethers.utils.parseEther("0.5"); + let bridgeFee = ethers.utils.parseEther("1.0"); + + // Approve + [priorityFee, maxFee] = await getFee(childProvider); + resp = await childWIMX.connect(childTestWallet).approve(childBridge.address, amt, { + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + await expect(childBridge.connect(childTestWallet).withdrawWIMX(amt, { + value: amt.add(bridgeFee), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + })).to.be.rejectedWith("Pausable: paused"); + + // Unpause child bridge + resp = await childBridge.connect(childPrivilegedWallet).unpause(); + await waitForReceipt(resp.hash, childProvider); + expect(await childBridge.paused()).to.false; + }).timeout(2400000) + + it("should not withdraw wIMX if allowance is insufficient", async() => { + // Wrap 1 IMX + let [priorityFee, maxFee] = await getFee(childProvider); + let resp = await childWIMX.connect(childTestWallet).deposit({ + value: ethers.utils.parseEther("1.0"), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + let amt = ethers.utils.parseEther("0.5"); + let bridgeFee = ethers.utils.parseEther("1.0"); + + // Approve + [priorityFee, maxFee] = await getFee(childProvider); + resp = await childWIMX.connect(childTestWallet).approve(childBridge.address, amt.sub(1), { + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + // wIMX withdraw L2 to L1 + [priorityFee, maxFee] = await getFee(childProvider); + await expect(childBridge.connect(childTestWallet).withdrawWIMX(amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + })).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); + }).timeout(2400000) + + it("should not withdraw wIMX if balance is insufficient", async() => { + let balance = await childWIMX.balanceOf(childTestWallet.address); + + let amt = balance; + let bridgeFee = ethers.utils.parseEther("1.0"); + + // wIMX withdraw L2 to L1 + let [priorityFee, maxFee] = await getFee(childProvider); + await expect(childBridge.connect(childTestWallet).withdrawWIMX(amt.add(1), { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + })).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); + }).timeout(2400000) + + it("should successfully withdraw wIMX to self from L2 to L1", async() => { + // Wrap 1 IMX + let [priorityFee, maxFee] = await getFee(childProvider); + let resp = await childWIMX.connect(childTestWallet).deposit({ + value: ethers.utils.parseEther("1.0"), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + // Get IMX balance on root & child chains before withdraw + let preBalL1 = await rootIMX.balanceOf(rootTestWallet.address); + let preBalL2 = await childWIMX.balanceOf(childTestWallet.address); + + let amt = ethers.utils.parseEther("0.5"); + let bridgeFee = ethers.utils.parseEther("1.0"); + + // Approve + [priorityFee, maxFee] = await getFee(childProvider); + resp = await childWIMX.connect(childTestWallet).approve(childBridge.address, amt, { + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + // wIMX withdraw L2 to L1 + [priorityFee, maxFee] = await getFee(childProvider); + resp = await childBridge.connect(childTestWallet).withdrawWIMX(amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + let postBalL1 = preBalL1; + let postBalL2 = await childWIMX.balanceOf(childTestWallet.address); + + await waitUntilSucceed(axelarAPI, resp.hash); + + while (postBalL1.eq(preBalL1)) { + postBalL1 = await rootIMX.balanceOf(rootTestWallet.address); + await delay(1000); + } + + // Verify + let expectedPostL1 = preBalL1.add(amt); + let expectedPostL2 = preBalL2.sub(amt); + expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); + expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); + }).timeout(2400000) + + it("should successfully withdraw wIMX to others from L2 to L1", async() => { + let rootRecipient = rootPrivilegedWallet.address; + // Wrap 1 IMX + let [priorityFee, maxFee] = await getFee(childProvider); + let resp = await childWIMX.connect(childTestWallet).deposit({ + value: ethers.utils.parseEther("1.0"), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + // Get IMX balance on root & child chains before withdraw + let preBalL1 = await rootIMX.balanceOf(rootRecipient); + let preBalL2 = await childWIMX.balanceOf(childTestWallet.address); + + let amt = ethers.utils.parseEther("0.5"); + let bridgeFee = ethers.utils.parseEther("1.0"); + + // Approve + [priorityFee, maxFee] = await getFee(childProvider); + resp = await childWIMX.connect(childTestWallet).approve(childBridge.address, amt, { + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + // wIMX withdraw L2 to L1 + [priorityFee, maxFee] = await getFee(childProvider); + resp = await childBridge.connect(childTestWallet).withdrawWIMXTo(rootRecipient, amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + let postBalL1 = preBalL1; + let postBalL2 = await childWIMX.balanceOf(childTestWallet.address); + + await waitUntilSucceed(axelarAPI, resp.hash); + + while (postBalL1.eq(preBalL1)) { + postBalL1 = await rootIMX.balanceOf(rootRecipient); + await delay(1000); + } + + // Verify + let expectedPostL1 = preBalL1.add(amt); + let expectedPostL2 = preBalL2.sub(amt); + expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); + expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); + }).timeout(2400000) + + // Local only + it("should not withdraw wIMX on L1 if root bridge is paused", async() => { + // Transfer 0.1 ETH to root pauser + let resp = await rootTestWallet.sendTransaction({ + to: rootPauserWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, rootProvider); + + // Transfer 0.1 ETH to root unpauser + resp = await rootTestWallet.sendTransaction({ + to: rootPrivilegedWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, rootProvider); + + // Pause root bridge + if (!await rootBridge.paused()) { + resp = await rootBridge.connect(rootPauserWallet).pause(); + await waitForReceipt(resp.hash, rootProvider); + expect(await rootBridge.paused()).to.true; + } + + // Wrap 1 IMX + let [priorityFee, maxFee] = await getFee(childProvider); + resp = await childWIMX.connect(childTestWallet).deposit({ + value: ethers.utils.parseEther("1.0"), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + // Get IMX balance on root & child chains before withdraw + let preBalL1 = await rootIMX.balanceOf(rootTestWallet.address); + let preBalL2 = await childWIMX.balanceOf(childTestWallet.address); + + let amt = ethers.utils.parseEther("0.5"); + let bridgeFee = ethers.utils.parseEther("1.0"); + + // Approve + [priorityFee, maxFee] = await getFee(childProvider); + resp = await childWIMX.connect(childTestWallet).approve(childBridge.address, amt, { + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + // wIMX withdraw L2 to L1 + [priorityFee, maxFee] = await getFee(childProvider); + resp = await childBridge.connect(childTestWallet).withdrawWIMX(amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + await waitUntilSucceed(axelarAPI, resp.hash); + + // Balance on L1 should not change. + await delay(10000); + let postBalL1 = await rootIMX.balanceOf(rootTestWallet.address); + let postBalL2 = await childWIMX.balanceOf(childTestWallet.address); + + // Verify + let expectedPostL1 = preBalL1; + let expectedPostL2 = preBalL2.sub(amt); + expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); + expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); + + // Unpause root bridge + resp = await rootBridge.connect(rootPrivilegedWallet).unpause(); + await waitForReceipt(resp.hash, rootProvider); + expect(await rootBridge.paused()).to.false; + }).timeout(2400000) + + // Local only + it("should put wIMX withdrawal in pending when violating rate limit policy", async() => { + // Set new rate limit + let resp = await rootBridge.connect(rootPrivilegedWallet).setRateControlThreshold(rootIMX.address, ethers.utils.parseEther("2.016"), ethers.utils.parseEther("0.00056"), ethers.utils.parseEther("1.008")); + await waitForReceipt(resp.hash, rootProvider); + + // Wrap 3 IMX + let [priorityFee, maxFee] = await getFee(childProvider); + resp = await childWIMX.connect(childTestWallet).deposit({ + value: ethers.utils.parseEther("3.0"), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + // Withdraw of IMX exceeding large threshold + // Get IMX balance on root & child chains before withdraw + let preBalL1 = await rootIMX.balanceOf(rootTestWallet.address); + let preBalL2 = await childWIMX.balanceOf(childTestWallet.address); + let preLength = await rootBridge.getPendingWithdrawalsLength(rootTestWallet.address); + + let amt1 = ethers.utils.parseEther("1.1"); + let bridgeFee1 = ethers.utils.parseEther("1.0"); + + // Approve + [priorityFee, maxFee] = await getFee(childProvider); + resp = await childWIMX.connect(childTestWallet).approve(childBridge.address, amt1, { + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + // wIMX withdraw L2 to L1 + [priorityFee, maxFee] = await getFee(childProvider); + resp = await childBridge.connect(childTestWallet).withdrawWIMX(amt1, { + value: bridgeFee1, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + await waitUntilSucceed(axelarAPI, resp.hash); + + while ((await rootBridge.getPendingWithdrawalsLength(rootTestWallet.address)).eq(preLength)) { + await delay(1000); + } + + // Withdraw of IMX exceeding rate limit + let amt2 = ethers.utils.parseEther("1.0"); + let bridgeFee2 = ethers.utils.parseEther("1.0"); + + // Approve + [priorityFee, maxFee] = await getFee(childProvider); + resp = await childWIMX.connect(childTestWallet).approve(childBridge.address, amt2, { + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + // IMX withdraw L2 to L1 + [priorityFee, maxFee] = await getFee(childProvider); + resp = await childBridge.connect(childTestWallet).withdrawWIMX(amt2, { + value: bridgeFee2, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + await waitUntilSucceed(axelarAPI, resp.hash); + + while ((await rootBridge.getPendingWithdrawalsLength(rootTestWallet.address)).eq(preLength.add(1))) { + await delay(1000); + } + + // Try to withdraw + await expect(rootBridge.connect(rootTestWallet).finaliseQueuedWithdrawal(rootTestWallet.address, preLength.add(1))).to.be.rejectedWith( + "UNPREDICTABLE_GAS_LIMIT" + ); + + // Fast-forward to 24 hours later. + await rootProvider.send( + "hardhat_mine", [ + "0x15181", // 24 hours + ]); + + // Withdraw again + resp = await rootBridge.connect(rootTestWallet).finaliseQueuedWithdrawal(rootTestWallet.address, preLength.add(1)) + await waitForReceipt(resp.hash, rootProvider); + + resp = await rootBridge.connect(rootTestWallet).finaliseQueuedWithdrawal(rootTestWallet.address, preLength) + await waitForReceipt(resp.hash, rootProvider); + + let postBalL1 = await rootIMX.balanceOf(rootTestWallet.address); + let postBalL2 = await childWIMX.balanceOf(childTestWallet.address); + + // Verify + let expectedPostL1 = preBalL1.add(amt1).add(amt2); + let expectedPostL2 = preBalL2.sub(amt1).sub(amt2); + expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); + expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); + + // Recover rate limit + resp = await rootBridge.connect(rootPrivilegedWallet).setRateControlThreshold(rootIMX.address, ethers.utils.parseEther("15516"), ethers.utils.parseEther("4.31"), ethers.utils.parseEther("7758")); + await waitForReceipt(resp.hash, rootProvider); + + // Deactive withdraw queue + resp = await rootBridge.connect(rootPrivilegedWallet).deactivateWithdrawalQueue(); + await waitForReceipt(resp.hash, rootProvider); + }).timeout(2400000) + + it("should not deposit ETH if balance is insufficient", async() => { + let balance = await rootProvider.getBalance(rootTestWallet.address); + + let amt = balance; + let bridgeFee = ethers.utils.parseEther("0.001"); + + await expect(rootBridge.connect(rootTestWallet).depositETH(amt, { + value: amt.add(bridgeFee), + })).to.be.rejectedWith("sender doesn't have enough funds to send tx"); + }).timeout(2400000) + + // Local only + it("should not deposit ETH if root bridge is paused", async() => { + // Transfer 0.1 ETH to root pauser + let resp = await rootTestWallet.sendTransaction({ + to: rootPauserWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, rootProvider); + + // Transfer 0.1 ETH to root unpauser + resp = await rootTestWallet.sendTransaction({ + to: rootPrivilegedWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, rootProvider); + + // Pause root bridge + if (!await rootBridge.paused()) { + resp = await rootBridge.connect(rootPauserWallet).pause(); + await waitForReceipt(resp.hash, rootProvider); + expect(await rootBridge.paused()).to.true; + } + + let amt = ethers.utils.parseEther("0.001"); + let bridgeFee = ethers.utils.parseEther("0.001"); + + // Fail to deposit on L1 + await expect(rootBridge.connect(rootTestWallet).depositETH(amt, { + value: amt.add(bridgeFee), + })).to.be.rejectedWith("Pausable: paused"); + + // Unpause root bridge + resp = await rootBridge.connect(rootPrivilegedWallet).unpause(); + await waitForReceipt(resp.hash, rootProvider); + expect(await rootBridge.paused()).to.false; + }).timeout(2400000) + + it("should successfully deposit ETH to self from L1 to L2", async() => { + // Get ETH balance on root & child chains before deposit + let preBalL1 = await rootProvider.getBalance(rootTestWallet.address); + let preBalL2 = await childETH.balanceOf(childTestWallet.address); + + let amt = ethers.utils.parseEther("1.0"); + let bridgeFee = ethers.utils.parseEther("0.001"); + + // ETH deposit L1 to L2 + let resp = await rootBridge.connect(rootTestWallet).depositETH(amt, { + value: amt.add(bridgeFee), + }); + await waitForReceipt(resp.hash, rootProvider); + + let postBalL1 = await rootProvider.getBalance(rootTestWallet.address); + let postBalL2 = preBalL2; + + await waitUntilSucceed(axelarAPI, resp.hash); + + while (postBalL2.eq(preBalL2)) { + postBalL2 = await childETH.balanceOf(childTestWallet.address); + await delay(1000); + } + + // Verify + let receipt = await rootProvider.getTransactionReceipt(resp.hash); + let txFee = receipt.gasUsed.mul(receipt.effectiveGasPrice); + let expectedPostL1 = preBalL1.sub(txFee).sub(amt).sub(bridgeFee); + let expectedPostL2 = preBalL2.add(amt); + expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); + expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); + }).timeout(2400000) + + it("should successfully deposit ETH to others from L1 to L2", async() => { + let childRecipient = childPrivilegedWallet.address; + // Get ETH balance on root & child chains before deposit + let preBalL1 = await rootProvider.getBalance(rootTestWallet.address); + let preBalL2 = await childETH.balanceOf(childRecipient); + + let amt = ethers.utils.parseEther("0.001"); + let bridgeFee = ethers.utils.parseEther("0.001"); + + // ETH deposit L1 to L2 + let resp = await rootBridge.connect(rootTestWallet).depositToETH(childRecipient, amt, { + value: amt.add(bridgeFee), + }); + await waitForReceipt(resp.hash, rootProvider); + + let postBalL1 = await rootProvider.getBalance(rootTestWallet.address); + let postBalL2 = preBalL2; + + await waitUntilSucceed(axelarAPI, resp.hash); + + while (postBalL2.eq(preBalL2)) { + postBalL2 = await childETH.balanceOf(childRecipient); + await delay(1000); + } + + // Verify + let receipt = await rootProvider.getTransactionReceipt(resp.hash); + let txFee = receipt.gasUsed.mul(receipt.effectiveGasPrice); + let expectedPostL1 = preBalL1.sub(txFee).sub(amt).sub(bridgeFee); + let expectedPostL2 = preBalL2.add(amt); + expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); + expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); + }).timeout(2400000) + + // Local only + it("should not deposit ETH on L2 if child bridge is paused", async() => { + // Transfer 0.1 IMX to child pauser + let resp = await childTestWallet.sendTransaction({ + to: childPauserWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, childProvider); + + // Transfer 0.1 IMX to child unpauser + resp = await childTestWallet.sendTransaction({ + to: childPrivilegedWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, childProvider); + + // Pause child bridge + if (!await childBridge.paused()) { + resp = await childBridge.connect(childPauserWallet).pause(); + await waitForReceipt(resp.hash, childProvider); + expect(await childBridge.paused()).to.true; + } + + // Get ETH balance on root & child chains before deposit + let preBalL1 = await rootProvider.getBalance(rootTestWallet.address); + let preBalL2 = await childETH.balanceOf(childTestWallet.address); + + let amt = ethers.utils.parseEther("0.001"); + let bridgeFee = ethers.utils.parseEther("0.001"); + + // Try to deposit + resp = await rootBridge.connect(rootTestWallet).depositETH(amt, { + value: amt.add(bridgeFee), + }); + await waitForReceipt(resp.hash, rootProvider); + await waitUntilSucceed(axelarAPI, resp.hash); + + // Balance on L2 should not change. + await delay(10000); + let postBalL1 = await rootProvider.getBalance(rootTestWallet.address); + let postBalL2 = await childETH.balanceOf(childTestWallet.address); + + // Verify + let receipt = await rootProvider.getTransactionReceipt(resp.hash); + let txFee = receipt.gasUsed.mul(receipt.effectiveGasPrice); + let expectedPostL1 = preBalL1.sub(txFee).sub(amt).sub(bridgeFee); + let expectedPostL2 = preBalL2; + expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); + expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); + + // Unpause child bridge + resp = await childBridge.connect(childPrivilegedWallet).unpause(); + await waitForReceipt(resp.hash, childProvider); + expect(await childBridge.paused()).to.false; + }).timeout(2400000) + + it("should successfully deposit wETH to self from L1 to L2", async() => { + // Wrap 0.01 ETH + let resp = await rootWETH.connect(rootTestWallet).deposit({ + value: ethers.utils.parseEther("0.01"), + }) + await waitForReceipt(resp.hash, rootProvider); + + // Get ETH balance on root & child chains before withdraw + let preBalL1 = await rootWETH.balanceOf(rootTestWallet.address); + let preBalL2 = await childETH.balanceOf(childTestWallet.address); + + let amt = ethers.utils.parseEther("0.001"); + let bridgeFee = ethers.utils.parseEther("0.001"); + + // Approve + resp = await rootWETH.connect(rootTestWallet).approve(rootBridge.address, amt); + await waitForReceipt(resp.hash, rootProvider); + + // wETH deposit L1 to L2 + resp = await rootBridge.connect(rootTestWallet).deposit(rootWETH.address, amt, { + value: bridgeFee, + }) + await waitForReceipt(resp.hash, rootProvider); + + let postBalL1 = await rootWETH.balanceOf(rootTestWallet.address); + let postBalL2 = preBalL2; + + await waitUntilSucceed(axelarAPI, resp.hash); + + while (postBalL2.eq(preBalL2)) { + postBalL2 = await childETH.balanceOf(childTestWallet.address); + await delay(1000); + } + + // Verify + let expectedPostL1 = preBalL1.sub(amt); + let expectedPostL2 = preBalL2.add(amt); + expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); + expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); + }).timeout(2400000) + + it("should successfully deposit wETH to others from L1 to L2", async() => { + let childRecipient = childPrivilegedWallet.address; + // Wrap 0.01 ETH + let resp = await rootWETH.connect(rootTestWallet).deposit({ + value: ethers.utils.parseEther("0.01"), + }) + await waitForReceipt(resp.hash, rootProvider); + + // Get ETH balance on root & child chains before withdraw + let preBalL1 = await rootWETH.balanceOf(rootTestWallet.address); + let preBalL2 = await childETH.balanceOf(childRecipient); + + let amt = ethers.utils.parseEther("0.001"); + let bridgeFee = ethers.utils.parseEther("0.001"); + + // Approve + resp = await rootWETH.connect(rootTestWallet).approve(rootBridge.address, amt); + await waitForReceipt(resp.hash, rootProvider); + + // wETH deposit L1 to L2 + resp = await rootBridge.connect(rootTestWallet).depositTo(rootWETH.address, childRecipient, amt, { + value: bridgeFee, + }) + await waitForReceipt(resp.hash, rootProvider); + + let postBalL1 = await rootWETH.balanceOf(rootTestWallet.address); + let postBalL2 = preBalL2; + + await waitUntilSucceed(axelarAPI, resp.hash); + + while (postBalL2.eq(preBalL2)) { + postBalL2 = await childETH.balanceOf(childRecipient); + await delay(1000); + } + + // Verify + let expectedPostL1 = preBalL1.sub(amt); + let expectedPostL2 = preBalL2.add(amt); + expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); + expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); + }).timeout(2400000) + + // Local only + it("should not withdraw ETH if child bridge is paused", async() => { + // Transfer 0.1 IMX to child pauser + let resp = await childTestWallet.sendTransaction({ + to: childPauserWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, childProvider); + + // Transfer 0.1 IMX to child unpauser + resp = await childTestWallet.sendTransaction({ + to: childPrivilegedWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, childProvider); + + // Pause child bridge + if (!await childBridge.paused()) { + resp = await childBridge.connect(childPauserWallet).pause(); + await waitForReceipt(resp.hash, childProvider); + expect(await childBridge.paused()).to.true; + } + + let amt = ethers.utils.parseEther("0.0005"); + let bridgeFee = ethers.utils.parseEther("1.0"); + + // ETH withdraw L2 to L1 + let [priorityFee, maxFee] = await getFee(childProvider); + await expect(childBridge.connect(childTestWallet).withdrawETH(amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + })).to.be.rejectedWith("Pausable: paused"); + + // Unpause child bridge + resp = await childBridge.connect(childPrivilegedWallet).unpause(); + await waitForReceipt(resp.hash, childProvider); + expect(await childBridge.paused()).to.false; + }).timeout(2400000) + + it("should not withdraw ETH if balance is insufficient", async() => { + let amt = await childETH.balanceOf(childTestWallet.address); + let bridgeFee = ethers.utils.parseEther("1.0"); + + // ETH withdraw L2 to L1 + let [priorityFee, maxFee] = await getFee(childProvider); + await expect(childBridge.connect(childTestWallet).withdrawETH(amt.add(1), { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + })).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); + }).timeout(2400000) + + it("should successfully withdraw ETH to self from L2 to L1", async() => { + // Get ETH balance on root & child chains before withdraw + let preBalL1 = await rootProvider.getBalance(rootTestWallet.address); + let preBalL2 = await childETH.balanceOf(childTestWallet.address); + + let amt = ethers.utils.parseEther("0.0005"); + let bridgeFee = ethers.utils.parseEther("1.0"); + + // ETH withdraw L2 to L1 + let [priorityFee, maxFee] = await getFee(childProvider); + let resp = await childBridge.connect(childTestWallet).withdrawETH(amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + let postBalL1 = preBalL1; + let postBalL2 = await childETH.balanceOf(childTestWallet.address); + + await waitUntilSucceed(axelarAPI, resp.hash); + + while (postBalL1.eq(preBalL1)) { + postBalL1 = await rootProvider.getBalance(rootTestWallet.address); + await delay(1000); + } + + // Verify + let expectedPostL1 = preBalL1.add(amt); + let expectedPostL2 = preBalL2.sub(amt); + expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); + expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); + }).timeout(2400000) + + it("should successfully withdraw ETH to others from L2 to L1", async() => { + let rootRecipient = rootPrivilegedWallet.address; + // Get ETH balance on root & child chains before withdraw + let preBalL1 = await rootProvider.getBalance(rootRecipient); + let preBalL2 = await childETH.balanceOf(childTestWallet.address); + + let amt = ethers.utils.parseEther("0.0005"); + let bridgeFee = ethers.utils.parseEther("1.0"); + + // ETH withdraw L2 to L1 + let [priorityFee, maxFee] = await getFee(childProvider); + let resp = await childBridge.connect(childTestWallet).withdrawETHTo(rootRecipient, amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + let postBalL1 = preBalL1; + let postBalL2 = await childETH.balanceOf(childTestWallet.address); + + await waitUntilSucceed(axelarAPI, resp.hash); + + while (postBalL1.eq(preBalL1)) { + postBalL1 = await rootProvider.getBalance(rootRecipient); + await delay(1000); + } + + // Verify + let expectedPostL1 = preBalL1.add(amt); + let expectedPostL2 = preBalL2.sub(amt); + expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); + expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); + }).timeout(2400000) + + // Local only + it("should not withdraw ETH on L1 if root bridge is paused", async() => { + // Transfer 0.1 ETH to root pauser + let resp = await rootTestWallet.sendTransaction({ + to: rootPauserWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, rootProvider); + + // Transfer 0.1 ETH to root unpauser + resp = await rootTestWallet.sendTransaction({ + to: rootPrivilegedWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, rootProvider); + + // Pause root bridge + if (!await rootBridge.paused()) { + resp = await rootBridge.connect(rootPauserWallet).pause(); + await waitForReceipt(resp.hash, rootProvider); + expect(await rootBridge.paused()).to.true; + } + + // Get ETH balance on root & child chains before withdraw + let preBalL1 = await rootProvider.getBalance(rootTestWallet.address); + let preBalL2 = await childETH.balanceOf(childTestWallet.address); + + let amt = ethers.utils.parseEther("0.0005"); + let bridgeFee = ethers.utils.parseEther("1.0"); + + // ETH withdraw L2 to L1 + let [priorityFee, maxFee] = await getFee(childProvider); + resp = await childBridge.connect(childTestWallet).withdrawETH(amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); await waitUntilSucceed(axelarAPI, resp.hash); - while (postBalL2.eq(preBalL2)) { - postBalL2 = await childETH.balanceOf(childTestWallet.address); - await delay(1000); - } + // Balance on L1 should not change. + await delay(10000); + let postBalL1 = await rootProvider.getBalance(rootTestWallet.address); + let postBalL2 = await childETH.balanceOf(childTestWallet.address); // Verify - let expectedPostL1 = preBalL1.sub(amt); - let expectedPostL2 = preBalL2.add(amt); + let expectedPostL1 = preBalL1; + let expectedPostL2 = preBalL2.sub(amt); expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); + + // Unpause root bridge + resp = await rootBridge.connect(rootPrivilegedWallet).unpause(); + await waitForReceipt(resp.hash, rootProvider); + expect(await rootBridge.paused()).to.false; }).timeout(2400000) - it("should successfully withdraw ETH to self from L2 to L1", async() => { + // Local only + it("should put ETH withdrawal in pending when violating rate limit policy", async() => { + // Set new rate limit + let resp = await rootBridge.connect(rootPrivilegedWallet).setRateControlThreshold(await rootBridge.NATIVE_ETH(), ethers.utils.parseEther("0.0010008"), ethers.utils.parseEther("0.000000278"), ethers.utils.parseEther("0.0005004")); + await waitForReceipt(resp.hash, rootProvider); + + // Withdraw of ETH exceeding large threshold // Get ETH balance on root & child chains before withdraw let preBalL1 = await rootProvider.getBalance(rootTestWallet.address); let preBalL2 = await childETH.balanceOf(childTestWallet.address); + let preLength = await rootBridge.getPendingWithdrawalsLength(rootTestWallet.address); - let amt = ethers.utils.parseEther("0.0005"); - let bridgeFee = ethers.utils.parseEther("1.0"); + let amt1 = ethers.utils.parseEther("0.0006"); + let bridgeFee1 = ethers.utils.parseEther("1.0"); // ETH withdraw L2 to L1 let [priorityFee, maxFee] = await getFee(childProvider); - let resp = await childBridge.connect(childTestWallet).withdrawETH(amt, { - value: bridgeFee, + resp = await childBridge.connect(childTestWallet).withdrawETH(amt1, { + value: bridgeFee1, maxPriorityFeePerGas: priorityFee, maxFeePerGas: maxFee, }); await waitForReceipt(resp.hash, childProvider); + await waitUntilSucceed(axelarAPI, resp.hash); - let postBalL1 = preBalL1; - let postBalL2 = await childETH.balanceOf(childTestWallet.address); + while ((await rootBridge.getPendingWithdrawalsLength(rootTestWallet.address)).eq(preLength)) { + await delay(1000); + } + + // Withdraw of ETH exceeding rate limit + let amt2 = ethers.utils.parseEther("0.0005"); + let bridgeFee2 = ethers.utils.parseEther("1.0"); + // ETH withdraw L2 to L1 + [priorityFee, maxFee] = await getFee(childProvider); + resp = await childBridge.connect(childTestWallet).withdrawETH(amt2, { + value: bridgeFee2, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); await waitUntilSucceed(axelarAPI, resp.hash); - while (postBalL1.eq(preBalL1)) { - postBalL1 = await rootProvider.getBalance(rootTestWallet.address); + while ((await rootBridge.getPendingWithdrawalsLength(rootTestWallet.address)).eq(preLength.add(1))) { await delay(1000); } + // Try to withdraw + await expect(rootBridge.connect(rootTestWallet).finaliseQueuedWithdrawal(rootTestWallet.address, preLength.add(1))).to.be.rejectedWith( + "UNPREDICTABLE_GAS_LIMIT" + ); + + // Fast-forward to 24 hours later. + await rootProvider.send( + "hardhat_mine", [ + "0x15181", // 24 hours + ]); + + // Withdraw again + resp = await rootBridge.connect(rootTestWallet).finaliseQueuedWithdrawal(rootTestWallet.address, preLength.add(1)) + await waitForReceipt(resp.hash, rootProvider); + let receipt = await rootProvider.getTransactionReceipt(resp.hash); + let txFee1 = receipt.gasUsed.mul(receipt.effectiveGasPrice); + + resp = await rootBridge.connect(rootTestWallet).finaliseQueuedWithdrawal(rootTestWallet.address, preLength) + await waitForReceipt(resp.hash, rootProvider); + receipt = await rootProvider.getTransactionReceipt(resp.hash); + let txFee2 = receipt.gasUsed.mul(receipt.effectiveGasPrice); + + let postBalL1 = await rootProvider.getBalance(rootTestWallet.address); + let postBalL2 = await childETH.balanceOf(childTestWallet.address); + // Verify - let expectedPostL1 = preBalL1.add(amt); - let expectedPostL2 = preBalL2.sub(amt); + let expectedPostL1 = preBalL1.sub(txFee1).sub(txFee2).add(amt1).add(amt2); + let expectedPostL2 = preBalL2.sub(amt1).sub(amt2); expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); + + // Recover rate limit + resp = await rootBridge.connect(rootPrivilegedWallet).setRateControlThreshold(await rootBridge.NATIVE_ETH(), ethers.utils.parseEther("10.08"), ethers.utils.parseEther("0.0028"), ethers.utils.parseEther("5.04")); + await waitForReceipt(resp.hash, rootProvider); + + // Deactive withdraw queue + resp = await rootBridge.connect(rootPrivilegedWallet).deactivateWithdrawalQueue(); + await waitForReceipt(resp.hash, rootProvider); + }).timeout(2400000) + + it("should not deposit unmapped token", async() => { + let unMappedToken = ethers.utils.getAddress("0xccC8cb5229B0ac8069C51fd58367Fd1e622aFD97"); + let amt = ethers.utils.parseEther("1.0"); + let bridgeFee = ethers.utils.parseEther("0.001"); + + // Token deposit L1 to L2 + await expect(rootBridge.connect(rootTestWallet).deposit(unMappedToken, amt, { + value: bridgeFee, + })).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); }).timeout(2400000) it("should successfully map a ERC20 Token", async() => { @@ -321,12 +1511,98 @@ describe("Bridge e2e test", () => { expect(childTokenAddr).to.equal(expectedChildTokenAddr); }).timeout(2400000) + it("should not map a mapped ERC20 Token", async() => { + let childContracts = getChildContracts(); + let childCustomTokenAddr = childContracts.CHILD_TEST_CUSTOM_TOKEN; + if (childCustomTokenAddr == "") { + childCustomToken = getContract("ChildERC20", childCustomTokenAddr, childProvider); + console.log("Custom token has not been mapped yet, skip."); + return; + } + // Map token + let bridgeFee = ethers.utils.parseEther("0.001"); + await expect(rootBridge.connect(rootTestWallet).mapToken(rootCustomToken.address, { + value: bridgeFee, + })).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); + }).timeout(2400000) + + it("should not deposit mapped ERC20 Token if allowance is insufficient", async() => { + let amt = ethers.utils.parseEther("1.0"); + let bridgeFee = ethers.utils.parseEther("0.001"); + + // Approve + let resp = await rootCustomToken.connect(rootTestWallet).approve(rootBridge.address, amt.sub(1)); + await waitForReceipt(resp.hash, rootProvider); + + // Fail to deposit on L1 + await expect(rootBridge.connect(rootTestWallet).deposit(rootCustomToken.address, amt, { + value: bridgeFee, + })).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); + }).timeout(2400000) + + it("should not deposit mapped ERC20 Token if balance is insufficient", async() => { + let balance = await rootCustomToken.balanceOf(rootTestWallet.address); + + let amt = balance.add(1); + let bridgeFee = ethers.utils.parseEther("0.001"); + + // Approve + let resp = await rootCustomToken.connect(rootTestWallet).approve(rootBridge.address, amt); + await waitForReceipt(resp.hash, rootProvider); + + await expect(rootBridge.connect(rootTestWallet).deposit(rootCustomToken.address, amt, { + value: bridgeFee, + })).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); + }).timeout(2400000) + + // Local only + it("should not deposit mapped ERC20 Token if root bridge is paused", async() => { + // Transfer 0.1 ETH to root pauser + let resp = await rootTestWallet.sendTransaction({ + to: rootPauserWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, rootProvider); + + // Transfer 0.1 ETH to root unpauser + resp = await rootTestWallet.sendTransaction({ + to: rootPrivilegedWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, rootProvider); + + // Pause root bridge + if (!await rootBridge.paused()) { + resp = await rootBridge.connect(rootPauserWallet).pause(); + await waitForReceipt(resp.hash, rootProvider); + expect(await rootBridge.paused()).to.true; + } + + // Try to deposit. + let amt = ethers.utils.parseEther("1.0"); + let bridgeFee = ethers.utils.parseEther("0.001"); + + // Approve + resp = await rootCustomToken.connect(rootTestWallet).approve(rootBridge.address, amt); + await waitForReceipt(resp.hash, rootProvider); + + // Fail to deposit on L1 + await expect(rootBridge.connect(rootTestWallet).deposit(rootCustomToken.address, amt, { + value: bridgeFee, + })).to.be.rejectedWith("Pausable: paused"); + + // Unpause root bridge + resp = await rootBridge.connect(rootPrivilegedWallet).unpause(); + await waitForReceipt(resp.hash, rootProvider); + expect(await rootBridge.paused()).to.false; + }).timeout(2400000) + it("should successfully deposit mapped ERC20 Token to self from L1 to L2", async() => { // Get token balance on root & child chains before deposit let preBalL1 = await rootCustomToken.balanceOf(rootTestWallet.address); let preBalL2 = await childCustomToken.balanceOf(childTestWallet.address); - let amt = ethers.utils.parseEther("1.0"); + let amt = ethers.utils.parseEther("10.0"); let bridgeFee = ethers.utils.parseEther("0.001"); // Approve @@ -356,6 +1632,109 @@ describe("Bridge e2e test", () => { expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); }).timeout(2400000) + it("should successfully deposit mapped ERC20 Token to others from L1 to L2", async() => { + let childRecipient = childPrivilegedWallet.address; + // Get token balance on root & child chains before deposit + let preBalL1 = await rootCustomToken.balanceOf(rootTestWallet.address); + let preBalL2 = await childCustomToken.balanceOf(childRecipient); + + let amt = ethers.utils.parseEther("1.0"); + let bridgeFee = ethers.utils.parseEther("0.001"); + + // Approve + let resp = await rootCustomToken.connect(rootTestWallet).approve(rootBridge.address, amt); + await waitForReceipt(resp.hash, rootProvider); + + // Token deposit L1 to L2 + resp = await rootBridge.connect(rootTestWallet).depositTo(rootCustomToken.address, childRecipient, amt, { + value: bridgeFee, + }) + await waitForReceipt(resp.hash, rootProvider); + + let postBalL1 = await rootCustomToken.balanceOf(rootTestWallet.address); + let postBalL2 = preBalL2; + + await waitUntilSucceed(axelarAPI, resp.hash); + + while (postBalL2.eq(preBalL2)) { + postBalL2 = await childCustomToken.balanceOf(childRecipient); + await delay(1000); + } + + // Verify + let expectedPostL1 = preBalL1.sub(amt); + let expectedPostL2 = preBalL2.add(amt); + expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); + expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); + }).timeout(2400000) + + it("should not withdraw unmapped token", async() => { + let unMappedToken = ethers.utils.getAddress("0xccC8cb5229B0ac8069C51fd58367Fd1e622aFD97"); + let amt = ethers.utils.parseEther("0.5"); + let bridgeFee = ethers.utils.parseEther("1.0"); + + // Token withdraw L2 to L1 + let [priorityFee, maxFee] = await getFee(childProvider); + await expect(childBridge.connect(childTestWallet).withdraw(unMappedToken, amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + })).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); + }).timeout(2400000) + + // Local only + it("should not withdraw mapped ERC20 Token if child bridge is paused", async() => { + // Transfer 0.1 IMX to child pauser + let resp = await childTestWallet.sendTransaction({ + to: childPauserWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, childProvider); + + // Transfer 0.1 IMX to child unpauser + resp = await childTestWallet.sendTransaction({ + to: childPrivilegedWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, childProvider); + + // Pause child bridge + if (!await childBridge.paused()) { + resp = await childBridge.connect(childPauserWallet).pause(); + await waitForReceipt(resp.hash, childProvider); + expect(await childBridge.paused()).to.true; + } + + let amt = ethers.utils.parseEther("0.5"); + let bridgeFee = ethers.utils.parseEther("1.0"); + + // Token withdraw L2 to L1 + let [priorityFee, maxFee] = await getFee(childProvider); + await expect(childBridge.connect(childTestWallet).withdraw(childCustomToken.address, amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + })).to.be.rejectedWith("Pausable: paused"); + + // Unpause child bridge + resp = await childBridge.connect(childPrivilegedWallet).unpause(); + await waitForReceipt(resp.hash, childProvider); + expect(await childBridge.paused()).to.false; + }).timeout(2400000) + + it("should not withdraw mapped ERC20 Token if balance is insufficient", async() => { + let amt = await childCustomToken.balanceOf(childTestWallet.address); + let bridgeFee = ethers.utils.parseEther("1.0"); + + // ETH withdraw L2 to L1 + let [priorityFee, maxFee] = await getFee(childProvider); + await expect(childBridge.connect(childTestWallet).withdraw(childCustomToken.address, amt.add(1), { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + })).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); + }).timeout(2400000) + it("should successfully withdraw mapped ERC20 Token to self from L2 to L1", async() => { // Get token balance on root & child chains before deposit let preBalL1 = await rootCustomToken.balanceOf(rootTestWallet.address); @@ -389,4 +1768,121 @@ describe("Bridge e2e test", () => { expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); }).timeout(2400000) + + it("should successfully withdraw mapped ERC20 Token to others from L2 to L1", async() => { + let rootRecipient = rootPrivilegedWallet.address; + // Get token balance on root & child chains before deposit + let preBalL1 = await rootCustomToken.balanceOf(rootRecipient); + let preBalL2 = await childCustomToken.balanceOf(childTestWallet.address); + + let amt = ethers.utils.parseEther("0.5"); + let bridgeFee = ethers.utils.parseEther("1.0"); + + // Token withdraw L2 to L1 + let [priorityFee, maxFee] = await getFee(childProvider); + let resp = await childBridge.connect(childTestWallet).withdrawTo(childCustomToken.address, rootRecipient, amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }) + await waitForReceipt(resp.hash, childProvider); + + let postBalL1 = preBalL1; + let postBalL2 = await childCustomToken.balanceOf(childTestWallet.address); + + await waitUntilSucceed(axelarAPI, resp.hash); + + while (postBalL1.eq(preBalL1)) { + postBalL1 = await rootCustomToken.balanceOf(rootRecipient); + await delay(1000); + } + + // Verify + let expectedPostL1 = preBalL1.add(amt); + let expectedPostL2 = preBalL2.sub(amt); + expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); + expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); + }).timeout(2400000) + + it("should put mapped ERC20 Token withdrawal in pending when violating rate limit policy", async() => { + // Set new rate limit + let resp = await rootBridge.connect(rootPrivilegedWallet).setRateControlThreshold(rootCustomToken.address, ethers.utils.parseEther("1.0008"), ethers.utils.parseEther("0.000278"), ethers.utils.parseEther("0.5004")); + await waitForReceipt(resp.hash, rootProvider); + + // Withdraw of ERC20 exceeding large threshold + // Get ERC20 balance on root & child chains before withdraw + let preBalL1 = await rootCustomToken.balanceOf(rootTestWallet.address); + let preBalL2 = await childCustomToken.balanceOf(childTestWallet.address); + let preLength = await rootBridge.getPendingWithdrawalsLength(rootTestWallet.address); + + let amt1 = ethers.utils.parseEther("0.6"); + let bridgeFee1 = ethers.utils.parseEther("1.0"); + + // ERC20 withdraw L2 to L1 + let [priorityFee, maxFee] = await getFee(childProvider); + resp = await childBridge.connect(childTestWallet).withdraw(childCustomToken.address, amt1, { + value: bridgeFee1, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + await waitUntilSucceed(axelarAPI, resp.hash); + + while ((await rootBridge.getPendingWithdrawalsLength(rootTestWallet.address)).eq(preLength)) { + await delay(1000); + } + + // Withdraw of ERC20 exceeding rate limit + let amt2 = ethers.utils.parseEther("0.5"); + let bridgeFee2 = ethers.utils.parseEther("1.0"); + + // ERC20 withdraw L2 to L1 + [priorityFee, maxFee] = await getFee(childProvider); + resp = await childBridge.connect(childTestWallet).withdraw(childCustomToken.address, amt2, { + value: bridgeFee2, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + await waitUntilSucceed(axelarAPI, resp.hash); + + while ((await rootBridge.getPendingWithdrawalsLength(rootTestWallet.address)).eq(preLength.add(1))) { + await delay(1000); + } + + // Try to withdraw + await expect(rootBridge.connect(rootTestWallet).finaliseQueuedWithdrawal(rootTestWallet.address, preLength.add(1))).to.be.rejectedWith( + "UNPREDICTABLE_GAS_LIMIT" + ); + + // Fast-forward to 24 hours later. + await rootProvider.send( + "hardhat_mine", [ + "0x15181", // 24 hours + ]); + + // Withdraw again + resp = await rootBridge.connect(rootTestWallet).finaliseQueuedWithdrawal(rootTestWallet.address, preLength.add(1)) + await waitForReceipt(resp.hash, rootProvider); + + resp = await rootBridge.connect(rootTestWallet).finaliseQueuedWithdrawal(rootTestWallet.address, preLength) + await waitForReceipt(resp.hash, rootProvider); + + let postBalL1 = await rootCustomToken.balanceOf(rootTestWallet.address); + let postBalL2 = await childCustomToken.balanceOf(childTestWallet.address); + + // Verify + let expectedPostL1 = preBalL1.add(amt1).add(amt2); + let expectedPostL2 = preBalL2.sub(amt1).sub(amt2); + expect(postBalL1.toBigInt()).to.equal(expectedPostL1.toBigInt()); + expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); + + // Recover rate limit + resp = await rootBridge.connect(rootPrivilegedWallet).setRateControlThreshold(rootCustomToken.address, ethers.utils.parseEther("20016.0"), ethers.utils.parseEther("5.56"), ethers.utils.parseEther("10008.0")); + await waitForReceipt(resp.hash, rootProvider); + + // Deactive withdraw queue + resp = await rootBridge.connect(rootPrivilegedWallet).deactivateWithdrawalQueue(); + await waitForReceipt(resp.hash, rootProvider); + }).timeout(2400000) }) \ No newline at end of file diff --git a/scripts/localdev/.env.local b/scripts/localdev/.env.local index 8633ddc7..da8b99ad 100644 --- a/scripts/localdev/.env.local +++ b/scripts/localdev/.env.local @@ -110,4 +110,6 @@ TEST_ACCOUNT_SECRET=92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1 ROOT_EOA_SECRET=df57089febbacf7ba0bc227dafbffa9fc08a93fdc68e1e42411a14efcf23656e AXELAR_ROOT_EOA_SECRET=5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a AXELAR_CHILD_EOA_SECRET=5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a -AXELAR_DEPLOYER_SECRET=7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6 \ No newline at end of file +AXELAR_DEPLOYER_SECRET=7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6 +BREAKGLASS_EOA_SECRET=dbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97 +PRIVILEGED_EOA_SECRET=4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356 \ No newline at end of file diff --git a/scripts/localdev/rootchain_setup.ts b/scripts/localdev/rootchain_setup.ts index c31fe00f..523b528e 100644 --- a/scripts/localdev/rootchain_setup.ts +++ b/scripts/localdev/rootchain_setup.ts @@ -14,6 +14,8 @@ async function main() { let reservedAddr = requireEnv("NONCE_RESERVED_DEPLOYER_ADDR"); let axelarEOA = requireEnv("AXELAR_EOA"); let rootTestKey = requireEnv("TEST_ACCOUNT_SECRET"); + let rootBreakGlassAddr = requireEnv("ROOT_BREAKGLASS_ADDR"); + let rootPrivilegedAddr = requireEnv("ROOT_PRIVILEGED_MULTISIG_ADDR"); // Get root provider. let rootProvider = new RetryProvider(rootRPCURL, Number(rootChainID)); @@ -46,8 +48,8 @@ async function main() { let resp = await IMX.connect(admin).mint(deployerAddr, ethers.utils.parseEther("1110.0")); await waitForReceipt(resp.hash, rootProvider); - // Transfer 1000 IMX to test wallet - resp = await IMX.connect(admin).mint(testWallet.address, ethers.utils.parseEther("1000.0")) + // Transfer 1000000000 IMX to test wallet + resp = await IMX.connect(admin).mint(testWallet.address, ethers.utils.parseEther("1000000000.0")) await waitForReceipt(resp.hash, rootProvider); // Transfer 0.1 ETH to root deployer @@ -70,10 +72,10 @@ async function main() { value: ethers.utils.parseEther("500.0"), }) - // Transfer 10 ETH to test wallet + // Transfer 1000 ETH to test wallet resp = await admin.sendTransaction({ to: testWallet.address, - value: ethers.utils.parseEther("10.0"), + value: ethers.utils.parseEther("1000.0"), }) await waitForReceipt(resp.hash, rootProvider); diff --git a/yarn.lock b/yarn.lock index e8988aaf..dd5c0ce9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1640,7 +1640,7 @@ "@types/node" "*" "@types/responselike" "^1.0.0" -"@types/chai-as-promised@^7.1.3": +"@types/chai-as-promised@^7.1.3", "@types/chai-as-promised@^7.1.8": version "7.1.8" resolved "https://registry.yarnpkg.com/@types/chai-as-promised/-/chai-as-promised-7.1.8.tgz#f2b3d82d53c59626b5d6bbc087667ccb4b677fe9" integrity sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw== From 1f3db34c7ec17bc019cc675f05f462a6ae5bc0f1 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Mon, 29 Jan 2024 14:40:34 +1000 Subject: [PATCH 29/40] Fix CI --- .github/workflows/coverage.yml | 2 +- .github/workflows/e2e.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/test.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 3a4d7b13..2176a7b0 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -25,7 +25,7 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: - version: nightly-34f684ddfacc5b2ed371353ba6f730c485616ffe + version: nightly-caef1360e29dfefb1723fa501f425e6f7824bf7f - name: Run Forge build run: | diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index fde67cd7..dcc988ff 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -21,7 +21,7 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: - version: nightly-34f684ddfacc5b2ed371353ba6f730c485616ffe + version: nightly-caef1360e29dfefb1723fa501f425e6f7824bf7f - name: Run install uses: borales/actions-yarn@v4 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 73c7580d..418e54cb 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -20,7 +20,7 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: - version: nightly-34f684ddfacc5b2ed371353ba6f730c485616ffe + version: nightly-caef1360e29dfefb1723fa501f425e6f7824bf7f - name: Run Forge fmt --check run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 52f77ec5..c8879c5d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,7 +20,7 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: - version: nightly-34f684ddfacc5b2ed371353ba6f730c485616ffe + version: nightly-caef1360e29dfefb1723fa501f425e6f7824bf7f - name: Run Forge build run: | From 2a730d2e851e48c3948a68ab46f47708150b0899 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Mon, 29 Jan 2024 14:49:29 +1000 Subject: [PATCH 30/40] Lint --- test/integration/root/RootERC20BridgeFlowRate.t.sol | 5 ----- test/unit/child/ChildAxelarBridgeAdaptor.t.sol | 7 ------- test/unit/child/ChildERC20Bridge.t.sol | 8 -------- .../unit/child/withdrawals/ChildERC20BridgeWithdraw.t.sol | 1 - .../child/withdrawals/ChildERC20BridgeWithdrawETH.t.sol | 1 - .../child/withdrawals/ChildERC20BridgeWithdrawETHTo.t.sol | 1 - .../child/withdrawals/ChildERC20BridgeWithdrawIMX.t.sol | 1 - .../child/withdrawals/ChildERC20BridgeWithdrawIMXTo.t.sol | 1 - .../child/withdrawals/ChildERC20BridgeWithdrawTo.t.sol | 1 - .../child/withdrawals/ChildERC20BridgeWithdrawWIMX.t.sol | 1 - .../withdrawals/ChildERC20BridgeWithdrawWIMXTo.t.sol | 1 - test/unit/deploy/OwnableCreate2Deployer.t.sol | 3 --- test/unit/root/RootAxelarBridgeAdaptor.t.sol | 6 ------ test/unit/root/RootERC20Bridge.t.sol | 8 -------- test/unit/root/flowrate/RootERC20BridgeFlowRate.t.sol | 3 --- 15 files changed, 48 deletions(-) diff --git a/test/integration/root/RootERC20BridgeFlowRate.t.sol b/test/integration/root/RootERC20BridgeFlowRate.t.sol index cd12454f..17b00b3e 100644 --- a/test/integration/root/RootERC20BridgeFlowRate.t.sol +++ b/test/integration/root/RootERC20BridgeFlowRate.t.sol @@ -61,7 +61,6 @@ contract RootERC20BridgeFlowRateIntegrationTest is * This test uses the same code as the mapToken function does to calculate this address, so we can * not consider it sufficient. */ - function test_mapTokenTransfersValue() public { address childToken = Clones.predictDeterministicAddress(address(token), keccak256(abi.encodePacked(token)), CHILD_BRIDGE); @@ -135,7 +134,6 @@ contract RootERC20BridgeFlowRateIntegrationTest is /** * DEPOSIT ETH */ - function test_depositETHTransfersValue() public { uint256 tokenAmount = 300; setupDeposit(NATIVE_ETH, rootBridgeFlowRate, mapTokenFee, depositFee, tokenAmount, false); @@ -211,7 +209,6 @@ contract RootERC20BridgeFlowRateIntegrationTest is /** * DEPOSIT IMX */ - function test_depositIMXTokenTransfersValue() public { uint256 tokenAmount = 300; @@ -292,7 +289,6 @@ contract RootERC20BridgeFlowRateIntegrationTest is /** * DEPOSIT WETH */ - function test_depositWETHTransfersValue() public { uint256 tokenAmount = 300; setupDeposit(WRAPPED_ETH, rootBridgeFlowRate, mapTokenFee, depositFee, tokenAmount, false); @@ -446,7 +442,6 @@ contract RootERC20BridgeFlowRateIntegrationTest is /** * DEPOSIT TO */ - function test_depositToTransfersValue() public { uint256 tokenAmount = 300; address recipient = address(9876); diff --git a/test/unit/child/ChildAxelarBridgeAdaptor.t.sol b/test/unit/child/ChildAxelarBridgeAdaptor.t.sol index 87010030..73182c59 100644 --- a/test/unit/child/ChildAxelarBridgeAdaptor.t.sol +++ b/test/unit/child/ChildAxelarBridgeAdaptor.t.sol @@ -56,7 +56,6 @@ contract ChildAxelarBridgeAdaptorUnitTest is Test, IChildAxelarBridgeAdaptorErro /** * INITIALIZE */ - function test_Initialize() public { assertEq(address(axelarAdaptor.childBridge()), address(mockChildERC20Bridge), "childBridge not set"); assertEq(axelarAdaptor.rootChainId(), ROOT_CHAIN_NAME, "rootChain not set"); @@ -179,7 +178,6 @@ contract ChildAxelarBridgeAdaptorUnitTest is Test, IChildAxelarBridgeAdaptorErro /** * EXECUTE */ - function test_RevertIf_executeCalledWithInvalidSourceChain() public { bytes32 commandId = bytes32("testCommandId"); bytes memory payload = abi.encodePacked("payload"); @@ -221,7 +219,6 @@ contract ChildAxelarBridgeAdaptorUnitTest is Test, IChildAxelarBridgeAdaptorErro /** * SEND MESSAGE */ - function test_sendMessage_CallsGasService() public { address refundRecipient = address(123); bytes memory payload = abi.encode(WITHDRAW_SIG, address(token), address(this), address(999), 11111); @@ -342,7 +339,6 @@ contract ChildAxelarBridgeAdaptorUnitTest is Test, IChildAxelarBridgeAdaptorErro /** * UPDATE CHILD BRIDGE */ - function test_updateChildBridge_UpdatesChildBridge() public { vm.startPrank(bridgeManager); address newChildBridge = address(0x123); @@ -384,7 +380,6 @@ contract ChildAxelarBridgeAdaptorUnitTest is Test, IChildAxelarBridgeAdaptorErro /** * UPDATE ROOT CHAIN */ - function test_updateRootChain_UpdatesRootChain() public { vm.startPrank(targetManager); string memory newRootChain = "newRoot"; @@ -425,7 +420,6 @@ contract ChildAxelarBridgeAdaptorUnitTest is Test, IChildAxelarBridgeAdaptorErro /** * UPDATE ROOT BRIDGE ADAPTOR */ - function test_updateRootBridgeAdaptor_UpdatesRootBridgeAdaptor() public { vm.startPrank(targetManager); string memory newAdaptor = "newAdaptor"; @@ -470,7 +464,6 @@ contract ChildAxelarBridgeAdaptorUnitTest is Test, IChildAxelarBridgeAdaptorErro /** * UPDATE GAS SERVICE */ - function test_updateGasService_UpdatesGasService() public { vm.startPrank(gasServiceManager); address newGasService = address(0x123); diff --git a/test/unit/child/ChildERC20Bridge.t.sol b/test/unit/child/ChildERC20Bridge.t.sol index e93f334e..cc70efe2 100644 --- a/test/unit/child/ChildERC20Bridge.t.sol +++ b/test/unit/child/ChildERC20Bridge.t.sol @@ -67,7 +67,6 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B /** * RECEIVE */ - function test_NativeTransferFromWIMX() public { address caller = address(0x123a); payable(caller).transfer(2 ether); @@ -107,7 +106,6 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B /** * TREASURY DEPOSIT */ - function test_treasuryDepostIncreasesBalance() public { vm.deal(treasuryManager, 100 ether); vm.startPrank(treasuryManager); @@ -150,7 +148,6 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B /** * INITIALIZE */ - function test_Initialize() public { assertEq(address(childBridge.childBridgeAdaptor()), address(address(this)), "bridgeAdaptor not set"); assertEq(childBridge.childTokenTemplate(), address(childTokenTemplate), "childTokenTemplate not set"); @@ -257,7 +254,6 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B /** * UPDATE CHILD BRIDGE ADAPTOR */ - function test_updateChildBridgeAdaptor_UpdatesChildBridgeAdaptor() public { address newAdaptorAddress = address(0x11111); @@ -298,7 +294,6 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B /** * ON MESSAGE RECIEVE */ - function test_onMessageReceive_SetsTokenMapping() public { address predictedChildToken = Clones.predictDeterministicAddress( address(childTokenTemplate), keccak256(abi.encodePacked(rootToken)), address(childBridge) @@ -387,7 +382,6 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B /** * DEPOSIT ETH */ - function test_RevertsIf_OnMessageReceiveWhenPaused() public { pause(IPausable(address(childBridge))); bytes memory depositData = @@ -461,7 +455,6 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B /** * DEPOSIT */ - function test_onMessageReceive_DepositIMX_EmitsIMXDepositEvent() public { uint256 fundedAmount = 10 ether; vm.deal(address(childBridge), fundedAmount); @@ -616,7 +609,6 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B /** * WITHDRAW */ - function test_RevertIf_WithdrawReentered() public { // Create attack token vm.startPrank(address(childBridge)); diff --git a/test/unit/child/withdrawals/ChildERC20BridgeWithdraw.t.sol b/test/unit/child/withdrawals/ChildERC20BridgeWithdraw.t.sol index dd3dea5e..9b564788 100644 --- a/test/unit/child/withdrawals/ChildERC20BridgeWithdraw.t.sol +++ b/test/unit/child/withdrawals/ChildERC20BridgeWithdraw.t.sol @@ -62,7 +62,6 @@ contract ChildERC20BridgeWithdrawUnitTest is Test, IChildERC20BridgeEvents, IChi /** * WITHDRAW */ - function test_RevertsIf_WithdrawWhenPaused() public { pause(IPausable(address(childBridge))); vm.expectRevert("Pausable: paused"); diff --git a/test/unit/child/withdrawals/ChildERC20BridgeWithdrawETH.t.sol b/test/unit/child/withdrawals/ChildERC20BridgeWithdrawETH.t.sol index b564870f..c4fff9d4 100644 --- a/test/unit/child/withdrawals/ChildERC20BridgeWithdrawETH.t.sol +++ b/test/unit/child/withdrawals/ChildERC20BridgeWithdrawETH.t.sol @@ -49,7 +49,6 @@ contract ChildERC20BridgeWithdrawETHUnitTest is Test, IChildERC20BridgeEvents, I /** * WITHDRAW ETH */ - function test_RevertsIf_WithdrawETHWhenPaused() public { pause(IPausable(address(childBridge))); vm.expectRevert("Pausable: paused"); diff --git a/test/unit/child/withdrawals/ChildERC20BridgeWithdrawETHTo.t.sol b/test/unit/child/withdrawals/ChildERC20BridgeWithdrawETHTo.t.sol index 21708c0f..efb39dd7 100644 --- a/test/unit/child/withdrawals/ChildERC20BridgeWithdrawETHTo.t.sol +++ b/test/unit/child/withdrawals/ChildERC20BridgeWithdrawETHTo.t.sol @@ -69,7 +69,6 @@ contract ChildERC20BridgeWithdrawETHToUnitTest is Test, IChildERC20BridgeEvents, /** * WITHDRAW ETH TO */ - function test_RevertsIf_WithdrawETHToWhenPaused() public { pause(IPausable(address(childBridge))); vm.expectRevert("Pausable: paused"); diff --git a/test/unit/child/withdrawals/ChildERC20BridgeWithdrawIMX.t.sol b/test/unit/child/withdrawals/ChildERC20BridgeWithdrawIMX.t.sol index c48c08d8..31653971 100644 --- a/test/unit/child/withdrawals/ChildERC20BridgeWithdrawIMX.t.sol +++ b/test/unit/child/withdrawals/ChildERC20BridgeWithdrawIMX.t.sol @@ -44,7 +44,6 @@ contract ChildERC20BridgeWithdrawIMXUnitTest is Test, IChildERC20BridgeEvents, I /** * WITHDRAW IMX */ - function test_RevertIf_WithdrawIMXWhenPaused() public { pause(IPausable(address(childBridge))); vm.expectRevert("Pausable: paused"); diff --git a/test/unit/child/withdrawals/ChildERC20BridgeWithdrawIMXTo.t.sol b/test/unit/child/withdrawals/ChildERC20BridgeWithdrawIMXTo.t.sol index b2fa6b72..8676cdb9 100644 --- a/test/unit/child/withdrawals/ChildERC20BridgeWithdrawIMXTo.t.sol +++ b/test/unit/child/withdrawals/ChildERC20BridgeWithdrawIMXTo.t.sol @@ -48,7 +48,6 @@ contract ChildERC20BridgeWithdrawIMXToUnitTest is Test, IChildERC20BridgeEvents, /** * WITHDRAW IMX TO */ - function test_RevertsIf_WithdrawIMXToWhenPaused() public { pause(IPausable(address(childBridge))); vm.expectRevert("Pausable: paused"); diff --git a/test/unit/child/withdrawals/ChildERC20BridgeWithdrawTo.t.sol b/test/unit/child/withdrawals/ChildERC20BridgeWithdrawTo.t.sol index 88cc2b63..9bc0dc90 100644 --- a/test/unit/child/withdrawals/ChildERC20BridgeWithdrawTo.t.sol +++ b/test/unit/child/withdrawals/ChildERC20BridgeWithdrawTo.t.sol @@ -62,7 +62,6 @@ contract ChildERC20BridgeWithdrawToUnitTest is Test, IChildERC20BridgeEvents, IC /** * WITHDRAW TO */ - function test_RevertsIf_WithdrawToWhenPaused() public { pause(IPausable(address(childBridge))); vm.expectRevert("Pausable: paused"); diff --git a/test/unit/child/withdrawals/ChildERC20BridgeWithdrawWIMX.t.sol b/test/unit/child/withdrawals/ChildERC20BridgeWithdrawWIMX.t.sol index 0d1b314c..f7a38208 100644 --- a/test/unit/child/withdrawals/ChildERC20BridgeWithdrawWIMX.t.sol +++ b/test/unit/child/withdrawals/ChildERC20BridgeWithdrawWIMX.t.sol @@ -48,7 +48,6 @@ contract ChildERC20BridgeWithdrawWIMXUnitTest is Test, IChildERC20BridgeEvents, /** * WITHDRAW WIMX */ - function test_RevertsIf_WithdrawWIMXWhenPaused() public { pause(IPausable(address(childBridge))); vm.expectRevert("Pausable: paused"); diff --git a/test/unit/child/withdrawals/ChildERC20BridgeWithdrawWIMXTo.t.sol b/test/unit/child/withdrawals/ChildERC20BridgeWithdrawWIMXTo.t.sol index 83a6da3f..070cec06 100644 --- a/test/unit/child/withdrawals/ChildERC20BridgeWithdrawWIMXTo.t.sol +++ b/test/unit/child/withdrawals/ChildERC20BridgeWithdrawWIMXTo.t.sol @@ -50,7 +50,6 @@ contract ChildERC20BridgeWithdrawWIMXToUnitTest is Test, IChildERC20BridgeEvents /** * WITHDRAW WIMX TO */ - function test_RevertsIf_WithdrawWIMXToWhenPaused() public { pause(IPausable(address(childBridge))); vm.expectRevert("Pausable: paused"); diff --git a/test/unit/deploy/OwnableCreate2Deployer.t.sol b/test/unit/deploy/OwnableCreate2Deployer.t.sol index a0bf0bce..1b89ef8b 100644 --- a/test/unit/deploy/OwnableCreate2Deployer.t.sol +++ b/test/unit/deploy/OwnableCreate2Deployer.t.sol @@ -109,7 +109,6 @@ contract OwnableCreate2DeployerTest is Test { /** * deployAndInit */ - function test_RevertIf_DeployAndInitWithNonOwner() public { vm.stopPrank(); @@ -148,7 +147,6 @@ contract OwnableCreate2DeployerTest is Test { /** * deployedAddress */ - function test_deployedAddress_ReturnsPredictedAddress() public { address deployAddress = deployer.deployedAddress(childERC20Bytecode, address(owner), salt); @@ -162,7 +160,6 @@ contract OwnableCreate2DeployerTest is Test { /** * private helper functions */ - function predictCreate2Address(bytes memory _bytecode, address _deployer, address _sender, bytes32 _salt) private pure diff --git a/test/unit/root/RootAxelarBridgeAdaptor.t.sol b/test/unit/root/RootAxelarBridgeAdaptor.t.sol index a9c3451c..949afd50 100644 --- a/test/unit/root/RootAxelarBridgeAdaptor.t.sol +++ b/test/unit/root/RootAxelarBridgeAdaptor.t.sol @@ -55,7 +55,6 @@ contract RootAxelarBridgeAdaptorTest is Test, IRootAxelarBridgeAdaptorEvents, IR /** * INITIALIZE */ - function test_Initialize() public { assertEq(address(axelarAdaptor.rootBridge()), address(stubRootBridge), "rootBridge not set"); assertEq(axelarAdaptor.childChainId(), CHILD_CHAIN_NAME, "childChain not set"); @@ -281,7 +280,6 @@ contract RootAxelarBridgeAdaptorTest is Test, IRootAxelarBridgeAdaptorEvents, IR /** * MAP TOKEN */ - function test_RevertIf_mapTokenCalledByNonRootBridge() public { address payable prankster = payable(address(0x33)); uint256 value = 300; @@ -304,7 +302,6 @@ contract RootAxelarBridgeAdaptorTest is Test, IRootAxelarBridgeAdaptorEvents, IR /** * UPDATE ROOT BRIDGE */ - function test_updateRootBridge_UpdatesRootBridge() public { vm.startPrank(bridgeManager); address newRootBridge = address(0x3333); @@ -344,7 +341,6 @@ contract RootAxelarBridgeAdaptorTest is Test, IRootAxelarBridgeAdaptorEvents, IR /** * UPDATE CHILD CHAIN */ - function test_updateChildChain_UpdatesChildChain() public { vm.startPrank(targetManager); string memory newChildChain = "newChildChain"; @@ -384,7 +380,6 @@ contract RootAxelarBridgeAdaptorTest is Test, IRootAxelarBridgeAdaptorEvents, IR /** * UPDATE CHILD BRIDGE ADAPTOR */ - function test_updateChildBridgeAdaptor_UpdatesChildBridgeAdaptor() public { vm.startPrank(targetManager); @@ -428,7 +423,6 @@ contract RootAxelarBridgeAdaptorTest is Test, IRootAxelarBridgeAdaptorEvents, IR /** * UPDATE GAS SERVICE */ - function test_updateGasService_UpdatesGasService() public { vm.startPrank(gasServiceManager); address newGasService = address(0x3333); diff --git a/test/unit/root/RootERC20Bridge.t.sol b/test/unit/root/RootERC20Bridge.t.sol index 51ab4364..8b63e1e2 100644 --- a/test/unit/root/RootERC20Bridge.t.sol +++ b/test/unit/root/RootERC20Bridge.t.sol @@ -84,7 +84,6 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid /** * INITIALIZE */ - function test_InitializeBridge() public { assertEq(address(rootBridge.rootBridgeAdaptor()), address(mockAxelarAdaptor), "bridgeAdaptor not set"); assertEq(rootBridge.childERC20Bridge(), CHILD_BRIDGE, "childERC20Bridge not set"); @@ -339,7 +338,6 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid /** * MAP TOKEN */ - function test_RevertsIf_MapTokenWhenPaused() public { pause(IPausable(address(rootBridge))); vm.expectRevert("Pausable: paused"); @@ -497,7 +495,6 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid /** * DEPOSIT ETH */ - function test_RevertsIf_DepositETHWhenPaused() public { pause(IPausable(address(rootBridge))); vm.expectRevert("Pausable: paused"); @@ -544,7 +541,6 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid /** * DEPOSIT TO ETH */ - function test_RevertsIf_DepositToETHWhenPaused() public { pause(IPausable(address(rootBridge))); vm.expectRevert("Pausable: paused"); @@ -594,7 +590,6 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid /** * ZERO AMOUNT */ - function test_RevertIf_depositETHAmountIsZero() public { uint256 amount = 0; setupDeposit(NATIVE_ETH, rootBridge, mapTokenFee, depositFee, amount, false); @@ -633,7 +628,6 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid /** * DEPOSIT WETH */ - function test_depositWETHCallsSendMessage() public { uint256 amount = 100; (, bytes memory predictedPayload) = @@ -708,7 +702,6 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid /** * DEPOSIT TOKEN */ - function test_RevertsIf_DepositReentered() public { // Create attack token ReentrancyAttackDeposit attackToken = new ReentrancyAttackDeposit(address(rootBridge)); @@ -916,7 +909,6 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid /** * DEPOSIT TO */ - function test_RevertsIf_DepositToWhenPaused() public { pause(IPausable(address(rootBridge))); vm.expectRevert("Pausable: paused"); diff --git a/test/unit/root/flowrate/RootERC20BridgeFlowRate.t.sol b/test/unit/root/flowrate/RootERC20BridgeFlowRate.t.sol index 1f435afb..d7f4d664 100644 --- a/test/unit/root/flowrate/RootERC20BridgeFlowRate.t.sol +++ b/test/unit/root/flowrate/RootERC20BridgeFlowRate.t.sol @@ -203,7 +203,6 @@ contract RootERC20BridgeFlowRateUnitTest is /** * INITIALIZE */ - function test_InitializeBridgeFlowRate() public { assertEq(address(rootBridgeFlowRate.rootBridgeAdaptor()), address(mockAxelarAdaptor), "bridgeAdaptor not set"); assertEq(rootBridgeFlowRate.childERC20Bridge(), CHILD_BRIDGE, "childERC20Bridge not set"); @@ -281,7 +280,6 @@ contract RootERC20BridgeFlowRateUnitTest is /** * RATE ROLE ACTIONS */ - function testActivateWithdrawalQueue() public { vm.prank(rateAdmin); rootBridgeFlowRate.activateWithdrawalQueue(); @@ -504,7 +502,6 @@ contract RootERC20BridgeFlowRateUnitTest is /** * FLOW RATE WITHDRAW */ - function testWithdrawalUnconfiguredToken() public { transferTokensToChild(); From 9cfd57fe72650aa747381c8262e0d81b69ac5142 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Wed, 31 Jan 2024 13:37:32 +1000 Subject: [PATCH 31/40] Add test case --- package.json | 2 +- scripts/e2e/e2e.ts | 142 +++++++++++++++++++++++++++++++++++++ scripts/helpers/helpers.ts | 1 - 3 files changed, 143 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index aa213c2b..f3648521 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "lint": "forge fmt", "local:start": "cd scripts/localdev; ./start.sh", "local:setup": "cd scripts/localdev; rm -rf .child.bridge.contracts.json .root.bridge.contracts.json; ./deploy.sh", - "local:test": "cd scripts/localdev; AXELAR_API_URL=skip npx mocha --require mocha-suppress-logs ../e2e/e2e.ts", + "local:test": "cd scripts/localdev; AXELAR_API_URL=skip npx mocha ../e2e/e2e.ts", "local:ci": "cd scripts/localdev; rm -rf .child.bridge.contracts.json .root.bridge.contracts.json; ./ci.sh && ./deploy.sh && AXELAR_API_URL=skip npx mocha --require mocha-suppress-logs ../e2e/e2e.ts && ./stop.sh", "local:chainonly": "cd scripts/localdev; LOCAL_CHAIN_ONLY=true ./start.sh", "local:axelaronly": "cd scripts/localdev; npx ts-node axelar_setup.ts", diff --git a/scripts/e2e/e2e.ts b/scripts/e2e/e2e.ts index 7e0175bb..712fbe1a 100644 --- a/scripts/e2e/e2e.ts +++ b/scripts/e2e/e2e.ts @@ -1804,6 +1804,7 @@ describe("Bridge e2e test", () => { expect(postBalL2.toBigInt()).to.equal(expectedPostL2.toBigInt()); }).timeout(2400000) + // Local only it("should put mapped ERC20 Token withdrawal in pending when violating rate limit policy", async() => { // Set new rate limit let resp = await rootBridge.connect(rootPrivilegedWallet).setRateControlThreshold(rootCustomToken.address, ethers.utils.parseEther("1.0008"), ethers.utils.parseEther("0.000278"), ethers.utils.parseEther("0.5004")); @@ -1885,4 +1886,145 @@ describe("Bridge e2e test", () => { resp = await rootBridge.connect(rootPrivilegedWallet).deactivateWithdrawalQueue(); await waitForReceipt(resp.hash, rootProvider); }).timeout(2400000) + + // Local only + it("should successfully process multiple deposit and withdrawal requests in parallel", async() => { + // Deposit & withdrawal amount + let amtL1 = ethers.utils.parseEther("1.0"); + let bridgeFeeL1 = ethers.utils.parseEther("0.001"); + let amtL2 = ethers.utils.parseEther("0.5"); + let bridgeFeeL2 = ethers.utils.parseEther("1.0"); + + // Wrap & Approval + let resp = await rootIMX.connect(rootTestWallet).approve(rootBridge.address, amtL1); + await waitForReceipt(resp.hash, rootProvider); + + resp = await rootWETH.connect(rootTestWallet).deposit({ value: amtL1 }); + await waitForReceipt(resp.hash, rootProvider); + resp = await rootWETH.connect(rootTestWallet).approve(rootBridge.address, amtL1); + await waitForReceipt(resp.hash, rootProvider); + + resp = await rootCustomToken.connect(rootTestWallet).approve(rootBridge.address, amtL1); + await waitForReceipt(resp.hash, rootProvider); + + resp = await childWIMX.connect(childTestWallet).deposit( {value: amtL2 }); + await waitForReceipt(resp.hash, childProvider); + resp = await childWIMX.connect(childTestWallet).approve(childBridge.address, amtL2); + await waitForReceipt(resp.hash, childProvider); + + // Deposit IMX, ETH, WETH, ERC20, and withdraw IMX, WIMX, ETH, ERC20 + let preIMXBalL1 = await rootIMX.balanceOf(rootTestWallet.address); + let preETHBalL1 = await rootProvider.getBalance(rootTestWallet.address); + let preWETHBalL1 = await rootWETH.balanceOf(rootTestWallet.address); + let preERC20BalL1 = await rootCustomToken.balanceOf(rootTestWallet.address); + let preIMXBalL2 = await childProvider.getBalance(childTestWallet.address); + let preWIMXBalL2 = await childWIMX.balanceOf(childTestWallet.address); + let preETHBalL2 = await childETH.balanceOf(childTestWallet.address); + let preERC20BalL2 = await childCustomToken.balanceOf(childTestWallet.address); + + // Stop mining + await rootProvider.send( + "evm_setIntervalMining", [ + 0, + ]); + await childProvider.send( + "evm_setIntervalMining", [ + 0, + ]); + + // Calls on L1 & L2 + let resp1 = await rootBridge.connect(rootTestWallet).deposit(rootIMX.address, amtL1, { + value: bridgeFeeL1, + }); + let resp2 = await rootBridge.connect(rootTestWallet).depositETH(amtL1, { + value: bridgeFeeL1.add(amtL1), + }); + let resp3 = await rootBridge.connect(rootTestWallet).deposit(rootWETH.address, amtL1, { + value: bridgeFeeL1, + }); + let resp4 = await rootBridge.connect(rootTestWallet).deposit(rootCustomToken.address, amtL1, { + value: bridgeFeeL1, + }); + let resp5 = await childBridge.connect(childTestWallet).withdrawIMX(amtL2, { + value: bridgeFeeL2.add(amtL2), + }); + let resp6 = await childBridge.connect(childTestWallet).withdrawWIMX(amtL2, { + value: bridgeFeeL2, + }); + let resp7 = await childBridge.connect(childTestWallet).withdrawETH(amtL2, { + value: bridgeFeeL2, + }); + let resp8 = await childBridge.connect(childTestWallet).withdraw(childCustomToken.address, amtL2, { + value: bridgeFeeL2, + }); + + + // Enable mining + await rootProvider.send( + "evm_setIntervalMining", [ + 1200, + ]); + await childProvider.send( + "evm_setIntervalMining", [ + 200, + ]); + + // Wait for transactions to be mined. + await waitForReceipt(resp1.hash, rootProvider); + await waitForReceipt(resp2.hash, rootProvider); + await waitForReceipt(resp3.hash, rootProvider); + await waitForReceipt(resp4.hash, rootProvider); + await waitForReceipt(resp5.hash, childProvider); + await waitForReceipt(resp6.hash, childProvider); + await waitForReceipt(resp7.hash, childProvider); + await waitForReceipt(resp8.hash, childProvider); + + // Wait for 30 seconds + await delay(30000); + let receipt = await rootProvider.getTransactionReceipt(resp1.hash); + let txFee1 = receipt.gasUsed.mul(receipt.effectiveGasPrice); + receipt = await rootProvider.getTransactionReceipt(resp2.hash); + let txFee2 = receipt.gasUsed.mul(receipt.effectiveGasPrice); + receipt = await rootProvider.getTransactionReceipt(resp3.hash); + let txFee3 = receipt.gasUsed.mul(receipt.effectiveGasPrice); + receipt = await rootProvider.getTransactionReceipt(resp4.hash); + let txFee4 = receipt.gasUsed.mul(receipt.effectiveGasPrice); + receipt = await childProvider.getTransactionReceipt(resp5.hash); + let txFee5 = receipt.gasUsed.mul(receipt.effectiveGasPrice); + receipt = await childProvider.getTransactionReceipt(resp6.hash); + let txFee6 = receipt.gasUsed.mul(receipt.effectiveGasPrice); + receipt = await childProvider.getTransactionReceipt(resp7.hash); + let txFee7 = receipt.gasUsed.mul(receipt.effectiveGasPrice); + receipt = await childProvider.getTransactionReceipt(resp8.hash); + let txFee8 = receipt.gasUsed.mul(receipt.effectiveGasPrice); + + let postIMXBalL1 = await rootIMX.balanceOf(rootTestWallet.address); + let postETHBalL1 = await rootProvider.getBalance(rootTestWallet.address); + let postWETHBalL1 = await rootWETH.balanceOf(rootTestWallet.address); + let postERC20BalL1 = await rootCustomToken.balanceOf(rootTestWallet.address); + let postIMXBalL2 = await childProvider.getBalance(childTestWallet.address); + let postWIMXBalL2 = await childWIMX.balanceOf(childTestWallet.address); + let postETHBalL2 = await childETH.balanceOf(childTestWallet.address); + let postERC20BalL2 = await childCustomToken.balanceOf(childTestWallet.address); + + // Verify + let expectedIMXBalL1 = preIMXBalL1.sub(amtL1).add(amtL2.mul(2)); + let expectedETHBalL1 = preETHBalL1.sub(amtL1).add(amtL2).sub(bridgeFeeL1.mul(4)).sub(txFee1).sub(txFee2).sub(txFee3).sub(txFee4); + let expectedWETHBalL1 = preWETHBalL1.sub(amtL1); + let expectedERC20BalL1 = preERC20BalL1.sub(amtL1).add(amtL2); + let expectedIMXBalL2 = preIMXBalL2.add(amtL1).sub(amtL2).sub(bridgeFeeL2.mul(4)).sub(txFee5).sub(txFee6).sub(txFee7).sub(txFee8); + let expectedWIMXBalL2 = preWIMXBalL2.sub(amtL2); + let expectedETHBalL2 = preETHBalL2.add(amtL1.mul(2)).sub(amtL2); + let expectedERC20BalL2 = preERC20BalL2.add(amtL1).sub(amtL2); + expect(postIMXBalL1.toBigInt()).to.equal(expectedIMXBalL1.toBigInt()); + expect(postETHBalL1.toBigInt()).to.equal(expectedETHBalL1.toBigInt()); + expect(postWETHBalL1.toBigInt()).to.equal(expectedWETHBalL1.toBigInt()); + expect(postERC20BalL1.toBigInt()).to.equal(expectedERC20BalL1.toBigInt()); + expect(postIMXBalL2.toBigInt()).to.equal(expectedIMXBalL2.toBigInt()); + expect(postWIMXBalL2.toBigInt()).to.equal(expectedWIMXBalL2.toBigInt()); + expect(postETHBalL2.toBigInt()).to.equal(expectedETHBalL2.toBigInt()); + expect(postERC20BalL2.toBigInt()).to.equal(expectedERC20BalL2.toBigInt()); + + // Test balance. + }).timeout(2400000) }) \ No newline at end of file diff --git a/scripts/helpers/helpers.ts b/scripts/helpers/helpers.ts index c0ed555e..18992b13 100644 --- a/scripts/helpers/helpers.ts +++ b/scripts/helpers/helpers.ts @@ -27,7 +27,6 @@ export async function waitForReceipt(txHash: string, provider: providers.JsonRpc receipt = await provider.getTransactionReceipt(txHash) await exports.delay(1000); } - console.log("Receipt: " + JSON.stringify(receipt, null, 2)); if (receipt.status != 1) { throw("Fail to execute: " + txHash); } From ba857665b41fb5e3b4782c0f05feb6e8dea4d6bc Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Wed, 31 Jan 2024 14:25:37 +1000 Subject: [PATCH 32/40] Update e2e.yml --- .github/workflows/e2e.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 17a3cdde..099f4a7b 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -9,7 +9,7 @@ jobs: build: name: End to End Test runs-on: ubuntu-latest - timeout-minutes: 15 + timeout-minutes: 30 steps: - uses: actions/checkout@v3 From 9a678f60d2506a72e1a66239b1743946e2684727 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Mon, 12 Feb 2024 08:09:43 +1000 Subject: [PATCH 33/40] Delete OwnableCreate2Deployer.sol --- src/deploy/OwnableCreate2Deployer.sol | 47 --------------------------- 1 file changed, 47 deletions(-) delete mode 100644 src/deploy/OwnableCreate2Deployer.sol diff --git a/src/deploy/OwnableCreate2Deployer.sol b/src/deploy/OwnableCreate2Deployer.sol deleted file mode 100644 index 8aa5de05..00000000 --- a/src/deploy/OwnableCreate2Deployer.sol +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2023 -// SPDX-License-Identifier: Apache 2.0 -pragma solidity 0.8.19; - -import "@openzeppelin/contracts/access/Ownable.sol"; -import {Deployer} from "@axelar-gmp-sdk-solidity/contracts/deploy/Deployer.sol"; -import {Create2} from "@axelar-gmp-sdk-solidity/contracts/deploy/Create2.sol"; - -/** - * @title OwnableCreate2Deployer - * @notice Deploys and optionally initializes contracts using the `CREATE2` opcode. - * @dev This contract extends the {Deployer} contract from the Axelar SDK, by adding basic access control to the deployment functions. - * The contract has an owner, which is the only entity that can deploy new contracts. - * - * @dev The contract deploys a contract with the same bytecode, salt, and sender(owner) to the same address. - * Attempting to deploy a contract with the same bytecode, salt, and sender(owner) will revert. - * The address where the contract will be deployed can be found using {deployedAddress}. - */ -contract OwnableCreate2Deployer is Ownable, Create2, Deployer { - constructor(address owner) Ownable() { - transferOwnership(owner); - } - - /** - * @dev Deploys a contract using the `CREATE2` opcode. - * This function is called by {deploy} and {deployAndInit} external functions in the {Deployer} contract. - * This function can only be called by the owner of this contract, hence {deploy} and {deployAndInit} can only be called by the owner. - * The address where the contract will be deployed can be found using {deployedAddress}. - * @param bytecode The bytecode of the contract to be deployed - * @param deploySalt A salt which is a hash of the salt provided by the sender and the sender's address. - * @return The address of the deployed contract - */ - function _deploy(bytes memory bytecode, bytes32 deploySalt) internal override onlyOwner returns (address) { - return _create2(bytecode, deploySalt); - } - - /** - * @dev Returns the address where a contract will be stored if deployed via {deploy} or {deployAndInit}. - * This function is called by the {deployedAddress} external functions in the {Deployer} contract. - * @param bytecode The bytecode of the contract to be deployed - * @param deploySalt A salt which is a hash of the salt provided by the sender and the sender's address. - * @return The predicted deployment address of the contract - */ - function _deployedAddress(bytes memory bytecode, bytes32 deploySalt) internal view override returns (address) { - return _create2Address(bytecode, deploySalt); - } -} From f4c7278a670860c597972298add54ee10c857702 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Mon, 12 Feb 2024 08:47:40 +1000 Subject: [PATCH 34/40] Delete OwnableCreate2Deployer.t.sol --- test/unit/deploy/OwnableCreate2Deployer.t.sol | 177 ------------------ 1 file changed, 177 deletions(-) delete mode 100644 test/unit/deploy/OwnableCreate2Deployer.t.sol diff --git a/test/unit/deploy/OwnableCreate2Deployer.t.sol b/test/unit/deploy/OwnableCreate2Deployer.t.sol deleted file mode 100644 index 1b89ef8b..00000000 --- a/test/unit/deploy/OwnableCreate2Deployer.t.sol +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2023 -// SPDX-License-Identifier: Apache 2.0 -pragma solidity 0.8.19; - -import "forge-std/Test.sol"; -import {IDeploy} from "@axelar-gmp-sdk-solidity/contracts/interfaces/IDeploy.sol"; -import {IDeployer} from "@axelar-gmp-sdk-solidity/contracts/interfaces/IDeployer.sol"; - -import {ChildERC20} from "../../../src/child/ChildERC20.sol"; -import {OwnableCreate2Deployer} from "../../../src/deploy/OwnableCreate2Deployer.sol"; - -contract OwnableCreate2DeployerTest is Test { - OwnableCreate2Deployer private deployer; - ChildERC20 private childERC20; - - bytes private childERC20Bytecode; - bytes32 private salt; - address private owner; - - event Deployed(address indexed deployedAddress, address indexed sender, bytes32 indexed salt, bytes32 bytecodeHash); - - function setUp() public { - owner = address(0x12345); - - // create a new deployer that is owned by this contract - deployer = new OwnableCreate2Deployer(owner); - - childERC20 = new ChildERC20(); - childERC20Bytecode = type(ChildERC20).creationCode; - - salt = createSaltFromKey("test-salt"); - vm.startPrank(owner); - } - - function test_RevertIf_DeployWithEmptyByteCode() public { - vm.expectRevert(IDeploy.EmptyBytecode.selector); - deployer.deploy("", salt); - } - - function test_RevertIf_DeployWithNonOwner() public { - vm.stopPrank(); - - address nonOwner = address(0x1); - vm.startPrank(nonOwner); - vm.expectRevert("Ownable: caller is not the owner"); - deployer.deploy(childERC20Bytecode, salt); - } - - /// @dev deploying with the same bytecode, salt and sender should revert - function test_RevertIf_DeployAlreadyDeployedCreate2Contract() public { - deployer.deploy(childERC20Bytecode, salt); - - vm.expectRevert(IDeploy.AlreadyDeployed.selector); - deployer.deploy(childERC20Bytecode, salt); - } - - function test_deploy_DeploysContract() public { - address expectedAddress = predictCreate2Address(childERC20Bytecode, address(deployer), address(owner), salt); - - vm.expectEmit(); - emit Deployed(expectedAddress, address(owner), salt, keccak256(childERC20Bytecode)); - address deployed = deployer.deploy(childERC20Bytecode, salt); - - assertEq(deployed.code, address(childERC20).code, "deployed contract code does not match expected"); - - ChildERC20 deployedChildERC20 = ChildERC20(deployed); - assertEq(deployedChildERC20.name(), "", "deployed contract should have empty name"); - assertEq(deployedChildERC20.symbol(), "", "deployed contract should have empty symbol"); - assertEq(deployedChildERC20.decimals(), 0, "deployed contract should have 0 decimals"); - } - - function test_deploy_DeploysToPredictedAddress() public { - address deployedAddress = deployer.deploy(childERC20Bytecode, salt); - address expectedAddress = predictCreate2Address(childERC20Bytecode, address(deployer), address(owner), salt); - assertEq(deployedAddress, expectedAddress, "deployed address does not match expected address"); - } - - function test_deploy_DeploysSameContractToDifferentAddresses_GivenDifferentSalts() public { - address deployed1 = deployer.deploy(childERC20Bytecode, salt); - - bytes32 newSalt = createSaltFromKey("new-salt"); - address deployed2 = deployer.deploy(childERC20Bytecode, newSalt); - - assertEq(deployed1.code, deployed2.code, "bytecode of deployed contracts do not match"); - assertNotEq(deployed1, deployed2, "deployed contracts should not have the same address"); - } - - function test_deploy_DeploysContractGivenNewOwner() public { - address newOwner = address(0x1); - - deployer.transferOwnership(newOwner); - assertEq(deployer.owner(), newOwner, "owner did not change as expected"); - - // check that the old owner cannot deploy - vm.expectRevert("Ownable: caller is not the owner"); - deployer.deploy(childERC20Bytecode, salt); - - // test that the new owner can deploy - vm.startPrank(newOwner); - address expectedAddress = predictCreate2Address(childERC20Bytecode, address(deployer), address(newOwner), salt); - - vm.expectEmit(); - emit Deployed(expectedAddress, address(newOwner), salt, keccak256(childERC20Bytecode)); - address deployed = deployer.deploy(childERC20Bytecode, salt); - - assertEq(deployed.code, address(childERC20).code, "deployed contract should match expected"); - } - - /** - * deployAndInit - */ - function test_RevertIf_DeployAndInitWithNonOwner() public { - vm.stopPrank(); - - address nonOwner = address(0x1); - vm.startPrank(nonOwner); - vm.expectRevert("Ownable: caller is not the owner"); - deployer.deployAndInit(childERC20Bytecode, salt, ""); - } - - function test_deployAndInit_DeploysAndInitsContract() public { - address expectedAddress = predictCreate2Address(childERC20Bytecode, address(deployer), address(owner), salt); - address rootToken = address(0x1); - string memory name = "Test-Token"; - string memory symbol = "TST"; - uint8 decimals = 18; - bytes memory initPayload = - abi.encodeWithSelector(ChildERC20.initialize.selector, rootToken, name, symbol, decimals); - - vm.expectEmit(); - emit Deployed(expectedAddress, address(owner), salt, keccak256(childERC20Bytecode)); - address deployed = deployer.deployAndInit(childERC20Bytecode, salt, initPayload); - - // regardless of init data, the deployed address should match expected deployment - assertEq(deployed, expectedAddress, "deployed address should match expected address"); - - assertEq(deployed.code, address(childERC20).code, "deployed contract should match expected"); - - // verify initialisation - ChildERC20 deployedChildERC20 = ChildERC20(deployed); - assertEq(deployedChildERC20.rootToken(), rootToken, "rootToken does not match expected"); - assertEq(deployedChildERC20.name(), name, "name does not match expected"); - assertEq(deployedChildERC20.symbol(), symbol, "symbol does not match expected"); - assertEq(deployedChildERC20.decimals(), decimals, "decimals does not match expected"); - } - - /** - * deployedAddress - */ - function test_deployedAddress_ReturnsPredictedAddress() public { - address deployAddress = deployer.deployedAddress(childERC20Bytecode, address(owner), salt); - - address predictedAddress = predictCreate2Address(childERC20Bytecode, address(deployer), address(owner), salt); - address deployedAddress = deployer.deploy(childERC20Bytecode, salt); - - assertEq(deployAddress, predictedAddress, "deployment address did not match predicted address"); - assertEq(deployAddress, deployedAddress, "deployment address did not match deployed address"); - } - - /** - * private helper functions - */ - function predictCreate2Address(bytes memory _bytecode, address _deployer, address _sender, bytes32 _salt) - private - pure - returns (address) - { - bytes32 deploySalt = keccak256(abi.encode(_sender, _salt)); - return address( - uint160(uint256(keccak256(abi.encodePacked(hex"ff", address(_deployer), deploySalt, keccak256(_bytecode))))) - ); - } - - function createSaltFromKey(string memory key) private view returns (bytes32) { - return keccak256(abi.encode(address(owner), key)); - } -} From 1e83d42e1fdc594038f50e055265f7a0571ae93e Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Mon, 12 Feb 2024 12:00:06 +1000 Subject: [PATCH 35/40] Refactor --- scripts/e2e/e2e.ts | 611 ++++++++++++++++----------------------------- 1 file changed, 215 insertions(+), 396 deletions(-) diff --git a/scripts/e2e/e2e.ts b/scripts/e2e/e2e.ts index 712fbe1a..6badcd99 100644 --- a/scripts/e2e/e2e.ts +++ b/scripts/e2e/e2e.ts @@ -66,6 +66,34 @@ describe("Bridge e2e test", () => { childBridge = getContract("ChildERC20Bridge", childBridgeAddr, childProvider); childETH = getContract("ChildERC20", await childBridge.childETHToken(), childProvider); childWIMX = getContract("WIMX", childWIMXAddr, childProvider); + + // Transfer 0.5 ETH to root pauser + let resp = await rootTestWallet.sendTransaction({ + to: rootPauserWallet.address, + value: ethers.utils.parseEther("0.5"), + }) + await waitForReceipt(resp.hash, rootProvider); + + // Transfer 0.5 ETH to root unpauser + resp = await rootTestWallet.sendTransaction({ + to: rootPrivilegedWallet.address, + value: ethers.utils.parseEther("0.5"), + }) + await waitForReceipt(resp.hash, rootProvider); + + // Transfer 5 IMX to child pauser + resp = await childTestWallet.sendTransaction({ + to: childPauserWallet.address, + value: ethers.utils.parseEther("5"), + }) + await waitForReceipt(resp.hash, childProvider); + + // Transfer 5 IMX to child unpauser + resp = await childTestWallet.sendTransaction({ + to: childPrivilegedWallet.address, + value: ethers.utils.parseEther("5"), + }) + await waitForReceipt(resp.hash, childProvider); }) it("should not deposit IMX if allowance is insufficient", async() => { @@ -82,38 +110,20 @@ describe("Bridge e2e test", () => { })).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); }).timeout(2400000) - it("should not deposit IMX if balance is insufficient", async() => { + it.only("should not deposit IMX if balance is insufficient", async() => { let balance = await rootIMX.balanceOf(rootTestWallet.address); let amt = balance.add(1); let bridgeFee = ethers.utils.parseEther("0.001"); - // Approve - let resp = await rootIMX.connect(rootTestWallet).approve(rootBridge.address, amt); - await waitForReceipt(resp.hash, rootProvider); - - // Fail to deposit on L1 - await expect(rootBridge.connect(rootTestWallet).deposit(rootIMX.address, amt, { - value: bridgeFee, - })).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); + await expect( + depositIMX(rootTestWallet, amt, bridgeFee, null) + ).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); }).timeout(2400000) // Local only it("should not deposit IMX if root bridge is paused", async() => { - // Transfer 0.1 ETH to root pauser - let resp = await rootTestWallet.sendTransaction({ - to: rootPauserWallet.address, - value: ethers.utils.parseEther("0.1"), - }) - await waitForReceipt(resp.hash, rootProvider); - - // Transfer 0.1 ETH to root unpauser - resp = await rootTestWallet.sendTransaction({ - to: rootPrivilegedWallet.address, - value: ethers.utils.parseEther("0.1"), - }) - await waitForReceipt(resp.hash, rootProvider); - + let resp; // Pause root bridge if (!await rootBridge.paused()) { resp = await rootBridge.connect(rootPauserWallet).pause(); @@ -125,14 +135,9 @@ describe("Bridge e2e test", () => { let amt = ethers.utils.parseEther("10.0"); let bridgeFee = ethers.utils.parseEther("0.001"); - // Approve - resp = await rootIMX.connect(rootTestWallet).approve(rootBridge.address, amt); - await waitForReceipt(resp.hash, rootProvider); - - // Fail to deposit on L1 - await expect(rootBridge.connect(rootTestWallet).deposit(rootIMX.address, amt, { - value: bridgeFee, - })).to.be.rejectedWith("Pausable: paused"); + await expect( + depositIMX(rootTestWallet, amt, bridgeFee, null) + ).to.be.rejectedWith("Pausable: paused"); // Unpause root bridge resp = await rootBridge.connect(rootPrivilegedWallet).unpause(); @@ -147,14 +152,9 @@ describe("Bridge e2e test", () => { let amt = limit.add(1); let bridgeFee = ethers.utils.parseEther("0.001"); - // Approve - let resp = await rootIMX.connect(rootTestWallet).approve(rootBridge.address, amt); - await waitForReceipt(resp.hash, rootProvider); - - // Fail to deposit on L1 - await expect(rootBridge.connect(rootTestWallet).deposit(rootIMX.address, amt, { - value: bridgeFee, - })).to.be.rejectedWith(rootBridge.interface.getSighash('ImxDepositLimitExceeded()')); + await expect( + depositIMX(rootTestWallet, amt, bridgeFee, null) + ).to.be.rejectedWith("ImxDepositLimitExceeded()"); }).timeout(2400000) it("should successfully deposit IMX to self from L1 to L2", async() => { @@ -165,14 +165,7 @@ describe("Bridge e2e test", () => { let amt = ethers.utils.parseEther("50.0"); let bridgeFee = ethers.utils.parseEther("0.001"); - // Approve - let resp = await rootIMX.connect(rootTestWallet).approve(rootBridge.address, amt); - await waitForReceipt(resp.hash, rootProvider); - - // IMX deposit L1 to L2 - resp = await rootBridge.connect(rootTestWallet).deposit(rootIMX.address, amt, { - value: bridgeFee, - }); + let resp = await depositIMX(rootTestWallet, amt, bridgeFee, null); await waitForReceipt(resp.hash, rootProvider); let postBalL1 = await rootIMX.balanceOf(rootTestWallet.address); @@ -201,14 +194,7 @@ describe("Bridge e2e test", () => { let amt = ethers.utils.parseEther("50.0"); let bridgeFee = ethers.utils.parseEther("0.001"); - // Approve - let resp = await rootIMX.connect(rootTestWallet).approve(rootBridge.address, amt); - await waitForReceipt(resp.hash, rootProvider); - - // IMX deposit L1 to L2 - resp = await rootBridge.connect(rootTestWallet).depositTo(rootIMX.address, childRecipient, amt, { - value: bridgeFee, - }); + let resp = await depositIMX(rootTestWallet, amt, bridgeFee, childRecipient); await waitForReceipt(resp.hash, rootProvider); let postBalL1 = await rootIMX.balanceOf(rootTestWallet.address); @@ -257,15 +243,7 @@ describe("Bridge e2e test", () => { let amt = ethers.utils.parseEther("10.0"); let bridgeFee = ethers.utils.parseEther("0.001"); - - // Approve - resp = await rootIMX.connect(rootTestWallet).approve(rootBridge.address, amt); - await waitForReceipt(resp.hash, rootProvider); - - // Try to deposit - resp = await rootBridge.connect(rootTestWallet).deposit(rootIMX.address, amt, { - value: bridgeFee, - }); + resp = await depositIMX(rootTestWallet, amt, bridgeFee, null); await waitForReceipt(resp.hash, rootProvider); await waitUntilSucceed(axelarAPI, resp.hash); @@ -288,20 +266,7 @@ describe("Bridge e2e test", () => { // Local only it("should not withdraw IMX if child bridge is paused", async() => { - // Transfer 0.1 IMX to child pauser - let resp = await childTestWallet.sendTransaction({ - to: childPauserWallet.address, - value: ethers.utils.parseEther("0.1"), - }) - await waitForReceipt(resp.hash, childProvider); - - // Transfer 0.1 IMX to child unpauser - resp = await childTestWallet.sendTransaction({ - to: childPrivilegedWallet.address, - value: ethers.utils.parseEther("0.1"), - }) - await waitForReceipt(resp.hash, childProvider); - + let resp; // Pause child bridge if (!await childBridge.paused()) { resp = await childBridge.connect(childPauserWallet).pause(); @@ -333,12 +298,9 @@ describe("Bridge e2e test", () => { let bridgeFee = ethers.utils.parseEther("1.0"); // IMX withdraw L2 to L1 - let [priorityFee, maxFee] = await getFee(childProvider); - await expect(childBridge.connect(childTestWallet).withdrawIMX(amt, { - value: amt.add(bridgeFee), - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - })).to.be.rejectedWith("sender doesn't have enough funds to send tx"); + await expect( + withdrawIMX(childTestWallet, amt, bridgeFee, null) + ).to.be.rejectedWith("sender doesn't have enough funds to send tx"); }).timeout(2400000) it("should successfully withdraw IMX to self from L2 to L1", async() => { @@ -350,12 +312,7 @@ describe("Bridge e2e test", () => { let bridgeFee = ethers.utils.parseEther("1.0"); // IMX withdraw L2 to L1 - let [priorityFee, maxFee] = await getFee(childProvider); - let resp = await childBridge.connect(childTestWallet).withdrawIMX(amt, { - value: amt.add(bridgeFee), - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); + let resp = await withdrawIMX(childTestWallet, amt, bridgeFee, null); await waitForReceipt(resp.hash, childProvider); let postBalL1 = preBalL1; @@ -387,12 +344,7 @@ describe("Bridge e2e test", () => { let bridgeFee = ethers.utils.parseEther("1.0"); // IMX withdraw L2 to L1 - let [priorityFee, maxFee] = await getFee(childProvider); - let resp = await childBridge.connect(childTestWallet).withdrawIMXTo(rootRecipient, amt, { - value: amt.add(bridgeFee), - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); + let resp = await withdrawIMX(childTestWallet, amt, bridgeFee, rootRecipient); await waitForReceipt(resp.hash, childProvider); let postBalL1 = preBalL1; @@ -416,20 +368,7 @@ describe("Bridge e2e test", () => { // Local only it("should not withdraw IMX on L1 if root bridge is paused", async() => { - // Transfer 0.1 ETH to root pauser - let resp = await rootTestWallet.sendTransaction({ - to: rootPauserWallet.address, - value: ethers.utils.parseEther("0.1"), - }) - await waitForReceipt(resp.hash, rootProvider); - - // Transfer 0.1 ETH to root unpauser - resp = await rootTestWallet.sendTransaction({ - to: rootPrivilegedWallet.address, - value: ethers.utils.parseEther("0.1"), - }) - await waitForReceipt(resp.hash, rootProvider); - + let resp; // Pause root bridge if (!await rootBridge.paused()) { resp = await rootBridge.connect(rootPauserWallet).pause(); @@ -445,12 +384,7 @@ describe("Bridge e2e test", () => { let bridgeFee = ethers.utils.parseEther("1.0"); // IMX withdraw L2 to L1 - let [priorityFee, maxFee] = await getFee(childProvider); - resp = await childBridge.connect(childTestWallet).withdrawIMX(amt, { - value: amt.add(bridgeFee), - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); + resp = await withdrawIMX(childTestWallet, amt, bridgeFee, null); await waitForReceipt(resp.hash, childProvider); await waitUntilSucceed(axelarAPI, resp.hash); @@ -489,12 +423,7 @@ describe("Bridge e2e test", () => { let bridgeFee1 = ethers.utils.parseEther("1.0"); // IMX withdraw L2 to L1 - let [priorityFee, maxFee] = await getFee(childProvider); - resp = await childBridge.connect(childTestWallet).withdrawIMX(amt1, { - value: amt1.add(bridgeFee1), - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); + resp = await withdrawIMX(childTestWallet, amt1, bridgeFee1, null); await waitForReceipt(resp.hash, childProvider); await waitUntilSucceed(axelarAPI, resp.hash); @@ -510,12 +439,7 @@ describe("Bridge e2e test", () => { let bridgeFee2 = ethers.utils.parseEther("1.0"); // IMX withdraw L2 to L1 - [priorityFee, maxFee] = await getFee(childProvider); - resp = await childBridge.connect(childTestWallet).withdrawIMX(amt2, { - value: amt2.add(bridgeFee2), - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); + resp = await withdrawIMX(childTestWallet, amt2, bridgeFee2, null); await waitForReceipt(resp.hash, childProvider); await waitUntilSucceed(axelarAPI, resp.hash); receipt = await childProvider.getTransactionReceipt(resp.hash); @@ -563,20 +487,7 @@ describe("Bridge e2e test", () => { // Local only it("should not withdraw WIMX if child bridge is paused", async() => { - // Transfer 0.1 IMX to child pauser - let resp = await childTestWallet.sendTransaction({ - to: childPauserWallet.address, - value: ethers.utils.parseEther("0.1"), - }) - await waitForReceipt(resp.hash, childProvider); - - // Transfer 0.1 IMX to child unpauser - resp = await childTestWallet.sendTransaction({ - to: childPrivilegedWallet.address, - value: ethers.utils.parseEther("0.1"), - }) - await waitForReceipt(resp.hash, childProvider); - + let resp; // Pause child bridge if (!await childBridge.paused()) { resp = await childBridge.connect(childPauserWallet).pause(); @@ -584,31 +495,12 @@ describe("Bridge e2e test", () => { expect(await childBridge.paused()).to.true; } - // Wrap 1 IMX - let [priorityFee, maxFee] = await getFee(childProvider); - resp = await childWIMX.connect(childTestWallet).deposit({ - value: ethers.utils.parseEther("1.0"), - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); - await waitForReceipt(resp.hash, childProvider); - let amt = ethers.utils.parseEther("0.5"); let bridgeFee = ethers.utils.parseEther("1.0"); - // Approve - [priorityFee, maxFee] = await getFee(childProvider); - resp = await childWIMX.connect(childTestWallet).approve(childBridge.address, amt, { - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); - await waitForReceipt(resp.hash, childProvider); - - await expect(childBridge.connect(childTestWallet).withdrawWIMX(amt, { - value: amt.add(bridgeFee), - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - })).to.be.rejectedWith("Pausable: paused"); + await expect( + withdrawWIMX(childTestWallet, amt, bridgeFee, null) + ).to.be.rejectedWith("Pausable: paused"); // Unpause child bridge resp = await childBridge.connect(childPrivilegedWallet).unpause(); @@ -649,50 +541,23 @@ describe("Bridge e2e test", () => { it("should not withdraw wIMX if balance is insufficient", async() => { let balance = await childWIMX.balanceOf(childTestWallet.address); - let amt = balance; + let amt = balance.add(1); let bridgeFee = ethers.utils.parseEther("1.0"); - // wIMX withdraw L2 to L1 - let [priorityFee, maxFee] = await getFee(childProvider); - await expect(childBridge.connect(childTestWallet).withdrawWIMX(amt.add(1), { - value: bridgeFee, - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - })).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); + await expect( + withdrawWIMX(childTestWallet, amt, bridgeFee, null) + ).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); }).timeout(2400000) it("should successfully withdraw wIMX to self from L2 to L1", async() => { - // Wrap 1 IMX - let [priorityFee, maxFee] = await getFee(childProvider); - let resp = await childWIMX.connect(childTestWallet).deposit({ - value: ethers.utils.parseEther("1.0"), - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); - await waitForReceipt(resp.hash, childProvider); + let amt = ethers.utils.parseEther("0.5"); + let bridgeFee = ethers.utils.parseEther("1.0"); // Get IMX balance on root & child chains before withdraw let preBalL1 = await rootIMX.balanceOf(rootTestWallet.address); let preBalL2 = await childWIMX.balanceOf(childTestWallet.address); - let amt = ethers.utils.parseEther("0.5"); - let bridgeFee = ethers.utils.parseEther("1.0"); - - // Approve - [priorityFee, maxFee] = await getFee(childProvider); - resp = await childWIMX.connect(childTestWallet).approve(childBridge.address, amt, { - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); - await waitForReceipt(resp.hash, childProvider); - - // wIMX withdraw L2 to L1 - [priorityFee, maxFee] = await getFee(childProvider); - resp = await childBridge.connect(childTestWallet).withdrawWIMX(amt, { - value: bridgeFee, - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); + let resp = await withdrawWIMX(rootTestWallet, amt, bridgeFee, null); await waitForReceipt(resp.hash, childProvider); let postBalL1 = preBalL1; @@ -714,37 +579,13 @@ describe("Bridge e2e test", () => { it("should successfully withdraw wIMX to others from L2 to L1", async() => { let rootRecipient = rootPrivilegedWallet.address; - // Wrap 1 IMX - let [priorityFee, maxFee] = await getFee(childProvider); - let resp = await childWIMX.connect(childTestWallet).deposit({ - value: ethers.utils.parseEther("1.0"), - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); - await waitForReceipt(resp.hash, childProvider); - + let amt = ethers.utils.parseEther("0.5"); + let bridgeFee = ethers.utils.parseEther("1.0"); // Get IMX balance on root & child chains before withdraw let preBalL1 = await rootIMX.balanceOf(rootRecipient); let preBalL2 = await childWIMX.balanceOf(childTestWallet.address); - let amt = ethers.utils.parseEther("0.5"); - let bridgeFee = ethers.utils.parseEther("1.0"); - - // Approve - [priorityFee, maxFee] = await getFee(childProvider); - resp = await childWIMX.connect(childTestWallet).approve(childBridge.address, amt, { - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); - await waitForReceipt(resp.hash, childProvider); - - // wIMX withdraw L2 to L1 - [priorityFee, maxFee] = await getFee(childProvider); - resp = await childBridge.connect(childTestWallet).withdrawWIMXTo(rootRecipient, amt, { - value: bridgeFee, - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); + let resp = await withdrawWIMX(rootTestWallet, amt, bridgeFee, rootRecipient); await waitForReceipt(resp.hash, childProvider); let postBalL1 = preBalL1; @@ -766,20 +607,7 @@ describe("Bridge e2e test", () => { // Local only it("should not withdraw wIMX on L1 if root bridge is paused", async() => { - // Transfer 0.1 ETH to root pauser - let resp = await rootTestWallet.sendTransaction({ - to: rootPauserWallet.address, - value: ethers.utils.parseEther("0.1"), - }) - await waitForReceipt(resp.hash, rootProvider); - - // Transfer 0.1 ETH to root unpauser - resp = await rootTestWallet.sendTransaction({ - to: rootPrivilegedWallet.address, - value: ethers.utils.parseEther("0.1"), - }) - await waitForReceipt(resp.hash, rootProvider); - + let resp; // Pause root bridge if (!await rootBridge.paused()) { resp = await rootBridge.connect(rootPauserWallet).pause(); @@ -787,37 +615,14 @@ describe("Bridge e2e test", () => { expect(await rootBridge.paused()).to.true; } - // Wrap 1 IMX - let [priorityFee, maxFee] = await getFee(childProvider); - resp = await childWIMX.connect(childTestWallet).deposit({ - value: ethers.utils.parseEther("1.0"), - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); - await waitForReceipt(resp.hash, childProvider); + let amt = ethers.utils.parseEther("0.5"); + let bridgeFee = ethers.utils.parseEther("1.0"); // Get IMX balance on root & child chains before withdraw let preBalL1 = await rootIMX.balanceOf(rootTestWallet.address); let preBalL2 = await childWIMX.balanceOf(childTestWallet.address); - let amt = ethers.utils.parseEther("0.5"); - let bridgeFee = ethers.utils.parseEther("1.0"); - - // Approve - [priorityFee, maxFee] = await getFee(childProvider); - resp = await childWIMX.connect(childTestWallet).approve(childBridge.address, amt, { - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); - await waitForReceipt(resp.hash, childProvider); - - // wIMX withdraw L2 to L1 - [priorityFee, maxFee] = await getFee(childProvider); - resp = await childBridge.connect(childTestWallet).withdrawWIMX(amt, { - value: bridgeFee, - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); + resp = await withdrawWIMX(rootTestWallet, amt, bridgeFee, null); await waitForReceipt(resp.hash, childProvider); await waitUntilSucceed(axelarAPI, resp.hash); @@ -959,20 +764,7 @@ describe("Bridge e2e test", () => { // Local only it("should not deposit ETH if root bridge is paused", async() => { - // Transfer 0.1 ETH to root pauser - let resp = await rootTestWallet.sendTransaction({ - to: rootPauserWallet.address, - value: ethers.utils.parseEther("0.1"), - }) - await waitForReceipt(resp.hash, rootProvider); - - // Transfer 0.1 ETH to root unpauser - resp = await rootTestWallet.sendTransaction({ - to: rootPrivilegedWallet.address, - value: ethers.utils.parseEther("0.1"), - }) - await waitForReceipt(resp.hash, rootProvider); - + let resp; // Pause root bridge if (!await rootBridge.paused()) { resp = await rootBridge.connect(rootPauserWallet).pause(); @@ -984,9 +776,9 @@ describe("Bridge e2e test", () => { let bridgeFee = ethers.utils.parseEther("0.001"); // Fail to deposit on L1 - await expect(rootBridge.connect(rootTestWallet).depositETH(amt, { - value: amt.add(bridgeFee), - })).to.be.rejectedWith("Pausable: paused"); + await expect( + depositETH(rootTestWallet, amt, bridgeFee, null) + ).to.be.rejectedWith("Pausable: paused"); // Unpause root bridge resp = await rootBridge.connect(rootPrivilegedWallet).unpause(); @@ -1003,9 +795,7 @@ describe("Bridge e2e test", () => { let bridgeFee = ethers.utils.parseEther("0.001"); // ETH deposit L1 to L2 - let resp = await rootBridge.connect(rootTestWallet).depositETH(amt, { - value: amt.add(bridgeFee), - }); + let resp = await depositETH(rootTestWallet, amt, bridgeFee, null); await waitForReceipt(resp.hash, rootProvider); let postBalL1 = await rootProvider.getBalance(rootTestWallet.address); @@ -1037,9 +827,7 @@ describe("Bridge e2e test", () => { let bridgeFee = ethers.utils.parseEther("0.001"); // ETH deposit L1 to L2 - let resp = await rootBridge.connect(rootTestWallet).depositToETH(childRecipient, amt, { - value: amt.add(bridgeFee), - }); + let resp = await depositETH(rootTestWallet, amt, bridgeFee, childRecipient); await waitForReceipt(resp.hash, rootProvider); let postBalL1 = await rootProvider.getBalance(rootTestWallet.address); @@ -1063,20 +851,7 @@ describe("Bridge e2e test", () => { // Local only it("should not deposit ETH on L2 if child bridge is paused", async() => { - // Transfer 0.1 IMX to child pauser - let resp = await childTestWallet.sendTransaction({ - to: childPauserWallet.address, - value: ethers.utils.parseEther("0.1"), - }) - await waitForReceipt(resp.hash, childProvider); - - // Transfer 0.1 IMX to child unpauser - resp = await childTestWallet.sendTransaction({ - to: childPrivilegedWallet.address, - value: ethers.utils.parseEther("0.1"), - }) - await waitForReceipt(resp.hash, childProvider); - + let resp; // Pause child bridge if (!await childBridge.paused()) { resp = await childBridge.connect(childPauserWallet).pause(); @@ -1092,9 +867,7 @@ describe("Bridge e2e test", () => { let bridgeFee = ethers.utils.parseEther("0.001"); // Try to deposit - resp = await rootBridge.connect(rootTestWallet).depositETH(amt, { - value: amt.add(bridgeFee), - }); + resp = await depositETH(rootTestWallet, amt, bridgeFee, null); await waitForReceipt(resp.hash, rootProvider); await waitUntilSucceed(axelarAPI, resp.hash); @@ -1118,27 +891,14 @@ describe("Bridge e2e test", () => { }).timeout(2400000) it("should successfully deposit wETH to self from L1 to L2", async() => { - // Wrap 0.01 ETH - let resp = await rootWETH.connect(rootTestWallet).deposit({ - value: ethers.utils.parseEther("0.01"), - }) - await waitForReceipt(resp.hash, rootProvider); + let amt = ethers.utils.parseEther("0.001"); + let bridgeFee = ethers.utils.parseEther("0.001"); // Get ETH balance on root & child chains before withdraw let preBalL1 = await rootWETH.balanceOf(rootTestWallet.address); let preBalL2 = await childETH.balanceOf(childTestWallet.address); - let amt = ethers.utils.parseEther("0.001"); - let bridgeFee = ethers.utils.parseEther("0.001"); - - // Approve - resp = await rootWETH.connect(rootTestWallet).approve(rootBridge.address, amt); - await waitForReceipt(resp.hash, rootProvider); - - // wETH deposit L1 to L2 - resp = await rootBridge.connect(rootTestWallet).deposit(rootWETH.address, amt, { - value: bridgeFee, - }) + let resp = await depositWETH(rootTestWallet, amt, bridgeFee, null); await waitForReceipt(resp.hash, rootProvider); let postBalL1 = await rootWETH.balanceOf(rootTestWallet.address); @@ -1160,27 +920,14 @@ describe("Bridge e2e test", () => { it("should successfully deposit wETH to others from L1 to L2", async() => { let childRecipient = childPrivilegedWallet.address; - // Wrap 0.01 ETH - let resp = await rootWETH.connect(rootTestWallet).deposit({ - value: ethers.utils.parseEther("0.01"), - }) - await waitForReceipt(resp.hash, rootProvider); + let amt = ethers.utils.parseEther("0.001"); + let bridgeFee = ethers.utils.parseEther("0.001"); // Get ETH balance on root & child chains before withdraw let preBalL1 = await rootWETH.balanceOf(rootTestWallet.address); let preBalL2 = await childETH.balanceOf(childRecipient); - let amt = ethers.utils.parseEther("0.001"); - let bridgeFee = ethers.utils.parseEther("0.001"); - - // Approve - resp = await rootWETH.connect(rootTestWallet).approve(rootBridge.address, amt); - await waitForReceipt(resp.hash, rootProvider); - - // wETH deposit L1 to L2 - resp = await rootBridge.connect(rootTestWallet).depositTo(rootWETH.address, childRecipient, amt, { - value: bridgeFee, - }) + let resp = await depositWETH(rootTestWallet, amt, bridgeFee, childRecipient); await waitForReceipt(resp.hash, rootProvider); let postBalL1 = await rootWETH.balanceOf(rootTestWallet.address); @@ -1202,20 +949,7 @@ describe("Bridge e2e test", () => { // Local only it("should not withdraw ETH if child bridge is paused", async() => { - // Transfer 0.1 IMX to child pauser - let resp = await childTestWallet.sendTransaction({ - to: childPauserWallet.address, - value: ethers.utils.parseEther("0.1"), - }) - await waitForReceipt(resp.hash, childProvider); - - // Transfer 0.1 IMX to child unpauser - resp = await childTestWallet.sendTransaction({ - to: childPrivilegedWallet.address, - value: ethers.utils.parseEther("0.1"), - }) - await waitForReceipt(resp.hash, childProvider); - + let resp; // Pause child bridge if (!await childBridge.paused()) { resp = await childBridge.connect(childPauserWallet).pause(); @@ -1227,12 +961,9 @@ describe("Bridge e2e test", () => { let bridgeFee = ethers.utils.parseEther("1.0"); // ETH withdraw L2 to L1 - let [priorityFee, maxFee] = await getFee(childProvider); - await expect(childBridge.connect(childTestWallet).withdrawETH(amt, { - value: bridgeFee, - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - })).to.be.rejectedWith("Pausable: paused"); + await expect( + withdrawETH(childTestWallet, amt, bridgeFee, null) + ).to.be.rejectedWith("Pausable: paused"); // Unpause child bridge resp = await childBridge.connect(childPrivilegedWallet).unpause(); @@ -1241,16 +972,13 @@ describe("Bridge e2e test", () => { }).timeout(2400000) it("should not withdraw ETH if balance is insufficient", async() => { - let amt = await childETH.balanceOf(childTestWallet.address); + let amt = await childETH.balanceOf(childTestWallet.address).add(1); let bridgeFee = ethers.utils.parseEther("1.0"); // ETH withdraw L2 to L1 - let [priorityFee, maxFee] = await getFee(childProvider); - await expect(childBridge.connect(childTestWallet).withdrawETH(amt.add(1), { - value: bridgeFee, - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - })).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); + await expect( + withdrawETH(childTestWallet, amt, bridgeFee, null) + ).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); }).timeout(2400000) it("should successfully withdraw ETH to self from L2 to L1", async() => { @@ -1262,12 +990,7 @@ describe("Bridge e2e test", () => { let bridgeFee = ethers.utils.parseEther("1.0"); // ETH withdraw L2 to L1 - let [priorityFee, maxFee] = await getFee(childProvider); - let resp = await childBridge.connect(childTestWallet).withdrawETH(amt, { - value: bridgeFee, - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); + let resp = await withdrawETH(childTestWallet, amt, bridgeFee, null); await waitForReceipt(resp.hash, childProvider); let postBalL1 = preBalL1; @@ -1297,12 +1020,7 @@ describe("Bridge e2e test", () => { let bridgeFee = ethers.utils.parseEther("1.0"); // ETH withdraw L2 to L1 - let [priorityFee, maxFee] = await getFee(childProvider); - let resp = await childBridge.connect(childTestWallet).withdrawETHTo(rootRecipient, amt, { - value: bridgeFee, - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); + let resp = await withdrawETH(childTestWallet, amt, bridgeFee, rootRecipient); await waitForReceipt(resp.hash, childProvider); let postBalL1 = preBalL1; @@ -1324,20 +1042,7 @@ describe("Bridge e2e test", () => { // Local only it("should not withdraw ETH on L1 if root bridge is paused", async() => { - // Transfer 0.1 ETH to root pauser - let resp = await rootTestWallet.sendTransaction({ - to: rootPauserWallet.address, - value: ethers.utils.parseEther("0.1"), - }) - await waitForReceipt(resp.hash, rootProvider); - - // Transfer 0.1 ETH to root unpauser - resp = await rootTestWallet.sendTransaction({ - to: rootPrivilegedWallet.address, - value: ethers.utils.parseEther("0.1"), - }) - await waitForReceipt(resp.hash, rootProvider); - + let resp; // Pause root bridge if (!await rootBridge.paused()) { resp = await rootBridge.connect(rootPauserWallet).pause(); @@ -1353,12 +1058,7 @@ describe("Bridge e2e test", () => { let bridgeFee = ethers.utils.parseEther("1.0"); // ETH withdraw L2 to L1 - let [priorityFee, maxFee] = await getFee(childProvider); - resp = await childBridge.connect(childTestWallet).withdrawETH(amt, { - value: bridgeFee, - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); + resp = await withdrawETH(childTestWallet, amt, bridgeFee, null); await waitForReceipt(resp.hash, childProvider); await waitUntilSucceed(axelarAPI, resp.hash); @@ -2027,4 +1727,123 @@ describe("Bridge e2e test", () => { // Test balance. }).timeout(2400000) + + async function depositIMX(sender: ethers.Wallet, amt: ethers.BigNumber, bridgeFee: ethers.BigNumber, recipient: string | null) { + // Approve + let resp = await rootIMX.connect(rootTestWallet).approve(rootBridge.address, amt); + await waitForReceipt(resp.hash, rootProvider); + + if (recipient == null) { + return rootBridge.connect(sender).deposit(rootIMX.address, amt, { + value: bridgeFee, + }); + } else { + return rootBridge.connect(sender).depositTo(rootIMX.address, recipient, amt, { + value: bridgeFee, + }); + } + } + + async function withdrawIMX(sender: ethers.Wallet, amt: ethers.BigNumber, bridgeFee: ethers.BigNumber, recipient: string | null) { + let [priorityFee, maxFee] = await getFee(childProvider); + + if (recipient == null) { + return childBridge.connect(sender).withdrawIMX(amt, { + value: amt.add(bridgeFee), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + } else { + return childBridge.connect(sender).withdrawIMXTo(recipient, amt, { + value: amt.add(bridgeFee), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + } + } + + async function withdrawWIMX(sender: ethers.Wallet, amt: ethers.BigNumber, bridgeFee: ethers.BigNumber, recipient: string | null) { + let [priorityFee, maxFee] = await getFee(childProvider); + + // Wrap IMX + let resp = await childWIMX.connect(sender).deposit({ + value: amt, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + // Approve + resp = await childWIMX.connect(sender).approve(childBridge.address, amt, { + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + if (recipient == null) { + return childBridge.connect(sender).withdrawWIMX(amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + } else { + return childBridge.connect(sender).withdrawWIMXTo(recipient, amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + } + } + + async function depositETH(sender: ethers.Wallet, amt: ethers.BigNumber, bridgeFee: ethers.BigNumber, recipient: string | null) { + if (recipient == null) { + return rootBridge.connect(sender).depositETH(amt, { + value: amt.add(bridgeFee), + }); + } else { + return rootBridge.connect(sender).depositToETH(recipient, amt, { + value: amt.add(bridgeFee), + }); + } + } + + async function depositWETH(sender: ethers.Wallet, amt: ethers.BigNumber, bridgeFee: ethers.BigNumber, recipient: string | null) { + // Wrap ETH + let resp = await rootWETH.connect(sender).deposit({ + value: amt, + }) + await waitForReceipt(resp.hash, rootProvider); + + // Approve + resp = await rootBridge.connect(sender).approve(rootBridge.address, amt); + await waitForReceipt(resp.hash, rootProvider); + + if (recipient == null) { + return rootBridge.connect(sender).deposit(rootWETH.address, amt, { + value: bridgeFee, + }); + } else { + return rootBridge.connect(sender).depositTo(rootWETH.address, recipient, amt, { + value: bridgeFee, + }); + } + } + + async function withdrawETH(sender: ethers.Wallet, amt: ethers.BigNumber, bridgeFee: ethers.BigNumber, recipient: string | null) { + let [priorityFee, maxFee] = await getFee(childProvider); + + if (recipient == null) { + return childBridge.connect(sender).withdrawETH(amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + } else { + return childBridge.connect(sender).withdrawETHTo(recipient, amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }) + } + } }) \ No newline at end of file From f102a3e4492c0f9a6642d1f0d1913952ab092c35 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Mon, 12 Feb 2024 12:00:17 +1000 Subject: [PATCH 36/40] Update e2e.ts --- scripts/e2e/e2e.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/e2e/e2e.ts b/scripts/e2e/e2e.ts index 6badcd99..a99e519b 100644 --- a/scripts/e2e/e2e.ts +++ b/scripts/e2e/e2e.ts @@ -110,7 +110,7 @@ describe("Bridge e2e test", () => { })).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); }).timeout(2400000) - it.only("should not deposit IMX if balance is insufficient", async() => { + it("should not deposit IMX if balance is insufficient", async() => { let balance = await rootIMX.balanceOf(rootTestWallet.address); let amt = balance.add(1); From b3bd1132c762d94d2c7a2da3da6d1bf3f431142c Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Mon, 12 Feb 2024 13:43:23 +1000 Subject: [PATCH 37/40] Update e2e.ts --- scripts/e2e/e2e.ts | 136 +++++++++++++++++---------------------------- 1 file changed, 52 insertions(+), 84 deletions(-) diff --git a/scripts/e2e/e2e.ts b/scripts/e2e/e2e.ts index a99e519b..258e7e5c 100644 --- a/scripts/e2e/e2e.ts +++ b/scripts/e2e/e2e.ts @@ -1246,31 +1246,14 @@ describe("Bridge e2e test", () => { let amt = balance.add(1); let bridgeFee = ethers.utils.parseEther("0.001"); - // Approve - let resp = await rootCustomToken.connect(rootTestWallet).approve(rootBridge.address, amt); - await waitForReceipt(resp.hash, rootProvider); - - await expect(rootBridge.connect(rootTestWallet).deposit(rootCustomToken.address, amt, { - value: bridgeFee, - })).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); + await expect( + depositERC20(rootTestWallet, amt, bridgeFee, null) + ).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); }).timeout(2400000) // Local only it("should not deposit mapped ERC20 Token if root bridge is paused", async() => { - // Transfer 0.1 ETH to root pauser - let resp = await rootTestWallet.sendTransaction({ - to: rootPauserWallet.address, - value: ethers.utils.parseEther("0.1"), - }) - await waitForReceipt(resp.hash, rootProvider); - - // Transfer 0.1 ETH to root unpauser - resp = await rootTestWallet.sendTransaction({ - to: rootPrivilegedWallet.address, - value: ethers.utils.parseEther("0.1"), - }) - await waitForReceipt(resp.hash, rootProvider); - + let resp; // Pause root bridge if (!await rootBridge.paused()) { resp = await rootBridge.connect(rootPauserWallet).pause(); @@ -1282,14 +1265,10 @@ describe("Bridge e2e test", () => { let amt = ethers.utils.parseEther("1.0"); let bridgeFee = ethers.utils.parseEther("0.001"); - // Approve - resp = await rootCustomToken.connect(rootTestWallet).approve(rootBridge.address, amt); - await waitForReceipt(resp.hash, rootProvider); - // Fail to deposit on L1 - await expect(rootBridge.connect(rootTestWallet).deposit(rootCustomToken.address, amt, { - value: bridgeFee, - })).to.be.rejectedWith("Pausable: paused"); + await expect( + depositERC20(rootTestWallet, amt, bridgeFee, null) + ).to.be.rejectedWith("Pausable: paused"); // Unpause root bridge resp = await rootBridge.connect(rootPrivilegedWallet).unpause(); @@ -1305,14 +1284,7 @@ describe("Bridge e2e test", () => { let amt = ethers.utils.parseEther("10.0"); let bridgeFee = ethers.utils.parseEther("0.001"); - // Approve - let resp = await rootCustomToken.connect(rootTestWallet).approve(rootBridge.address, amt); - await waitForReceipt(resp.hash, rootProvider); - - // Token deposit L1 to L2 - resp = await rootBridge.connect(rootTestWallet).deposit(rootCustomToken.address, amt, { - value: bridgeFee, - }) + let resp = await depositERC20(rootTestWallet, amt, bridgeFee, null); await waitForReceipt(resp.hash, rootProvider); let postBalL1 = await rootCustomToken.balanceOf(rootTestWallet.address); @@ -1341,14 +1313,7 @@ describe("Bridge e2e test", () => { let amt = ethers.utils.parseEther("1.0"); let bridgeFee = ethers.utils.parseEther("0.001"); - // Approve - let resp = await rootCustomToken.connect(rootTestWallet).approve(rootBridge.address, amt); - await waitForReceipt(resp.hash, rootProvider); - - // Token deposit L1 to L2 - resp = await rootBridge.connect(rootTestWallet).depositTo(rootCustomToken.address, childRecipient, amt, { - value: bridgeFee, - }) + let resp = await depositERC20(rootTestWallet, amt, bridgeFee, childRecipient); await waitForReceipt(resp.hash, rootProvider); let postBalL1 = await rootCustomToken.balanceOf(rootTestWallet.address); @@ -1384,20 +1349,7 @@ describe("Bridge e2e test", () => { // Local only it("should not withdraw mapped ERC20 Token if child bridge is paused", async() => { - // Transfer 0.1 IMX to child pauser - let resp = await childTestWallet.sendTransaction({ - to: childPauserWallet.address, - value: ethers.utils.parseEther("0.1"), - }) - await waitForReceipt(resp.hash, childProvider); - - // Transfer 0.1 IMX to child unpauser - resp = await childTestWallet.sendTransaction({ - to: childPrivilegedWallet.address, - value: ethers.utils.parseEther("0.1"), - }) - await waitForReceipt(resp.hash, childProvider); - + let resp; // Pause child bridge if (!await childBridge.paused()) { resp = await childBridge.connect(childPauserWallet).pause(); @@ -1409,12 +1361,9 @@ describe("Bridge e2e test", () => { let bridgeFee = ethers.utils.parseEther("1.0"); // Token withdraw L2 to L1 - let [priorityFee, maxFee] = await getFee(childProvider); - await expect(childBridge.connect(childTestWallet).withdraw(childCustomToken.address, amt, { - value: bridgeFee, - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - })).to.be.rejectedWith("Pausable: paused"); + await expect( + withdrawERC20(childTestWallet, amt, bridgeFee, null) + ).to.be.rejectedWith("Pausable: paused"); // Unpause child bridge resp = await childBridge.connect(childPrivilegedWallet).unpause(); @@ -1427,12 +1376,9 @@ describe("Bridge e2e test", () => { let bridgeFee = ethers.utils.parseEther("1.0"); // ETH withdraw L2 to L1 - let [priorityFee, maxFee] = await getFee(childProvider); - await expect(childBridge.connect(childTestWallet).withdraw(childCustomToken.address, amt.add(1), { - value: bridgeFee, - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - })).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); + await expect( + withdrawERC20(childTestWallet, amt, bridgeFee, null) + ).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); }).timeout(2400000) it("should successfully withdraw mapped ERC20 Token to self from L2 to L1", async() => { @@ -1443,13 +1389,7 @@ describe("Bridge e2e test", () => { let amt = ethers.utils.parseEther("0.5"); let bridgeFee = ethers.utils.parseEther("1.0"); - // Token withdraw L2 to L1 - let [priorityFee, maxFee] = await getFee(childProvider); - let resp = await childBridge.connect(childTestWallet).withdraw(childCustomToken.address, amt, { - value: bridgeFee, - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }) + let resp = await withdrawERC20(childTestWallet, amt, bridgeFee, null); await waitForReceipt(resp.hash, childProvider); let postBalL1 = preBalL1; @@ -1478,13 +1418,7 @@ describe("Bridge e2e test", () => { let amt = ethers.utils.parseEther("0.5"); let bridgeFee = ethers.utils.parseEther("1.0"); - // Token withdraw L2 to L1 - let [priorityFee, maxFee] = await getFee(childProvider); - let resp = await childBridge.connect(childTestWallet).withdrawTo(childCustomToken.address, rootRecipient, amt, { - value: bridgeFee, - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }) + let resp = await withdrawERC20(childTestWallet, amt, bridgeFee, rootRecipient); await waitForReceipt(resp.hash, childProvider); let postBalL1 = preBalL1; @@ -1846,4 +1780,38 @@ describe("Bridge e2e test", () => { }) } } + + async function depositERC20(sender: ethers.Wallet, amt: ethers.BigNumber, bridgeFee: ethers.BigNumber, recipient: string | null) { + // Approve + let resp = await rootCustomToken.connect(rootTestWallet).approve(rootBridge.address, amt); + await waitForReceipt(resp.hash, rootProvider); + + if (recipient == null) { + return rootBridge.connect(sender).deposit(rootTestWallet.address, amt, { + value: bridgeFee, + }); + } else { + return rootBridge.connect(sender).depositTo(rootTestWallet.address, recipient, amt, { + value: bridgeFee, + }); + } + } + + async function withdrawERC20(sender: ethers.Wallet, amt: ethers.BigNumber, bridgeFee: ethers.BigNumber, recipient: string | null) { + let [priorityFee, maxFee] = await getFee(childProvider); + + if (recipient == null) { + return childBridge.connect(sender).withdraw(childCustomToken.address, amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + } else { + return childBridge.connect(sender).withdrawTo(childCustomToken.address, recipient, amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + } + } }) \ No newline at end of file From ece226001f866cd63ce3e04413a1e0d019f57ad9 Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Mon, 12 Feb 2024 13:55:13 +1000 Subject: [PATCH 38/40] Update e2e.ts --- scripts/e2e/e2e.ts | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/scripts/e2e/e2e.ts b/scripts/e2e/e2e.ts index 258e7e5c..ba87e0ef 100644 --- a/scripts/e2e/e2e.ts +++ b/scripts/e2e/e2e.ts @@ -80,20 +80,6 @@ describe("Bridge e2e test", () => { value: ethers.utils.parseEther("0.5"), }) await waitForReceipt(resp.hash, rootProvider); - - // Transfer 5 IMX to child pauser - resp = await childTestWallet.sendTransaction({ - to: childPauserWallet.address, - value: ethers.utils.parseEther("5"), - }) - await waitForReceipt(resp.hash, childProvider); - - // Transfer 5 IMX to child unpauser - resp = await childTestWallet.sendTransaction({ - to: childPrivilegedWallet.address, - value: ethers.utils.parseEther("5"), - }) - await waitForReceipt(resp.hash, childProvider); }) it("should not deposit IMX if allowance is insufficient", async() => { @@ -154,7 +140,7 @@ describe("Bridge e2e test", () => { await expect( depositIMX(rootTestWallet, amt, bridgeFee, null) - ).to.be.rejectedWith("ImxDepositLimitExceeded()"); + ).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); }).timeout(2400000) it("should successfully deposit IMX to self from L1 to L2", async() => { @@ -216,17 +202,17 @@ describe("Bridge e2e test", () => { // Local only it("should not deposit IMX on L2 if child bridge is paused", async() => { - // Transfer 0.1 IMX to child pauser + // Transfer 5 IMX to child pauser let resp = await childTestWallet.sendTransaction({ to: childPauserWallet.address, - value: ethers.utils.parseEther("0.1"), + value: ethers.utils.parseEther("5"), }) await waitForReceipt(resp.hash, childProvider); - // Transfer 0.1 IMX to child unpauser + // Transfer 5 IMX to child unpauser resp = await childTestWallet.sendTransaction({ to: childPrivilegedWallet.address, - value: ethers.utils.parseEther("0.1"), + value: ethers.utils.parseEther("5"), }) await waitForReceipt(resp.hash, childProvider); @@ -541,12 +527,16 @@ describe("Bridge e2e test", () => { it("should not withdraw wIMX if balance is insufficient", async() => { let balance = await childWIMX.balanceOf(childTestWallet.address); - let amt = balance.add(1); + let amt = balance; let bridgeFee = ethers.utils.parseEther("1.0"); - await expect( - withdrawWIMX(childTestWallet, amt, bridgeFee, null) - ).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); + // wIMX withdraw L2 to L1 + let [priorityFee, maxFee] = await getFee(childProvider); + await expect(childBridge.connect(childTestWallet).withdrawWIMX(amt.add(1), { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + })).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); }).timeout(2400000) it("should successfully withdraw wIMX to self from L2 to L1", async() => { From e7f5e3765713c84d6b94baa7a4468fc0378cabec Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Mon, 12 Feb 2024 14:04:45 +1000 Subject: [PATCH 39/40] Fix e2e --- scripts/e2e/e2e.ts | 206 +++++++++++++++++++++++++++++---------------- 1 file changed, 133 insertions(+), 73 deletions(-) diff --git a/scripts/e2e/e2e.ts b/scripts/e2e/e2e.ts index ba87e0ef..cc5db60d 100644 --- a/scripts/e2e/e2e.ts +++ b/scripts/e2e/e2e.ts @@ -481,12 +481,31 @@ describe("Bridge e2e test", () => { expect(await childBridge.paused()).to.true; } + // Wrap 1 IMX + let [priorityFee, maxFee] = await getFee(childProvider); + resp = await childWIMX.connect(childTestWallet).deposit({ + value: ethers.utils.parseEther("1.0"), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + let amt = ethers.utils.parseEther("0.5"); let bridgeFee = ethers.utils.parseEther("1.0"); - await expect( - withdrawWIMX(childTestWallet, amt, bridgeFee, null) - ).to.be.rejectedWith("Pausable: paused"); + // Approve + [priorityFee, maxFee] = await getFee(childProvider); + resp = await childWIMX.connect(childTestWallet).approve(childBridge.address, amt, { + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + await expect(childBridge.connect(childTestWallet).withdrawWIMX(amt, { + value: amt.add(bridgeFee), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + })).to.be.rejectedWith("Pausable: paused"); // Unpause child bridge resp = await childBridge.connect(childPrivilegedWallet).unpause(); @@ -540,14 +559,37 @@ describe("Bridge e2e test", () => { }).timeout(2400000) it("should successfully withdraw wIMX to self from L2 to L1", async() => { - let amt = ethers.utils.parseEther("0.5"); - let bridgeFee = ethers.utils.parseEther("1.0"); + // Wrap 1 IMX + let [priorityFee, maxFee] = await getFee(childProvider); + let resp = await childWIMX.connect(childTestWallet).deposit({ + value: ethers.utils.parseEther("1.0"), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); // Get IMX balance on root & child chains before withdraw let preBalL1 = await rootIMX.balanceOf(rootTestWallet.address); let preBalL2 = await childWIMX.balanceOf(childTestWallet.address); - let resp = await withdrawWIMX(rootTestWallet, amt, bridgeFee, null); + let amt = ethers.utils.parseEther("0.5"); + let bridgeFee = ethers.utils.parseEther("1.0"); + + // Approve + [priorityFee, maxFee] = await getFee(childProvider); + resp = await childWIMX.connect(childTestWallet).approve(childBridge.address, amt, { + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + // wIMX withdraw L2 to L1 + [priorityFee, maxFee] = await getFee(childProvider); + resp = await childBridge.connect(childTestWallet).withdrawWIMX(amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); await waitForReceipt(resp.hash, childProvider); let postBalL1 = preBalL1; @@ -569,13 +611,37 @@ describe("Bridge e2e test", () => { it("should successfully withdraw wIMX to others from L2 to L1", async() => { let rootRecipient = rootPrivilegedWallet.address; - let amt = ethers.utils.parseEther("0.5"); - let bridgeFee = ethers.utils.parseEther("1.0"); + // Wrap 1 IMX + let [priorityFee, maxFee] = await getFee(childProvider); + let resp = await childWIMX.connect(childTestWallet).deposit({ + value: ethers.utils.parseEther("1.0"), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + // Get IMX balance on root & child chains before withdraw let preBalL1 = await rootIMX.balanceOf(rootRecipient); let preBalL2 = await childWIMX.balanceOf(childTestWallet.address); - let resp = await withdrawWIMX(rootTestWallet, amt, bridgeFee, rootRecipient); + let amt = ethers.utils.parseEther("0.5"); + let bridgeFee = ethers.utils.parseEther("1.0"); + + // Approve + [priorityFee, maxFee] = await getFee(childProvider); + resp = await childWIMX.connect(childTestWallet).approve(childBridge.address, amt, { + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + // wIMX withdraw L2 to L1 + [priorityFee, maxFee] = await getFee(childProvider); + resp = await childBridge.connect(childTestWallet).withdrawWIMXTo(rootRecipient, amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); await waitForReceipt(resp.hash, childProvider); let postBalL1 = preBalL1; @@ -605,14 +671,37 @@ describe("Bridge e2e test", () => { expect(await rootBridge.paused()).to.true; } - let amt = ethers.utils.parseEther("0.5"); - let bridgeFee = ethers.utils.parseEther("1.0"); + // Wrap 1 IMX + let [priorityFee, maxFee] = await getFee(childProvider); + resp = await childWIMX.connect(childTestWallet).deposit({ + value: ethers.utils.parseEther("1.0"), + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); // Get IMX balance on root & child chains before withdraw let preBalL1 = await rootIMX.balanceOf(rootTestWallet.address); let preBalL2 = await childWIMX.balanceOf(childTestWallet.address); - resp = await withdrawWIMX(rootTestWallet, amt, bridgeFee, null); + let amt = ethers.utils.parseEther("0.5"); + let bridgeFee = ethers.utils.parseEther("1.0"); + + // Approve + [priorityFee, maxFee] = await getFee(childProvider); + resp = await childWIMX.connect(childTestWallet).approve(childBridge.address, amt, { + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); + await waitForReceipt(resp.hash, childProvider); + + // wIMX withdraw L2 to L1 + [priorityFee, maxFee] = await getFee(childProvider); + resp = await childBridge.connect(childTestWallet).withdrawWIMX(amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }); await waitForReceipt(resp.hash, childProvider); await waitUntilSucceed(axelarAPI, resp.hash); @@ -881,14 +970,27 @@ describe("Bridge e2e test", () => { }).timeout(2400000) it("should successfully deposit wETH to self from L1 to L2", async() => { - let amt = ethers.utils.parseEther("0.001"); - let bridgeFee = ethers.utils.parseEther("0.001"); + // Wrap 0.01 ETH + let resp = await rootWETH.connect(rootTestWallet).deposit({ + value: ethers.utils.parseEther("0.01"), + }) + await waitForReceipt(resp.hash, rootProvider); // Get ETH balance on root & child chains before withdraw let preBalL1 = await rootWETH.balanceOf(rootTestWallet.address); let preBalL2 = await childETH.balanceOf(childTestWallet.address); - let resp = await depositWETH(rootTestWallet, amt, bridgeFee, null); + let amt = ethers.utils.parseEther("0.001"); + let bridgeFee = ethers.utils.parseEther("0.001"); + + // Approve + resp = await rootWETH.connect(rootTestWallet).approve(rootBridge.address, amt); + await waitForReceipt(resp.hash, rootProvider); + + // wETH deposit L1 to L2 + resp = await rootBridge.connect(rootTestWallet).deposit(rootWETH.address, amt, { + value: bridgeFee, + }) await waitForReceipt(resp.hash, rootProvider); let postBalL1 = await rootWETH.balanceOf(rootTestWallet.address); @@ -910,14 +1012,27 @@ describe("Bridge e2e test", () => { it("should successfully deposit wETH to others from L1 to L2", async() => { let childRecipient = childPrivilegedWallet.address; - let amt = ethers.utils.parseEther("0.001"); - let bridgeFee = ethers.utils.parseEther("0.001"); + // Wrap 0.01 ETH + let resp = await rootWETH.connect(rootTestWallet).deposit({ + value: ethers.utils.parseEther("0.01"), + }) + await waitForReceipt(resp.hash, rootProvider); // Get ETH balance on root & child chains before withdraw let preBalL1 = await rootWETH.balanceOf(rootTestWallet.address); let preBalL2 = await childETH.balanceOf(childRecipient); - let resp = await depositWETH(rootTestWallet, amt, bridgeFee, childRecipient); + let amt = ethers.utils.parseEther("0.001"); + let bridgeFee = ethers.utils.parseEther("0.001"); + + // Approve + resp = await rootWETH.connect(rootTestWallet).approve(rootBridge.address, amt); + await waitForReceipt(resp.hash, rootProvider); + + // wETH deposit L1 to L2 + resp = await rootBridge.connect(rootTestWallet).depositTo(rootWETH.address, childRecipient, amt, { + value: bridgeFee, + }) await waitForReceipt(resp.hash, rootProvider); let postBalL1 = await rootWETH.balanceOf(rootTestWallet.address); @@ -1686,39 +1801,6 @@ describe("Bridge e2e test", () => { } } - async function withdrawWIMX(sender: ethers.Wallet, amt: ethers.BigNumber, bridgeFee: ethers.BigNumber, recipient: string | null) { - let [priorityFee, maxFee] = await getFee(childProvider); - - // Wrap IMX - let resp = await childWIMX.connect(sender).deposit({ - value: amt, - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); - await waitForReceipt(resp.hash, childProvider); - - // Approve - resp = await childWIMX.connect(sender).approve(childBridge.address, amt, { - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); - await waitForReceipt(resp.hash, childProvider); - - if (recipient == null) { - return childBridge.connect(sender).withdrawWIMX(amt, { - value: bridgeFee, - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); - } else { - return childBridge.connect(sender).withdrawWIMXTo(recipient, amt, { - value: bridgeFee, - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); - } - } - async function depositETH(sender: ethers.Wallet, amt: ethers.BigNumber, bridgeFee: ethers.BigNumber, recipient: string | null) { if (recipient == null) { return rootBridge.connect(sender).depositETH(amt, { @@ -1731,28 +1813,6 @@ describe("Bridge e2e test", () => { } } - async function depositWETH(sender: ethers.Wallet, amt: ethers.BigNumber, bridgeFee: ethers.BigNumber, recipient: string | null) { - // Wrap ETH - let resp = await rootWETH.connect(sender).deposit({ - value: amt, - }) - await waitForReceipt(resp.hash, rootProvider); - - // Approve - resp = await rootBridge.connect(sender).approve(rootBridge.address, amt); - await waitForReceipt(resp.hash, rootProvider); - - if (recipient == null) { - return rootBridge.connect(sender).deposit(rootWETH.address, amt, { - value: bridgeFee, - }); - } else { - return rootBridge.connect(sender).depositTo(rootWETH.address, recipient, amt, { - value: bridgeFee, - }); - } - } - async function withdrawETH(sender: ethers.Wallet, amt: ethers.BigNumber, bridgeFee: ethers.BigNumber, recipient: string | null) { let [priorityFee, maxFee] = await getFee(childProvider); From edc3b7516c3856c974f8d288e4158c75f3835deb Mon Sep 17 00:00:00 2001 From: wcgcyx Date: Mon, 12 Feb 2024 14:21:59 +1000 Subject: [PATCH 40/40] Fix e2e --- scripts/e2e/e2e.ts | 139 ++++++++++++++++++++++++++++----------------- 1 file changed, 86 insertions(+), 53 deletions(-) diff --git a/scripts/e2e/e2e.ts b/scripts/e2e/e2e.ts index cc5db60d..7998941d 100644 --- a/scripts/e2e/e2e.ts +++ b/scripts/e2e/e2e.ts @@ -1077,7 +1077,8 @@ describe("Bridge e2e test", () => { }).timeout(2400000) it("should not withdraw ETH if balance is insufficient", async() => { - let amt = await childETH.balanceOf(childTestWallet.address).add(1); + let amt = await childETH.balanceOf(childTestWallet.address); + amt = amt.add(1); let bridgeFee = ethers.utils.parseEther("1.0"); // ETH withdraw L2 to L1 @@ -1351,14 +1352,31 @@ describe("Bridge e2e test", () => { let amt = balance.add(1); let bridgeFee = ethers.utils.parseEther("0.001"); - await expect( - depositERC20(rootTestWallet, amt, bridgeFee, null) - ).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); + // Approve + let resp = await rootCustomToken.connect(rootTestWallet).approve(rootBridge.address, amt); + await waitForReceipt(resp.hash, rootProvider); + + await expect(rootBridge.connect(rootTestWallet).deposit(rootCustomToken.address, amt, { + value: bridgeFee, + })).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); }).timeout(2400000) // Local only it("should not deposit mapped ERC20 Token if root bridge is paused", async() => { - let resp; + // Transfer 0.1 ETH to root pauser + let resp = await rootTestWallet.sendTransaction({ + to: rootPauserWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, rootProvider); + + // Transfer 0.1 ETH to root unpauser + resp = await rootTestWallet.sendTransaction({ + to: rootPrivilegedWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, rootProvider); + // Pause root bridge if (!await rootBridge.paused()) { resp = await rootBridge.connect(rootPauserWallet).pause(); @@ -1370,10 +1388,14 @@ describe("Bridge e2e test", () => { let amt = ethers.utils.parseEther("1.0"); let bridgeFee = ethers.utils.parseEther("0.001"); + // Approve + resp = await rootCustomToken.connect(rootTestWallet).approve(rootBridge.address, amt); + await waitForReceipt(resp.hash, rootProvider); + // Fail to deposit on L1 - await expect( - depositERC20(rootTestWallet, amt, bridgeFee, null) - ).to.be.rejectedWith("Pausable: paused"); + await expect(rootBridge.connect(rootTestWallet).deposit(rootCustomToken.address, amt, { + value: bridgeFee, + })).to.be.rejectedWith("Pausable: paused"); // Unpause root bridge resp = await rootBridge.connect(rootPrivilegedWallet).unpause(); @@ -1389,7 +1411,14 @@ describe("Bridge e2e test", () => { let amt = ethers.utils.parseEther("10.0"); let bridgeFee = ethers.utils.parseEther("0.001"); - let resp = await depositERC20(rootTestWallet, amt, bridgeFee, null); + // Approve + let resp = await rootCustomToken.connect(rootTestWallet).approve(rootBridge.address, amt); + await waitForReceipt(resp.hash, rootProvider); + + // Token deposit L1 to L2 + resp = await rootBridge.connect(rootTestWallet).deposit(rootCustomToken.address, amt, { + value: bridgeFee, + }) await waitForReceipt(resp.hash, rootProvider); let postBalL1 = await rootCustomToken.balanceOf(rootTestWallet.address); @@ -1418,7 +1447,14 @@ describe("Bridge e2e test", () => { let amt = ethers.utils.parseEther("1.0"); let bridgeFee = ethers.utils.parseEther("0.001"); - let resp = await depositERC20(rootTestWallet, amt, bridgeFee, childRecipient); + // Approve + let resp = await rootCustomToken.connect(rootTestWallet).approve(rootBridge.address, amt); + await waitForReceipt(resp.hash, rootProvider); + + // Token deposit L1 to L2 + resp = await rootBridge.connect(rootTestWallet).depositTo(rootCustomToken.address, childRecipient, amt, { + value: bridgeFee, + }) await waitForReceipt(resp.hash, rootProvider); let postBalL1 = await rootCustomToken.balanceOf(rootTestWallet.address); @@ -1454,7 +1490,20 @@ describe("Bridge e2e test", () => { // Local only it("should not withdraw mapped ERC20 Token if child bridge is paused", async() => { - let resp; + // Transfer 0.1 IMX to child pauser + let resp = await childTestWallet.sendTransaction({ + to: childPauserWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, childProvider); + + // Transfer 0.1 IMX to child unpauser + resp = await childTestWallet.sendTransaction({ + to: childPrivilegedWallet.address, + value: ethers.utils.parseEther("0.1"), + }) + await waitForReceipt(resp.hash, childProvider); + // Pause child bridge if (!await childBridge.paused()) { resp = await childBridge.connect(childPauserWallet).pause(); @@ -1466,9 +1515,12 @@ describe("Bridge e2e test", () => { let bridgeFee = ethers.utils.parseEther("1.0"); // Token withdraw L2 to L1 - await expect( - withdrawERC20(childTestWallet, amt, bridgeFee, null) - ).to.be.rejectedWith("Pausable: paused"); + let [priorityFee, maxFee] = await getFee(childProvider); + await expect(childBridge.connect(childTestWallet).withdraw(childCustomToken.address, amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + })).to.be.rejectedWith("Pausable: paused"); // Unpause child bridge resp = await childBridge.connect(childPrivilegedWallet).unpause(); @@ -1481,9 +1533,12 @@ describe("Bridge e2e test", () => { let bridgeFee = ethers.utils.parseEther("1.0"); // ETH withdraw L2 to L1 - await expect( - withdrawERC20(childTestWallet, amt, bridgeFee, null) - ).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); + let [priorityFee, maxFee] = await getFee(childProvider); + await expect(childBridge.connect(childTestWallet).withdraw(childCustomToken.address, amt.add(1), { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + })).to.be.rejectedWith("UNPREDICTABLE_GAS_LIMIT"); }).timeout(2400000) it("should successfully withdraw mapped ERC20 Token to self from L2 to L1", async() => { @@ -1494,7 +1549,13 @@ describe("Bridge e2e test", () => { let amt = ethers.utils.parseEther("0.5"); let bridgeFee = ethers.utils.parseEther("1.0"); - let resp = await withdrawERC20(childTestWallet, amt, bridgeFee, null); + // Token withdraw L2 to L1 + let [priorityFee, maxFee] = await getFee(childProvider); + let resp = await childBridge.connect(childTestWallet).withdraw(childCustomToken.address, amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }) await waitForReceipt(resp.hash, childProvider); let postBalL1 = preBalL1; @@ -1523,7 +1584,13 @@ describe("Bridge e2e test", () => { let amt = ethers.utils.parseEther("0.5"); let bridgeFee = ethers.utils.parseEther("1.0"); - let resp = await withdrawERC20(childTestWallet, amt, bridgeFee, rootRecipient); + // Token withdraw L2 to L1 + let [priorityFee, maxFee] = await getFee(childProvider); + let resp = await childBridge.connect(childTestWallet).withdrawTo(childCustomToken.address, rootRecipient, amt, { + value: bridgeFee, + maxPriorityFeePerGas: priorityFee, + maxFeePerGas: maxFee, + }) await waitForReceipt(resp.hash, childProvider); let postBalL1 = preBalL1; @@ -1830,38 +1897,4 @@ describe("Bridge e2e test", () => { }) } } - - async function depositERC20(sender: ethers.Wallet, amt: ethers.BigNumber, bridgeFee: ethers.BigNumber, recipient: string | null) { - // Approve - let resp = await rootCustomToken.connect(rootTestWallet).approve(rootBridge.address, amt); - await waitForReceipt(resp.hash, rootProvider); - - if (recipient == null) { - return rootBridge.connect(sender).deposit(rootTestWallet.address, amt, { - value: bridgeFee, - }); - } else { - return rootBridge.connect(sender).depositTo(rootTestWallet.address, recipient, amt, { - value: bridgeFee, - }); - } - } - - async function withdrawERC20(sender: ethers.Wallet, amt: ethers.BigNumber, bridgeFee: ethers.BigNumber, recipient: string | null) { - let [priorityFee, maxFee] = await getFee(childProvider); - - if (recipient == null) { - return childBridge.connect(sender).withdraw(childCustomToken.address, amt, { - value: bridgeFee, - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); - } else { - return childBridge.connect(sender).withdrawTo(childCustomToken.address, recipient, amt, { - value: bridgeFee, - maxPriorityFeePerGas: priorityFee, - maxFeePerGas: maxFee, - }); - } - } }) \ No newline at end of file