diff --git a/.gitignore b/.gitignore index 7c356e0..2f839f7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ bin cache artifacts typechain/ -.env +*.env* # OpenZeppelin .openzeppelin/dev-*.json diff --git a/hardhat.config.ts b/hardhat.config.ts index 366936d..f4abbb1 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -8,8 +8,7 @@ import "@openzeppelin/hardhat-upgrades"; import "@nomiclabs/hardhat-etherscan"; import "@nomiclabs/hardhat-ethers"; import "solidity-coverage"; -import "./tasks/merkle"; -import "./tasks/deploy"; +import { getEnabledCategories } from "node:trace_events"; task("accounts", "Prints the list of accounts", async (args, hre) => { const accounts = await hre.ethers.getSigners(); @@ -31,7 +30,17 @@ const config: HardhatUserConfig = { }, }, }, - { version: "0.6.8", settings: {} } + { + version: "0.6.8", + settings: { + optimizer: { + enabled: true, + runs: 200 + } + } + }, + { version: "0.6.0", settings: {} } + ], }, paths: { @@ -42,18 +51,18 @@ const config: HardhatUserConfig = { timeout: 50000, }, networks: { - kovan: { - accounts: { mnemonic: process.env.TESTNET_MNEMONIC || "" }, - url: `https://kovan.infura.io/v3/0e6434f252a949719227b5d68caa2657`, - }, - ropsten: { - accounts: { mnemonic: process.env.TESTNET_MNEMONIC || "" }, - url: "https://ropsten.infura.io/v3/77c3d733140f4c12a77699e24cb30c27", - }, - rinkeby: { - accounts: { mnemonic: process.env.TESTNET_MNEMONIC || "" }, - url: "https://rinkeby.infura.io/v3/77c3d733140f4c12a77699e24cb30c27", - }, + // kovan: { + // accounts: { mnemonic: process.env.TESTNET_MNEMONIC || "" }, + // url: `https://kovan.infura.io/v3/0e6434f252a949719227b5d68caa2657`, + // }, + // ropsten: { + // accounts: { mnemonic: process.env.TESTNET_MNEMONIC || "" }, + // url: "https://ropsten.infura.io/v3/77c3d733140f4c12a77699e24cb30c27", + // }, + // rinkeby: { + // accounts: { mnemonic: process.env.TESTNET_MNEMONIC || "" }, + // url: "https://rinkeby.infura.io/v3/77c3d733140f4c12a77699e24cb30c27", + // }, sepolia: { chainId: 11155111, accounts: [`${process.env.TESTNET_PRIVATE_KEY}`], diff --git a/package.json b/package.json index 508f752..fdbc61a 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@types/node": "^14.14.25", "@typescript-eslint/eslint-plugin": "^4.16.1", "@typescript-eslint/parser": "^4.16.1", + "@zero-tech/protocol-utils": "0.1.0", "chai": "^4.3.3", "dotenv": "^8.2.0", "eslint": "^7.21.0", @@ -35,7 +36,6 @@ "typescript": "^4.1.4" }, "scripts": { - "postinstall": "hardhat typechain", "typechain": "hardhat typechain", "build": "yarn run clean && yarn run compile", "clean": "hardhat clean", @@ -53,4 +53,4 @@ "name": "zdao-token", "version": "0.0.0", "license": "MIT" -} \ No newline at end of file +} diff --git a/scripts/upgrade/01-deploy-v1.ts b/scripts/upgrade/01-deploy-v1.ts index cd90e69..76578cb 100644 --- a/scripts/upgrade/01-deploy-v1.ts +++ b/scripts/upgrade/01-deploy-v1.ts @@ -1,107 +1,50 @@ import * as hre from "hardhat"; -import * as fs from "fs"; -import assert from "assert"; -import { - ERC20Mock__factory, - ZeroDAOToken__factory, - ZeroDAOToken -} from "../../typechain"; -import { deployAndTransferOwner } from "../../test/helpers/deploy-and-transfer"; -import { getLogger } from "../../utilities"; - -const logger = getLogger("deploy-v1"); +import { deployFundTransfer } from "../../test/helpers/deploy-fund-transfer"; +import { DEFAULT_MOCK_TOKEN_AMOUNT, DEFAULT_MOCK_TOKEN_DECIMALS } from "../../test/helpers/constants"; /** - * Perform a deployment of a ZeroDAOToken as well as an ERC20Mock token + * Script 01: Deploy V1 Token Contract + * + * This script performs the initial setup for testing the WILD token upgrade process. + * It serves as the first step in a multi-step upgrade testing workflow. + * + * Operations performed: + * - Deploy the ZeroDAOToken (V1) as an upgradeable proxy + * - Deploy an ERC20Mock token for testing purposes + * - Mint mock tokens to the ZeroDAOToken contract + * - Transfer ownership of both the Proxy and ProxyAdmin to a specified address + * + * Environment Variables Required: + * - OWNER_ADDRESS: The address to transfer ownership to (optional for hardhat network) + * + * Output: + * - Creates a JSON file with deployment details: `01-deploy-v1-${network}.json` */ const main = async () => { const [deployer] = await hre.ethers.getSigners(); + const outputFile = `01-deploy-v1-${hre.network.name}.json`; - const name = "Wilder World Test"; - const symbol = "tWILD"; - - // The address of the owner to be transferred to. + // The address of the new owner to be transferred to for WILD as well as ProxyAdmin // If not using hardhat, this should be a Safe address let newOwnerAddress = process.env.OWNER_ADDRESS; if (!newOwnerAddress) { - if (hre.network.name !== "hardhat") { - throw new Error("No address given for transfer ownership call"); - } else { - // Hardhat, can just use the same address in testing script - newOwnerAddress = deployer.address; - } + throw new Error("No address given for transfer ownership call"); } - logger.info(`Network: ${hre.network.name}`); - logger.info(`Deploying token: ${name} with symbol ${symbol}`); - logger.info(`Deployer: ${deployer.address}`); - logger.info(`New owner will be: ${newOwnerAddress}`); - - // Deploy ZeroDaoTokenV1 and transfer ownership - const zeroDAOTokenV1 = await deployAndTransferOwner( - new ZeroDAOToken__factory(deployer), - [ - name, - symbol - ], - newOwnerAddress + const amount = hre.ethers.utils.parseUnits( + DEFAULT_MOCK_TOKEN_AMOUNT, + DEFAULT_MOCK_TOKEN_DECIMALS ); - logger.info(`${name} token deployed to address: ${zeroDAOTokenV1.address}`); - logger.info(`Ownership transferred to: ${newOwnerAddress}`); - - // Deploy a mock token to later transfer balance to the zeroDAOToken - const mockName = "Mock Token"; - const mockSymbol = "MOCK"; - logger.info(`Deploying token: ${mockName} with symbol ${mockSymbol}`); - - const mockTokenFactory = new ERC20Mock__factory(deployer); - const mockToken = await mockTokenFactory.deploy(mockName, mockSymbol); - - if (hre.network.name !== "hardhat") { - await mockToken.deployed(); - } - - logger.info(`${mockName} deployed to address: ${mockToken.address}`); - - const amount = hre.ethers.utils.parseUnits("500000", 6); - logger.info(`Minting ${amount.toString()} of ${mockSymbol} to ${zeroDAOTokenV1.address}`); - - const balanceBefore = await mockToken.balanceOf(zeroDAOTokenV1.address); - logger.info(`Balance before minting: ${balanceBefore.toString()}`); - - const tx = await mockToken.mint(zeroDAOTokenV1.address, amount); - await tx.wait(hre.network.name === "hardhat" ? 0 : 3); - - const balanceAfter = await mockToken.balanceOf(zeroDAOTokenV1.address); - logger.info(`Balance after minting: ${balanceAfter.toString()}`); - - // Confirm balance has changed correctly - assert(balanceAfter.eq(balanceBefore.add(amount)), "Balance minting failed"); - - logger.info(`Minting successful: ${amount.toString()} of ${mockSymbol} to ${zeroDAOTokenV1.address}`); - - const obj = { - network: hre.network.name, - zeroDAOToken: zeroDAOTokenV1.address, - zeroDAOTokenOwner: newOwnerAddress, - mockToken: mockToken.address, - mockTokenAmount: amount.toString(), - deployedAt: new Date().toISOString() - }; - - // Write to file with predictable naming - const outputFile = `01-deploy-v1-${hre.network.name}-${Date.now()}.json`; - fs.writeFileSync(outputFile, JSON.stringify(obj, undefined, 2)); - logger.info(`Deployment data saved to: ${outputFile}`); - - logger.info("=== Deployment Summary ==="); - logger.info(`Network: ${obj.network}`); - logger.info(`ZeroDAOToken: ${obj.zeroDAOToken}`); - logger.info(`ZeroDAOToken Owner: ${obj.zeroDAOTokenOwner}`); - logger.info(`MockToken: ${obj.mockToken}`); - logger.info(`MockToken Amount Minted: ${obj.mockTokenAmount}`); + // Deploy V1 token contract with mock token for testing + await deployFundTransfer( + deployer, + newOwnerAddress, + outputFile, + amount, + true + ); }; main() @@ -109,6 +52,5 @@ main() process.exit(0); }) .catch((error) => { - logger.error("Deployment failed:", error); - process.exit(1); + throw error; }); diff --git a/scripts/upgrade/02-deploy-v2.ts b/scripts/upgrade/02-deploy-v2.ts index 9d1172d..fe082e4 100644 --- a/scripts/upgrade/02-deploy-v2.ts +++ b/scripts/upgrade/02-deploy-v2.ts @@ -1,45 +1,41 @@ import * as hre from "hardhat"; -import * as fs from "fs"; -import { ZeroDAOTokenV2__factory, ZeroDAOTokenV2 } from "../../typechain"; -import { getLogger } from "../../utilities"; - -const logger = getLogger("deploy-v2"); +import { deployV2 } from "../../test/helpers/deploy-v2"; /** - * Deploy ZeroDAOTokenV2 implementation contract - * This deploys the V2 implementation that can later be used for upgrades + * Script 02: Deploy V2 Implementation Contract + * + * This script deploys the ZeroDAOTokenV2 implementation contract that will be used + * for upgrading the existing V1 proxy contract. This is step 2 in the upgrade process. + * + * Note: This only deploys the implementation contract, not a proxy. The actual upgrade + * happens separately using the proxy upgrade mechanism. + * + * Operations performed: + * - Deploy ZeroDAOTokenV2 implementation contract + * - Save deployment details to output file + * + * Prerequisites: + * - V1 contract should already be deployed (from script 01) + * - Deployer account should have sufficient funds for deployment + * + * Output: + * - Creates a JSON file with implementation address: `02-deploy-v2-${network}.json` + * + * Production Notes: + * - Ensure proper addresses are configured + * - Deployer account needs funds for deployment + * - For mainnet, proposer should be configured on Safe multisig */ const main = async () => { const [deployer] = await hre.ethers.getSigners(); - - logger.info(`Network: ${hre.network.name}`); - logger.info(`Deploying ZeroDAOTokenV2 implementation`); - logger.info(`Deployer: ${deployer.address}`); - - // Deploy ZeroDAOTokenV2 implementation (not as proxy) - const tokenFactory = new ZeroDAOTokenV2__factory(deployer); - const zeroDAOTokenV2 = await tokenFactory.deploy() as ZeroDAOTokenV2; - - if (hre.network.name !== "hardhat") { - await zeroDAOTokenV2.deployed(); - } - - logger.info(`ZeroDAOTokenV2 implementation deployed to address: ${zeroDAOTokenV2.address}`); - - const obj = { - network: hre.network.name, - zeroDAOTokenV2Implementation: zeroDAOTokenV2.address, - deployedAt: new Date().toISOString() - }; - - // Write to file with predictable naming const outputFile = `02-deploy-v2-${hre.network.name}.json`; - fs.writeFileSync(outputFile, JSON.stringify(obj, undefined, 2)); - logger.info(`Deployment data saved to: ${outputFile}`); - logger.info("=== Deployment Summary ==="); - logger.info(`Network: ${obj.network}`); - logger.info(`ZeroDAOTokenV2 Implementation: ${obj.zeroDAOTokenV2Implementation}`); + // Deploy V2 implementation contract + await deployV2( + deployer, + outputFile, + true + ); }; main() @@ -47,6 +43,5 @@ main() process.exit(0); }) .catch((error) => { - logger.error("Deployment failed:", error); - process.exit(1); + throw error; }); diff --git a/scripts/upgrade/03-read-state.ts b/scripts/upgrade/03-read-state.ts new file mode 100644 index 0000000..b1df0c1 --- /dev/null +++ b/scripts/upgrade/03-read-state.ts @@ -0,0 +1,56 @@ +import * as hre from "hardhat"; +import { readState } from "../../test/helpers/read-state"; +import { ZeroDAOToken__factory } from "../../typechain"; + +/** + * Script 03: Read Contract State (Pre-Upgrade) + * + * This script reads and captures the current state of the ZeroDAOToken contract + * before performing an upgrade. This creates a baseline snapshot that can be + * compared against the post-upgrade state to ensure data integrity. + * + * Purpose: + * - Capture contract storage state before upgrade + * - Create a reference point for state comparison + * - Ensure upgrade doesn't corrupt existing data + * + * Usage Pattern: + * 1. Run this script BEFORE upgrade (captures pre-upgrade state) + * 2. Perform the contract upgrade + * 3. Run script 04 to compare pre/post upgrade states + * + * Environment Variables Required: + * - TOKEN_ADDRESS: Address of the deployed ZeroDAOToken contract + * + * Output: + * - Creates a JSON file with contract state: `03-read-state-${network}.json` + * + * @note This script should be executed twice in the upgrade workflow: + * once BEFORE upgrading and once AFTER upgrading (via script 04) + */ +const main = async () => { + const [creator] = await hre.ethers.getSigners(); + const outputFile = `03-read-state-${hre.network.name}.json`; + + // Get the token address from initial deployment in step 1 + let tokenAddress = process.env.TOKEN_ADDRESS; + if (!tokenAddress) { + throw Error("No token address present in env"); + } + + // Read and save current contract state + await readState( + creator, + tokenAddress, + outputFile, + false + ); +} + +main() + .then(() => { + process.exit(0); + }) + .catch((error) => { + throw error; + }); diff --git a/scripts/upgrade/04-read-and-compare-state.ts b/scripts/upgrade/04-read-and-compare-state.ts new file mode 100644 index 0000000..44c22f8 --- /dev/null +++ b/scripts/upgrade/04-read-and-compare-state.ts @@ -0,0 +1,72 @@ +import * as hre from "hardhat"; +import * as fs from "fs"; +import { readCompareState } from "../../test/helpers/read-compare-state"; + +/** + * Script 04: Read and Compare Contract State (Post-Upgrade) + * + * This script reads the current contract state after an upgrade and compares it + * with the pre-upgrade state captured by script 03. This ensures that the upgrade + * process preserved all existing data and didn't introduce any storage corruption. + * + * Purpose: + * - Read current (post-upgrade) contract state + * - Compare with pre-upgrade state from script 03 + * - Validate that upgrade preserved data integrity + * - Generate comparison report + * + * Workflow Position: + * 1. Script 03: Capture pre-upgrade state + * 2. Perform contract upgrade + * 3. THIS SCRIPT: Compare post-upgrade state with pre-upgrade baseline + * + * Environment Variables Required: + * - TOKEN_ADDRESS: Address of the upgraded ZeroDAOToken contract + * + * Prerequisites: + * - Pre-upgrade state file must exist (created by script 03) + * - Contract upgrade must have been completed + * + * Output: + * - Creates a JSON file with comparison results: `04-read-and-compare-state-${network}.json` + * - Throws error if state differences are detected + * + * @throws Error if pre-upgrade state file is not found + * @throws Error if storage comparison fails (indicates upgrade corruption) + */ +const main = async () => { + const [creator] = await hre.ethers.getSigners(); + const outputFile = `04-read-and-compare-state-${hre.network.name}.json`; + + // Find the pre-upgrade state file created by script 03 + const files = fs.readdirSync('.'); + const preUpgradeFile = files.find(file => file.startsWith('03-read-state-') && file.endsWith('.json')); + + if (!preUpgradeFile) { + throw new Error('Pre-upgrade state file not found. Expected file starting with "03-read-state-". Please run script 03 first.'); + } + + const preUpgradeState = JSON.parse(fs.readFileSync(preUpgradeFile, 'utf8')); + + let tokenAddress = process.env.TOKEN_ADDRESS; + if (!tokenAddress) { + throw Error("No token address present in env"); + } + + // Read current state and compare with pre-upgrade state + await readCompareState( + creator, + tokenAddress, + preUpgradeState, + outputFile, + true + ); +} + +main() + .then(() => { + process.exit(0); + }) + .catch((error) => { + throw error; + }); diff --git a/scripts/upgrade/upgrade-test.ts b/scripts/upgrade/upgrade-test.ts deleted file mode 100644 index 0eea6b9..0000000 --- a/scripts/upgrade/upgrade-test.ts +++ /dev/null @@ -1,75 +0,0 @@ -import * as hre from "hardhat"; - -import { - ZeroDAOTokenV2, - ERC20Mock__factory, - ZeroDAOToken__factory, - ZeroDAOTokenV2__factory -} from "../../typechain"; - -import { getLogger } from "../../utilities"; - -const logger = getLogger("test-upgrade::balance::transfer"); - - -/** - * Full flow testing the token contract upgrade on Sepolia - * TODO break into separate upgrades - */ -const main = async () => { - const [deployer] = await hre.ethers.getSigners(); - - // Sepolia address - const wildAddress = process.env.WILD_ADDRESS; - if (!wildAddress) throw Error("No WILD token contract address given"); - - const mockAddress = process.env.MOCK_ADDRESS ?? ""; - if (!mockAddress) throw Error("No MOCK token contract address given"); - - const mockToken = new ERC20Mock__factory(deployer).attach(mockAddress); - - logger.info(`1. Give balance of "mockToken" to "wildToken" contract`); - logger.info("Balance Before Mint: ", await mockToken.balanceOf(wildAddress)); - - // Minting to a contract works - const mintTx = await mockToken.connect(deployer)["mint(address,uint256)"](wildAddress, hre.ethers.utils.parseUnits("500000", 6)) - - await mintTx.wait(3); - - logger.info("Balance After Mint: ", await mockToken.balanceOf(wildAddress)); - - logger.info(`2. Upgrade "wildToken" from ZeroDAOToken to ZeroDAOTokenV2`); - - const newWildToken = await hre.upgrades.upgradeProxy( - wildAddress, - new ZeroDAOTokenV2__factory(deployer), - ) as ZeroDAOTokenV2; - - logger.info("Successfully upgraded..."); - - logger.info("3. Confirm balance is unchanged"); - - const balance = await mockToken.balanceOf(wildAddress); - - logger.info("Balance of wildToken before: ", await mockToken.balanceOf(wildAddress)); - logger.info("Balance of deployer before: ", await mockToken.balanceOf(deployer.address)); - - logger.info("4. Withdraw balance"); - - const withdrawTx = await newWildToken.withdrawERC20( - mockAddress, - deployer.address, - balance - ); - - const receipt = await withdrawTx.wait(3); - - logger.info("withdrawTx Hash: ", receipt.transactionHash); - - // wildToken should have no balance after - // Deployer should have entire `mockToken` balance after - logger.info("Balance of wildToken after: ", await mockToken.balanceOf(wildAddress)); - logger.info("Balance of deployer after: ", await mockToken.balanceOf(deployer.address)); -} - -main(); \ No newline at end of file diff --git a/scripts/utils/storage-check.ts b/scripts/utils/storage-check.ts new file mode 100644 index 0000000..08894b8 --- /dev/null +++ b/scripts/utils/storage-check.ts @@ -0,0 +1,98 @@ +import { getStorageLayout, getUnlinkedBytecode, getVersion, StorageLayout } from '@openzeppelin/upgrades-core'; +import { readValidations } from "@openzeppelin/hardhat-upgrades/dist/validations"; +import { BigNumber, Contract, ContractFactory } from "ethers"; +import * as hre from "hardhat"; + + +export type ContractStorageElement = string | number | BigNumber | Array; +export type ContractStorageData = Array<{ + [label: string]: ContractStorageElement; +}>; +export type ContractStorageDiff = Array<{ + key: string; + valueBefore: ContractStorageElement; + valueAfter: ContractStorageElement; +}>; + + +export const getContractStorageLayout = async ( + contractFactory: ContractFactory +): Promise => { + const validations = await readValidations(hre); + const unlinkedBytecode = getUnlinkedBytecode(validations, contractFactory.bytecode); + const version = getVersion(unlinkedBytecode, contractFactory.bytecode); + + return getStorageLayout(validations, version); +}; + +export const readContractStorage = async ( + contractFactory: ContractFactory, + contractObj: Contract +): Promise => { + const layout = await getContractStorageLayout(contractFactory); + + return layout.storage.reduce( + async ( + acc: Promise, + { label, type } + ): Promise => { + const newAcc = await acc; + + if (type.includes("mapping") || type.includes("array")) + return newAcc; // Skip mappings and arrays + + try { + const newLabel = label.startsWith("_") ? label.slice(1) : label; + const value = await contractObj[(newLabel as keyof Contract)](); + + newAcc.push({ [label]: value }); + } catch (e: unknown) { + if ((e as Error).message.includes("is not a function")) + return newAcc; // Skip non-public variables + + console.log(`Error on LABEL ${label}: ${(e as Error).message}`); + } + + return newAcc; + }, + Promise.resolve([]) + ); +}; + + +export const compareStorageData = ( + dataBefore: ContractStorageData, + dataAfter: ContractStorageData, +) => { + const storageDiff = dataAfter.reduce( + (acc: ContractStorageDiff | undefined, stateVar, idx) => { + const [key, value] = Object.entries(stateVar)[0]; + + if (!dataBefore[idx]) return acc; + + const equals = BigNumber.isBigNumber(value) + ? (value as BigNumber).eq((dataBefore[idx][key] as BigNumber)) : value === dataBefore[idx][key]; + + if (!equals) { + console.error( + `Mismatch on state var ${key} at idx ${idx}! Prev value: ${dataBefore[idx][key]}, new value: ${value}` + ); + + return [ + ...acc as ContractStorageDiff, + { + key, + valueBefore: dataBefore[idx][key], + valueAfter: value, + }, + ]; + } else { + return acc; + } + }, [] + ); + + if (storageDiff && storageDiff.length > 0) { + throw new Error(`Storage data mismatch: ${JSON.stringify(storageDiff)}`); + } +}; diff --git a/test/helpers/constants.ts b/test/helpers/constants.ts new file mode 100644 index 0000000..ffb8fd2 --- /dev/null +++ b/test/helpers/constants.ts @@ -0,0 +1,35 @@ +// ZeroDAOToken Constants +export const DEFAULT_ZERO_TOKEN_NAME = "Wilder World Test"; +export const DEFAULT_ZERO_TOKEN_SYMBOL = "tWILD"; + +// Mock Token Constants +export const DEFAULT_MOCK_TOKEN_NAME = "Mock Token"; +export const DEFAULT_MOCK_TOKEN_SYMBOL = "MOCK"; +export const DEFAULT_TEST_MOCK_TOKEN_NAME = "Test Mock Token"; +export const DEFAULT_TEST_MOCK_TOKEN_SYMBOL = "TMOCK"; + +// Default Amounts +export const DEFAULT_MOCK_TOKEN_AMOUNT = "500000"; // 500,000 with 6 decimals +export const DEFAULT_MOCK_TOKEN_DECIMALS = 6; +export const DEFAULT_TEST_MINT_AMOUNT = "100000"; // 100,000 with 6 decimals +export const DEFAULT_WITHDRAW_AMOUNT = "50000"; // 50,000 with 6 decimals +export const DEFAULT_TOKEN_MINT_AMOUNT = "1000"; // 1,000 tokens (18 decimals) +export const DEFAULT_TRANSFER_AMOUNT = "100"; // 100 tokens (18 decimals) + +// Network Constants +export const HARDHAT_NETWORK_NAME = "hardhat"; + +// Logger Names +export const DEPLOY_FUND_TRANSFER_LOGGER = "deploy-fund-transfer"; +export const DEPLOY_V2_LOGGER = "deploy-v2"; +export const READ_STATE_LOGGER = "read-state" +export const READ_AND_COMPARE_LOGGER = "read-compare-state"; + +// Messages +export const TRANSFERRING_OWNERSHIP_MESSAGE = "Transferring ownership of proxy..."; +export const OWNERSHIP_TRANSFERRED_MESSAGE = "Ownership transferred successfully"; +export const DEPLOYING_V2_MESSAGE = "Deploying ZeroDAOTokenV2 implementation"; +export const READING_POST_UPGRADE_STATE_MESSAGE = "Reading post upgrade state..."; +export const COMPARING_STATES_MESSAGE = "Comparing pre and post upgrade states..."; +export const STORAGE_COMPARISON_PASSED_MESSAGE = "Storage comparison passed - no differences found"; +export const STORAGE_COMPARISON_FAILED_MESSAGE = "Storage comparison failed:"; diff --git a/test/helpers/deploy-and-transfer.ts b/test/helpers/deploy-and-transfer.ts deleted file mode 100644 index e14cc28..0000000 --- a/test/helpers/deploy-and-transfer.ts +++ /dev/null @@ -1,26 +0,0 @@ -import * as hre from "hardhat"; - -import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers" -import { Contract, ContractFactory } from "ethers" - -/** - * Deploy a contract of type `Factory` to the running chain then transfer ownership to `owner` - * @param factory - * @param args - * @param owner - */ -export const deployAndTransferOwner = async ( - factory: ContractFactory, - args: any[], - ownerAddress: string -) => { - const contract = await hre.upgrades.deployProxy(factory, args) as T; - - if (hre.network.name !== "hardhat") { - await contract.deployed(); - } - - await contract["transferOwnership(address)"](ownerAddress); - - return contract; -} \ No newline at end of file diff --git a/test/helpers/deploy-fund-transfer.ts b/test/helpers/deploy-fund-transfer.ts new file mode 100644 index 0000000..5d521a5 --- /dev/null +++ b/test/helpers/deploy-fund-transfer.ts @@ -0,0 +1,148 @@ +import * as hre from "hardhat"; +import * as fs from "fs"; +import { assert } from "console"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import { getLogger } from "../../utilities"; +import { ZeroDAOToken, ZeroDAOToken__factory, ERC20Mock__factory } from "../../typechain"; +import { + DEFAULT_ZERO_TOKEN_NAME, + DEFAULT_ZERO_TOKEN_SYMBOL, + DEFAULT_MOCK_TOKEN_NAME, + DEFAULT_MOCK_TOKEN_SYMBOL, + DEFAULT_MOCK_TOKEN_AMOUNT, + DEFAULT_MOCK_TOKEN_DECIMALS, + HARDHAT_NETWORK_NAME, + DEPLOY_FUND_TRANSFER_LOGGER, + TRANSFERRING_OWNERSHIP_MESSAGE, + OWNERSHIP_TRANSFERRED_MESSAGE +} from "./constants"; +import { BigNumber } from "ethers"; + +/** + * Deploy V1 Token Contract with Mock Token Setup + * + * This function handles the complete deployment and setup of the V1 ZeroDAOToken + * contract along with a mock ERC20 token for testing purposes. It's designed to + * create a complete testing environment for the upgrade process. + * + * @param creator - The signer account that will deploy the contracts + * @param outputFile - Optional path to save deployment details as JSON + * @param amount - Optional amount of mock tokens to mint (defaults to 500,000 with 6 decimals) + * + * @returns Promise - The deployed ZeroDAOToken contract instance + * + * Operations performed: + * 1. Deploy ZeroDAOToken V1 as an upgradeable proxy + * 2. Transfer ownership of the token contract to specified address + * 3. Transfer ownership of the proxy admin to specified address + * 4. Deploy a mock ERC20 token for testing + * 5. Mint mock tokens to the ZeroDAOToken contract + * 6. Save deployment details to output file (if specified) + * + * Environment Variables: + * - OWNER_ADDRESS: Address to transfer ownership to (optional for hardhat) + * + * @throws Error if OWNER_ADDRESS is not set for non-hardhat networks + * @throws AssertionError if token minting fails + */ +export const deployFundTransfer = async ( + creator: SignerWithAddress, + newOwnerAddress: string, + outputFile?: string, + amount?: number | BigNumber, + verbose: boolean = false +): Promise => { + const logger = getLogger(DEPLOY_FUND_TRANSFER_LOGGER); + logger.state.isEnabled = verbose; + + const name = DEFAULT_ZERO_TOKEN_NAME; + const symbol = DEFAULT_ZERO_TOKEN_SYMBOL; + + logger.info(`Network: ${hre.network.name}`); + logger.info(`Deploying token: ${name} with symbol ${symbol}`); + logger.info(`Deployer: ${creator.address}`); + logger.info(`New owner will be: ${newOwnerAddress}`); + + // Deploy ZeroDaoTokenV1 and transfer ownership + const factory = new ZeroDAOToken__factory(creator); + const zeroDAOTokenV1 = await hre.upgrades.deployProxy( + factory, + [ + name, + symbol + ], + ) as ZeroDAOToken; + + if (hre.network.name !== HARDHAT_NETWORK_NAME) { + await zeroDAOTokenV1.deployed(); + } + + logger.info(TRANSFERRING_OWNERSHIP_MESSAGE); + // Transfer ownership of contract itself + await zeroDAOTokenV1["transferOwnership(address)"](newOwnerAddress); + + // Transfer ownership or proxy admin, if needed + const proxyAdmin = await hre.upgrades.admin.getInstance(); + const owner = await proxyAdmin.owner(); + logger.info(`Current Proxy Admin owner: ${owner}`); + + if (owner !== newOwnerAddress && owner === creator.address) { + logger.info(`Transferring Proxy Admin Ownership to ${newOwnerAddress}`); + await proxyAdmin.transferOwnership(newOwnerAddress); + } + + logger.info(`${name} token deployed to address: ${zeroDAOTokenV1.address}`); + logger.info(OWNERSHIP_TRANSFERRED_MESSAGE); + + // Deploy a mock token to later transfer balance to the zeroDAOToken + const mockName = DEFAULT_MOCK_TOKEN_NAME; + const mockSymbol = DEFAULT_MOCK_TOKEN_SYMBOL; + logger.info(`Deploying token: ${mockName} with symbol ${mockSymbol}`); + + const mockTokenFactory = new ERC20Mock__factory(creator); + const mockToken = await mockTokenFactory.deploy(mockName, mockSymbol); + + if (hre.network.name !== HARDHAT_NETWORK_NAME) { + await mockToken.deployed(); + } + + logger.info(`${mockName} deployed to address: ${mockToken.address}`); + + const amountToUse = amount ? amount : hre.ethers.utils.parseUnits(DEFAULT_MOCK_TOKEN_AMOUNT, DEFAULT_MOCK_TOKEN_DECIMALS); + logger.info(`Minting ${amountToUse.toString()} of ${mockSymbol} to ${zeroDAOTokenV1.address}`); + + const balanceBefore = await mockToken.balanceOf(zeroDAOTokenV1.address); + logger.info(`Balance before minting: ${balanceBefore.toString()}`); + + const tx = await mockToken.mint(zeroDAOTokenV1.address, amountToUse); + await tx.wait(hre.network.name === HARDHAT_NETWORK_NAME ? 0 : 3); + + const balanceAfter = await mockToken.balanceOf(zeroDAOTokenV1.address); + logger.info(`Balance after minting: ${balanceAfter.toString()}`); + + // Confirm balance has changed correctly + assert(balanceAfter.eq(balanceBefore.add(amountToUse)), "Balance minting failed"); + + logger.info(`Minting successful: ${amountToUse.toString()} of ${mockSymbol} to ${zeroDAOTokenV1.address}`); + + // Write to file if path specified + if (outputFile) { + const proxyAdmin = await hre.upgrades.admin.getInstance(); + const obj = { + network: hre.network.name, + timestamp: Date.now(), + zeroDAOToken: zeroDAOTokenV1.address, + zeroDAOTokenOwner: newOwnerAddress, + proxyAdmin: proxyAdmin.address, + proxyAdminOwner: await proxyAdmin.owner(), // Should be the same as newOwnerAddress + mockToken: mockToken.address, + mockTokenAmount: amountToUse.toString(), + deployedAt: new Date().toISOString() + }; + + fs.writeFileSync(outputFile, JSON.stringify(obj, undefined, 2)); + logger.info(`Deployment data saved to: ${outputFile}`); + } + + return zeroDAOTokenV1; +} diff --git a/test/helpers/deploy-v2.ts b/test/helpers/deploy-v2.ts new file mode 100644 index 0000000..524ecde --- /dev/null +++ b/test/helpers/deploy-v2.ts @@ -0,0 +1,71 @@ +import * as hre from "hardhat"; +import * as fs from "fs"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import { getLogger } from "../../utilities"; +import { ZeroDAOTokenV2__factory, ZeroDAOTokenV2 } from "../../typechain"; +import { + HARDHAT_NETWORK_NAME, + DEPLOY_V2_LOGGER, + DEPLOYING_V2_MESSAGE +} from "./constants"; + +/** + * Deploy V2 Implementation Contract + * + * This function deploys the ZeroDAOTokenV2 implementation contract that will be used + * for upgrading existing V1 proxy contracts. This deployment creates only the + * implementation contract, not a proxy - the actual upgrade happens separately. + * + * @param deployer - The signer account that will deploy the implementation contract + * @param outputFile - Optional path to save deployment details as JSON + * + * @returns Promise - The deployed ZeroDAOTokenV2 implementation contract + * + * Operations performed: + * 1. Deploy ZeroDAOTokenV2 implementation contract (not as proxy) + * 2. Wait for deployment confirmation on non-hardhat networks + * 3. Save deployment details to output file (if specified) + * + * Usage Notes: + * - This creates the implementation that can be used with OpenZeppelin's upgradeProxy + * - The implementation address from this deployment is used in upgrade transactions + * - Multiple proxies can be upgraded to use the same implementation + * + * @throws Error if deployment fails + */ +export const deployV2 = async ( + deployer: SignerWithAddress, + outputFile?: string, + verbose: boolean = false +): Promise => { + const logger = getLogger(DEPLOY_V2_LOGGER); + logger.state.isEnabled = verbose; + + logger.info(`Network: ${hre.network.name}`); + logger.info(DEPLOYING_V2_MESSAGE); + logger.info(`Deployer: ${deployer.address}`); + + // Deploy ZeroDAOTokenV2 implementation (not as proxy) + const tokenFactory = new ZeroDAOTokenV2__factory(deployer); + const zeroDAOTokenV2 = await tokenFactory.deploy() as ZeroDAOTokenV2; + + if (hre.network.name !== HARDHAT_NETWORK_NAME) { + await zeroDAOTokenV2.deployed(); + } + + logger.info(`ZeroDAOTokenV2 implementation deployed to address: ${zeroDAOTokenV2.address}`); + + // Write to file if path is given + if (outputFile) { + const obj = { + network: hre.network.name, + zeroDAOTokenV2Implementation: zeroDAOTokenV2.address, + deployedAt: new Date().toISOString() + }; + + fs.writeFileSync(outputFile, JSON.stringify(obj, undefined, 2)); + logger.info(`Deployment data saved to: ${outputFile}`); + } + + return zeroDAOTokenV2; +} diff --git a/test/helpers/read-compare-state.ts b/test/helpers/read-compare-state.ts new file mode 100644 index 0000000..6edc318 --- /dev/null +++ b/test/helpers/read-compare-state.ts @@ -0,0 +1,65 @@ +import * as fs from "fs"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import { compareStorageData, ContractStorageData, readContractStorage } from "../../scripts/utils/storage-check"; +import { readState } from "./read-state"; +import { getLogger } from "../../utilities"; +import { + READ_AND_COMPARE_LOGGER, + READING_POST_UPGRADE_STATE_MESSAGE, + COMPARING_STATES_MESSAGE, + STORAGE_COMPARISON_PASSED_MESSAGE, + STORAGE_COMPARISON_FAILED_MESSAGE +} from "./constants"; + +/** + * Read Current Contract State and Compare with Previous State + * + * This function reads the current contract state and compares it with a previously + * captured state to ensure that contract upgrades haven't corrupted existing data. + * It's a critical validation step in the upgrade process. + * + * @param deployer - The signer account used to interact with the contract + * @param priorState - Previously captured contract state data to compare against + * @param outputFile - Optional path to save the current state data as JSON + * + * Operations performed: + * 1. Read current contract state using readState function + * 2. Save current state to output file (if specified) + * 3. Compare current state with prior state using compareStorageData + * 4. Log success or throw error if differences are found + * + * Environment Variables Required: + * - TOKEN_ADDRESS: Address of the contract to read state from + * + * @throws Error if storage comparison fails (indicates data corruption) + * @throws Error if TOKEN_ADDRESS is not set + */ +export const readCompareState = async ( + deployer: SignerWithAddress, + tokenAddress: string, + priorState: ContractStorageData, + outputFile?: string, + verbose: boolean = false +) => { + const logger = getLogger(READ_AND_COMPARE_LOGGER); + logger.state.isEnabled = verbose; + + + logger.info(READING_POST_UPGRADE_STATE_MESSAGE); + + const state = await readState( + deployer, + tokenAddress, + outputFile, + ); + + logger.info(COMPARING_STATES_MESSAGE); + + try { + compareStorageData(priorState, state); + logger.info(STORAGE_COMPARISON_PASSED_MESSAGE); + } catch (error) { + logger.error(STORAGE_COMPARISON_FAILED_MESSAGE, error); + throw error; + } +} diff --git a/test/helpers/read-state.ts b/test/helpers/read-state.ts new file mode 100644 index 0000000..668bf9f --- /dev/null +++ b/test/helpers/read-state.ts @@ -0,0 +1,63 @@ +import * as hre from "hardhat"; +import * as fs from "fs"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import { ZeroDAOToken__factory } from "../../typechain"; +import { ContractStorageData, readContractStorage } from "../../scripts/utils/storage-check"; +import { Contract } from "ethers"; +import { getLogger } from "../../utilities"; +import { READ_STATE_LOGGER } from "./constants"; + +/** + * Read Contract Storage State + * + * This function reads the complete storage state of a ZeroDAOToken contract. + * It's used to capture snapshots of contract state before and after upgrades + * to ensure data integrity is maintained throughout the upgrade process. + * + * @param deployer - The signer account used to interact with the contract + * @param outputFile - Optional path to save the state data as JSON + * + * @returns Promise - The complete contract storage state + * + * Operations performed: + * 1. Get contract address from TOKEN_ADDRESS environment variable + * 2. Create contract instance using ZeroDAOToken factory + * 3. Read complete contract storage using readContractStorage utility + * 4. Save state to output file (if specified) + * + * Environment Variables Required: + * - TOKEN_ADDRESS: Address of the ZeroDAOToken contract to read + * + * Usage: + * - Called before upgrades to capture baseline state + * - Called after upgrades to verify state preservation + * - Used by comparison functions to validate upgrade integrity + * + * @throws Error if TOKEN_ADDRESS is not set for non-hardhat networks + */ +export const readState = async ( + deployer: SignerWithAddress, + tokenAddress: string, + outputFile?: string, + verbose: boolean = false +): Promise => { + const logger = getLogger(READ_STATE_LOGGER); + + logger.state.isEnabled = verbose; + + const tokenFactory = new ZeroDAOToken__factory(deployer); + const token = tokenFactory.attach(tokenAddress); + + logger.info(`Reading storage state for contract: ${token.address}`); + + const state = await readContractStorage( + tokenFactory, + token + ); + + if (outputFile) { + fs.writeFileSync(outputFile, JSON.stringify(state, undefined, 2)); + } + + return state; +} diff --git a/test/zDAOTokenUpgrade.test.ts b/test/zDAOTokenUpgrade.test.ts new file mode 100644 index 0000000..da58264 --- /dev/null +++ b/test/zDAOTokenUpgrade.test.ts @@ -0,0 +1,172 @@ +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import { expect } from "chai"; +import { ethers } from "hardhat"; +import * as hre from "hardhat"; +import { + ZeroDAOToken, + ZeroDAOTokenV2, + ZeroDAOTokenV2__factory, + ERC20Mock__factory, + ERC20Mock, +} from "../typechain"; +import { deployFundTransfer } from "./helpers/deploy-fund-transfer"; +import { deployV2 } from "./helpers/deploy-v2"; +import { readState } from "./helpers/read-state"; +import { readCompareState } from "./helpers/read-compare-state"; +import { ContractStorageData } from "../scripts/utils/storage-check"; +import { + DEFAULT_ZERO_TOKEN_NAME, + DEFAULT_ZERO_TOKEN_SYMBOL, + DEFAULT_TEST_MOCK_TOKEN_NAME, + DEFAULT_TEST_MOCK_TOKEN_SYMBOL, + DEFAULT_TEST_MINT_AMOUNT, + DEFAULT_MOCK_TOKEN_DECIMALS, + DEFAULT_WITHDRAW_AMOUNT, + DEFAULT_TOKEN_MINT_AMOUNT, + DEFAULT_TRANSFER_AMOUNT +} from "./helpers/constants"; + +describe("zDAO Token Upgrade", () => { + let creator: SignerWithAddress; + let user1: SignerWithAddress; + let user2: SignerWithAddress; + + let tokenV1: ZeroDAOToken; + let tokenV2: ZeroDAOTokenV2; + let mockToken: ERC20Mock; + let preUpgradeState: ContractStorageData; + + before(async () => { + [creator, user1, user2] = await hre.ethers.getSigners(); + }); + + describe("Token Upgrade Flow", () => { + + + it("should deploy and fund V1 token with mock ERC20", async () => { + // Call deploy-fund-transfer helper + tokenV1 = await deployFundTransfer(creator, creator.address); + + expect(tokenV1.address).to.not.be.undefined; + expect(await tokenV1.name()).to.equal(DEFAULT_ZERO_TOKEN_NAME); + expect(await tokenV1.symbol()).to.equal(DEFAULT_ZERO_TOKEN_SYMBOL); + + // The deployFundTransfer helper already creates and funds a mock token + // We need to find that mock token address from the deployment + // For now, let's create our own mock token for testing the withdrawERC20 function + const mockTokenFactory = new ERC20Mock__factory(creator); + mockToken = await mockTokenFactory.deploy(DEFAULT_TEST_MOCK_TOKEN_NAME, DEFAULT_TEST_MOCK_TOKEN_SYMBOL); + + // Mint some mock tokens to the V1 contract for testing withdrawERC20 + const mintAmount = ethers.utils.parseUnits(DEFAULT_TEST_MINT_AMOUNT, DEFAULT_MOCK_TOKEN_DECIMALS); + await mockToken.mint(tokenV1.address, mintAmount); + + const balance = await mockToken.balanceOf(tokenV1.address); + expect(balance).to.equal(mintAmount); + }); + + it("should read state before upgrade", async () => { + // Call readState helper to get state before upgrading + preUpgradeState = await readState(creator, tokenV1.address); + + expect(preUpgradeState).to.not.be.undefined; + expect(Array.isArray(preUpgradeState)).to.be.true; + expect(preUpgradeState.length).to.be.greaterThan(0); + }); + + it("should upgrade from V1 to V2", async () => { + // Get the proxy admin and its owner + const proxyAdmin = await hre.upgrades.admin.getInstance(); + const proxyAdminOwner = await proxyAdmin.owner(); + + // Get the signer for the proxy admin owner + let upgrader: SignerWithAddress; + const accounts = await hre.ethers.getSigners(); + + // Find the account that matches the proxy admin owner + upgrader = accounts.find(account => account.address.toLowerCase() === proxyAdminOwner.toLowerCase()) || creator; + + // Upgrade from v1 to v2 using + const tokenV2Factory = new ZeroDAOTokenV2__factory(upgrader); + + tokenV2 = await hre.upgrades.upgradeProxy( + tokenV1.address, + tokenV2Factory + ) as ZeroDAOTokenV2; + + expect(tokenV2.address).to.equal(tokenV1.address); // Same proxy address + }); + + it("should read and compare state after upgrade", async () => { + // Call readStateAndCompare helper to compare states + // This function throws internally if there is a discrepency between + // the pre and post upgrade states. By not throwing, we know it passes. + await readCompareState(creator, tokenV2.address, preUpgradeState); + }); + + it("should test withdrawERC20 function and verify balances", async () => { + + // Connect to the token as the owner + const tokenV2AsOwner = tokenV2.connect(creator); + + // Check initial balance of the contract + const initialBalance = await mockToken.balanceOf(tokenV2.address); + expect(initialBalance).to.be.gt(0); + + // Check initial balance of recipient (creator) + const recipientInitialBalance = await mockToken.balanceOf(creator.address); + + // Define withdrawal amount + const withdrawAmount = ethers.utils.parseUnits(DEFAULT_WITHDRAW_AMOUNT, DEFAULT_MOCK_TOKEN_DECIMALS); + expect(withdrawAmount).to.be.lte(initialBalance); + + // Call withdrawERC20 function as the token owner + const tx = await tokenV2AsOwner.withdrawERC20( + mockToken.address, + creator.address, + withdrawAmount + ); + + // Verify the transaction was successful + await tx.wait(); + + // Check balances after withdrawal + const finalContractBalance = await mockToken.balanceOf(tokenV2.address); + const recipientFinalBalance = await mockToken.balanceOf(creator.address); + + // Verify the contract balance decreased by the withdrawal amount + expect(finalContractBalance).to.equal(initialBalance.sub(withdrawAmount)); + + // Verify the recipient balance increased by the withdrawal amount + expect(recipientFinalBalance).to.equal(recipientInitialBalance.add(withdrawAmount)); + }); + + it("should verify token functionality is preserved after upgrade", async () => { + // Get the token owner (who can call mint) + const tokenOwnerAddress = await tokenV2.owner(); + const accounts = await hre.ethers.getSigners(); + const ownerSigner = accounts.find(account => account.address.toLowerCase() === tokenOwnerAddress.toLowerCase()) || creator; + + // Connect to the token as the owner + const tokenV2AsOwner = tokenV2.connect(ownerSigner); + + // Test that basic V1 functionality still works + const mintAmount = ethers.utils.parseEther(DEFAULT_TOKEN_MINT_AMOUNT); + + await tokenV2AsOwner.mint(user1.address, mintAmount); + const balance = await tokenV2.balanceOf(user1.address); + expect(balance).to.equal(mintAmount); + + // Test transfer functionality + const transferAmount = ethers.utils.parseEther(DEFAULT_TRANSFER_AMOUNT); + const tokenAsUser1 = tokenV2.connect(user1); + await tokenAsUser1.transfer(user2.address, transferAmount); + + const user1Balance = await tokenV2.balanceOf(user1.address); + const user2Balance = await tokenV2.balanceOf(user2.address); + + expect(user1Balance).to.equal(mintAmount.sub(transferAmount)); + expect(user2Balance).to.equal(transferAmount); + }); + }); +}); diff --git a/test/zDaoTokenV2.ts b/test/zDaoTokenV2.ts index b7b416b..f1752a0 100644 --- a/test/zDaoTokenV2.ts +++ b/test/zDaoTokenV2.ts @@ -11,45 +11,37 @@ import { } from "../typechain"; import * as hre from "hardhat"; +import { compareStorageData, ContractStorageData, readContractStorage } from "../scripts/utils/storage-check"; +import { deployFundTransfer } from "./helpers/deploy-fund-transfer"; -describe("zDAOToken => zDAOTokenV2 Upgrade Test", () => { - let accounts: SignerWithAddress[]; +describe("zDAOToken => zDAOTokenV2 Upgrade Test", () => { let creator: SignerWithAddress; + let newOwner: SignerWithAddress; let user1: SignerWithAddress; let user2: SignerWithAddress; - let user3: SignerWithAddress; let zeroDAOToken: ZeroDAOToken; let zeroDAOTokenV2: ZeroDAOTokenV2; let mockToken: ERC20Mock; + let preUpgradeState: ContractStorageData; // Update as needed for testing const decimals = 6; const testTokenAmount = ethers.utils.parseUnits("500000", decimals); before(async () => { - accounts = await ethers.getSigners(); - creator = accounts[0]; - user1 = accounts[1]; - user2 = accounts[2]; - user3 = accounts[3]; + [creator, newOwner, user1, user2] = await ethers.getSigners(); }); describe("ZeroDAOToken to ZeroDAOTokenV2 upgrade test - mint function removal", () => { it("Deploys original ZeroDAOToken contract", async () => { // Deploy the original ZeroDAOToken using the original factory - zeroDAOToken = await hre.upgrades.deployProxy( - new ZeroDAOToken__factory(creator), - ["Test DAO Token", "TDT"] - ) as ZeroDAOToken; - - await zeroDAOToken.deployed(); - - // Verify deployment - expect(await zeroDAOToken.name()).to.eq("Test DAO Token"); - expect(await zeroDAOToken.symbol()).to.eq("TDT"); - expect(await zeroDAOToken.owner()).to.eq(creator.address); + // Use the `deployV1` Helper to that sets up the initial environment the same way it is used in scripts + // - Deploy ZeroDAOToken contract + // - Deploy an ERC20Mock contract, give funds to ZeroDAOToken + // - Transfer ownership to a given address + zeroDAOToken = await deployFundTransfer(creator, newOwner.address); }); it("Deploys mock token and mints balance to deployed zeroDAOToken", async () => { @@ -69,13 +61,18 @@ describe("zDAOToken => zDAOTokenV2 Upgrade Test", () => { const preUpgradeSymbol = await zeroDAOToken.symbol(); const preUpgradeOwner = await zeroDAOToken.owner(); const preUpgradeTotalSupply = await zeroDAOToken.totalSupply(); - const preUpgradeUser1Balance = await zeroDAOToken.balanceOf(user1.address); + const preUpgradeUser1Balance = await zeroDAOToken.balanceOf(newOwner.address); const preUpgradeContractBalance = await mockToken.balanceOf(zeroDAOToken.address); - // Perform the upgrade + preUpgradeState = await readContractStorage( + new ZeroDAOToken__factory(creator), + zeroDAOToken + ); + + // Perform the upgrade - use newOwner since they own the proxy admin zeroDAOTokenV2 = await hre.upgrades.upgradeProxy( zeroDAOToken.address, - new ZeroDAOTokenV2__factory(creator) + new ZeroDAOTokenV2__factory(newOwner) ) as ZeroDAOTokenV2; // Verify the upgrade was successful @@ -86,7 +83,7 @@ describe("zDAOToken => zDAOTokenV2 Upgrade Test", () => { expect(await zeroDAOTokenV2.symbol()).to.eq(preUpgradeSymbol); expect(await zeroDAOTokenV2.owner()).to.eq(preUpgradeOwner); expect(await zeroDAOTokenV2.totalSupply()).to.eq(preUpgradeTotalSupply); - expect(await zeroDAOTokenV2.balanceOf(user1.address)).to.eq(preUpgradeUser1Balance); + expect(await zeroDAOTokenV2.balanceOf(newOwner.address)).to.eq(preUpgradeUser1Balance); expect(await mockToken.balanceOf(zeroDAOToken.address)).to.eq(preUpgradeContractBalance); }); @@ -94,8 +91,8 @@ describe("zDAOToken => zDAOTokenV2 Upgrade Test", () => { const initialRecipientBalance = await mockToken.balanceOf(user1.address); const initialContractBalance = await mockToken.balanceOf(zeroDAOTokenV2.address); - // Call withdrawERC20 function - const tx = await zeroDAOTokenV2.connect(creator).withdrawERC20( + // Call withdrawERC20 function - use newOwner since they own the contract + const tx = await zeroDAOTokenV2.connect(newOwner).withdrawERC20( mockToken.address, user1.address, testTokenAmount @@ -122,8 +119,8 @@ describe("zDAOToken => zDAOTokenV2 Upgrade Test", () => { const initialRecipientBalance = await mockToken.balanceOf(user2.address); - // Call withdrawERC20 with amount 0 (should withdraw all) - const tx = zeroDAOTokenV2.connect(creator).withdrawERC20( + // Call withdrawERC20 with amount 0 (should withdraw all) - use newOwner since they own the contract + const tx = zeroDAOTokenV2.connect(newOwner).withdrawERC20( mockToken.address, user2.address, 0 // This should withdraw total contract balance of `token` given @@ -147,11 +144,11 @@ describe("zDAOToken => zDAOTokenV2 Upgrade Test", () => { const testAmount = ethers.utils.parseUnits("1000", decimals); await mockToken.connect(creator).mint(zeroDAOTokenV2.address, testAmount); - // Try to call withdrawERC20 from non-owner account + // Try to call withdrawERC20 from non-owner account (creator is not the owner, newOwner is) await expect( - zeroDAOTokenV2.connect(user1).withdrawERC20( + zeroDAOTokenV2.connect(creator).withdrawERC20( mockToken.address, - user1.address, + creator.address, testAmount ) ).to.be.revertedWith("Ownable: caller is not the owner"); @@ -160,18 +157,18 @@ describe("zDAOToken => zDAOTokenV2 Upgrade Test", () => { it("Fails when token or recipient address is zero", async () => { const testAmount = ethers.utils.parseUnits("1000", decimals); - // Test zero token address + // Test zero token address - use newOwner since they own the contract await expect( - zeroDAOTokenV2.connect(creator).withdrawERC20( + zeroDAOTokenV2.connect(newOwner).withdrawERC20( ethers.constants.AddressZero, user1.address, testAmount ) ).to.be.revertedWith("zDAOToken: Token address cannot be zero"); - // Test zero recipient address + // Test zero recipient address - use newOwner since they own the contract await expect( - zeroDAOTokenV2.connect(creator).withdrawERC20( + zeroDAOTokenV2.connect(newOwner).withdrawERC20( mockToken.address, ethers.constants.AddressZero, testAmount @@ -180,113 +177,126 @@ describe("zDAOToken => zDAOTokenV2 Upgrade Test", () => { }); it("Fails when contract has insufficient token balance", async () => { - // Test insufficient balance (contract has 1000, trying to withdraw 2000) + // Test insufficient balance (contract has 1000, trying to withdraw 2000) - use newOwner since they own the contract await expect( - zeroDAOTokenV2.connect(creator).withdrawERC20( + zeroDAOTokenV2.connect(newOwner).withdrawERC20( mockToken.address, user1.address, ethers.utils.parseUnits("2000", decimals) ) ).to.be.revertedWith("ERC20: transfer amount exceeds balance"); - }) + }); + + it("Post-upgrade base state should match state pre-upgrade", async () => { + // Verify storage layout is compatible + const postUpgradeState = await readContractStorage( + new ZeroDAOTokenV2__factory(creator), + zeroDAOTokenV2 + ); + + compareStorageData( + preUpgradeState, + postUpgradeState, + ); + }); }); describe("post-upgrade core functionality", () => { it("owner can mint; standard transfer works", async () => { - expect(await zeroDAOTokenV2.owner()).to.eq(creator.address); + expect(await zeroDAOTokenV2.owner()).to.eq(newOwner.address); expect(zeroDAOTokenV2.address).to.eq(zeroDAOToken.address); const mintCreator = ethers.utils.parseEther("1000"); const mintUser1 = ethers.utils.parseEther("100"); await expect( - zeroDAOTokenV2.connect(user1).mint(user1.address, mintUser1) + zeroDAOTokenV2.connect(creator).mint(creator.address, mintUser1) ).to.be.revertedWith("Ownable: caller is not the owner"); const creatorBalBefore = await zeroDAOTokenV2.balanceOf(creator.address); - const user1BalBefore = await zeroDAOTokenV2.balanceOf(user1.address); + const user1BalBefore = await zeroDAOTokenV2.balanceOf(newOwner.address); const supplyBefore = await zeroDAOTokenV2.totalSupply(); - await zeroDAOTokenV2.connect(creator).mint(creator.address, mintCreator); - await zeroDAOTokenV2.connect(creator).mint(user1.address, mintUser1); + await zeroDAOTokenV2.connect(newOwner).mint(creator.address, mintCreator); + await zeroDAOTokenV2.connect(newOwner).mint(newOwner.address, mintUser1); expect(await zeroDAOTokenV2.balanceOf(creator.address)).to.eq(creatorBalBefore.add(mintCreator)); - expect(await zeroDAOTokenV2.balanceOf(user1.address)).to.eq(user1BalBefore.add(mintUser1)); + expect(await zeroDAOTokenV2.balanceOf(newOwner.address)).to.eq(user1BalBefore.add(mintUser1)); expect(await zeroDAOTokenV2.totalSupply()).to.eq(supplyBefore.add(mintCreator).add(mintUser1)); const transferAmt = ethers.utils.parseEther("10"); const creatorBalBeforeTransfer = await zeroDAOTokenV2.balanceOf(creator.address); - const user1BalBeforeTransfer = await zeroDAOTokenV2.balanceOf(user1.address); + const user1BalBeforeTransfer = await zeroDAOTokenV2.balanceOf(newOwner.address); - await zeroDAOTokenV2.connect(creator).transfer(user1.address, transferAmt); + await zeroDAOTokenV2.connect(creator).transfer(newOwner.address, transferAmt); expect(await zeroDAOTokenV2.balanceOf(creator.address)).to.eq(creatorBalBeforeTransfer.sub(transferAmt)); - expect(await zeroDAOTokenV2.balanceOf(user1.address)).to.eq(user1BalBeforeTransfer.add(transferAmt)); + expect(await zeroDAOTokenV2.balanceOf(newOwner.address)).to.eq(user1BalBeforeTransfer.add(transferAmt)); }); it("approve and transferFrom update balances and allowance", async () => { const approveAmt = ethers.utils.parseEther("50"); - await zeroDAOTokenV2.connect(creator).approve(user2.address, approveAmt); - expect(await zeroDAOTokenV2.allowance(creator.address, user2.address)).to.eq(approveAmt); + await zeroDAOTokenV2.connect(creator).approve(user1.address, approveAmt); + expect(await zeroDAOTokenV2.allowance(creator.address, user1.address)).to.eq(approveAmt); const tfAmt = ethers.utils.parseEther("20"); const creatorBalBefore = await zeroDAOTokenV2.balanceOf(creator.address); - const user3BalBefore = await zeroDAOTokenV2.balanceOf(user3.address); - const allowanceBefore = await zeroDAOTokenV2.allowance(creator.address, user2.address); + const user3BalBefore = await zeroDAOTokenV2.balanceOf(user2.address); + const allowanceBefore = await zeroDAOTokenV2.allowance(creator.address, user1.address); - await zeroDAOTokenV2.connect(user2).transferFrom(creator.address, user3.address, tfAmt); + await zeroDAOTokenV2.connect(user1).transferFrom(creator.address, user2.address, tfAmt); expect(await zeroDAOTokenV2.balanceOf(creator.address)).to.eq(creatorBalBefore.sub(tfAmt)); - expect(await zeroDAOTokenV2.balanceOf(user3.address)).to.eq(user3BalBefore.add(tfAmt)); - expect(await zeroDAOTokenV2.allowance(creator.address, user2.address)).to.eq(allowanceBefore.sub(tfAmt)); + expect(await zeroDAOTokenV2.balanceOf(user2.address)).to.eq(user3BalBefore.add(tfAmt)); + expect(await zeroDAOTokenV2.allowance(creator.address, user1.address)).to.eq(allowanceBefore.sub(tfAmt)); }); it("snapshot authorization and events work", async () => { // Owner can snapshot - const nextId = await zeroDAOTokenV2.connect(creator).callStatic.snapshot(); - await expect(zeroDAOTokenV2.connect(creator).snapshot()).to.not.be.reverted; + const nextId = await zeroDAOTokenV2.connect(newOwner).callStatic.snapshot(); + await expect(zeroDAOTokenV2.connect(newOwner).snapshot()).to.not.be.reverted; const expectedNext = nextId.add(1); - expect(await zeroDAOTokenV2.connect(creator).callStatic.snapshot()).to.eq(expectedNext); + expect(await zeroDAOTokenV2.connect(newOwner).callStatic.snapshot()).to.eq(expectedNext); // Non-owner unauthorized by default - await expect(zeroDAOTokenV2.connect(user1).snapshot()).to.be.revertedWith("zDAOToken: Not authorized to snapshot"); + await expect(zeroDAOTokenV2.connect(creator).snapshot()).to.be.revertedWith("zDAOToken: Not authorized to snapshot"); - // Authorize user1 - await expect(zeroDAOTokenV2.connect(creator).authorizeSnapshotter(user1.address)) + // Authorize creator + await expect(zeroDAOTokenV2.connect(newOwner).authorizeSnapshotter(creator.address)) .to.emit(zeroDAOTokenV2, "AuthorizedSnapshotter") - .withArgs(user1.address); + .withArgs(creator.address); - // Now user1 can snapshot - await expect(zeroDAOTokenV2.connect(user1).snapshot()).to.not.be.reverted; + // Now creator can snapshot + await expect(zeroDAOTokenV2.connect(creator).snapshot()).to.not.be.reverted; // Deauthorize and ensure it reverts again - await expect(zeroDAOTokenV2.connect(creator).deauthorizeSnapshotter(user1.address)) + await expect(zeroDAOTokenV2.connect(newOwner).deauthorizeSnapshotter(creator.address)) .to.emit(zeroDAOTokenV2, "DeauthorizedSnapshotter") - .withArgs(user1.address); + .withArgs(creator.address); - await expect(zeroDAOTokenV2.connect(user1).snapshot()).to.be.revertedWith("zDAOToken: Not authorized to snapshot"); + await expect(zeroDAOTokenV2.connect(creator).snapshot()).to.be.revertedWith("zDAOToken: Not authorized to snapshot"); }); it("pause/unpause gates token operations", async () => { - await zeroDAOTokenV2.connect(creator).pause(); + await zeroDAOTokenV2.connect(newOwner).pause(); await expect( - zeroDAOTokenV2.connect(creator).transfer(user1.address, ethers.utils.parseEther("1")) + zeroDAOTokenV2.connect(creator).transfer(newOwner.address, ethers.utils.parseEther("1")) ).to.be.revertedWith("ERC20Pausable: token transfer while paused"); await expect( - zeroDAOTokenV2.connect(creator).mint(creator.address, ethers.utils.parseEther("1")) + zeroDAOTokenV2.connect(newOwner).mint(creator.address, ethers.utils.parseEther("1")) ).to.be.revertedWith("ERC20Pausable: token transfer while paused"); - await zeroDAOTokenV2.connect(creator).unpause(); + await zeroDAOTokenV2.connect(newOwner).unpause(); await expect( - zeroDAOTokenV2.connect(creator).transfer(user1.address, ethers.utils.parseEther("1")) + zeroDAOTokenV2.connect(creator).transfer(newOwner.address, ethers.utils.parseEther("1")) ).to.not.be.reverted; }); it("transferBulk distributes to multiple recipients and validates inputs", async () => { - const recipients = [user2.address, user3.address]; + const recipients = [user1.address, user2.address]; const per = ethers.utils.parseEther("5"); const total = per.mul(recipients.length); @@ -297,14 +307,14 @@ describe("zDAOToken => zDAOTokenV2 Upgrade Test", () => { } const creatorBefore = await zeroDAOTokenV2.balanceOf(creator.address); - const user2Before = await zeroDAOTokenV2.balanceOf(user2.address); - const user3Before = await zeroDAOTokenV2.balanceOf(user3.address); + const user2Before = await zeroDAOTokenV2.balanceOf(user1.address); + const user3Before = await zeroDAOTokenV2.balanceOf(user2.address); await zeroDAOTokenV2.connect(creator).transferBulk(recipients, per); expect(await zeroDAOTokenV2.balanceOf(creator.address)).to.eq(creatorBefore.sub(total)); - expect(await zeroDAOTokenV2.balanceOf(user2.address)).to.eq(user2Before.add(per)); - expect(await zeroDAOTokenV2.balanceOf(user3.address)).to.eq(user3Before.add(per)); + expect(await zeroDAOTokenV2.balanceOf(user1.address)).to.eq(user2Before.add(per)); + expect(await zeroDAOTokenV2.balanceOf(user2.address)).to.eq(user3Before.add(per)); // Zero address recipient should revert await expect( @@ -314,12 +324,12 @@ describe("zDAOToken => zDAOTokenV2 Upgrade Test", () => { // Insufficient balance should revert const tooBig = ethers.utils.parseEther("1000000000"); await expect( - zeroDAOTokenV2.connect(user1).transferBulk([user2.address], tooBig) + zeroDAOTokenV2.connect(newOwner).transferBulk([user1.address], tooBig) ).to.be.revertedWith("ERC20: transfer amount exceeds balance"); }); it("transferFromBulk uses allowance and updates balances and allowance", async () => { - const recipients = [user2.address, user3.address, user1.address]; + const recipients = [user1.address, user2.address, newOwner.address]; const per = ethers.utils.parseEther("2"); const total = per.mul(recipients.length); @@ -330,44 +340,44 @@ describe("zDAOToken => zDAOTokenV2 Upgrade Test", () => { } await expect( - zeroDAOTokenV2.connect(user1).transferFromBulk(creator.address, recipients, per) + zeroDAOTokenV2.connect(newOwner).transferFromBulk(creator.address, recipients, per) ).to.be.revertedWith("ERC20: transfer total exceeds allowance"); - await zeroDAOTokenV2.connect(creator).approve(user1.address, total); + await zeroDAOTokenV2.connect(creator).approve(newOwner.address, total); const creatorBefore = await zeroDAOTokenV2.balanceOf(creator.address); - const allowancesBefore = await zeroDAOTokenV2.allowance(creator.address, user1.address); - const user1Before = await zeroDAOTokenV2.balanceOf(user1.address); - const user2Before = await zeroDAOTokenV2.balanceOf(user2.address); - const user3Before = await zeroDAOTokenV2.balanceOf(user3.address); + const allowancesBefore = await zeroDAOTokenV2.allowance(creator.address, newOwner.address); + const user1Before = await zeroDAOTokenV2.balanceOf(newOwner.address); + const user2Before = await zeroDAOTokenV2.balanceOf(user1.address); + const user3Before = await zeroDAOTokenV2.balanceOf(user2.address); - await zeroDAOTokenV2.connect(user1).transferFromBulk(creator.address, recipients, per); + await zeroDAOTokenV2.connect(newOwner).transferFromBulk(creator.address, recipients, per); expect(await zeroDAOTokenV2.balanceOf(creator.address)).to.eq(creatorBefore.sub(total)); - expect(await zeroDAOTokenV2.balanceOf(user1.address)).to.eq(user1Before.add(per)); - expect(await zeroDAOTokenV2.balanceOf(user2.address)).to.eq(user2Before.add(per)); - expect(await zeroDAOTokenV2.balanceOf(user3.address)).to.eq(user3Before.add(per)); - expect(await zeroDAOTokenV2.allowance(creator.address, user1.address)).to.eq(allowancesBefore.sub(total)); + expect(await zeroDAOTokenV2.balanceOf(newOwner.address)).to.eq(user1Before.add(per)); + expect(await zeroDAOTokenV2.balanceOf(user1.address)).to.eq(user2Before.add(per)); + expect(await zeroDAOTokenV2.balanceOf(user2.address)).to.eq(user3Before.add(per)); + expect(await zeroDAOTokenV2.allowance(creator.address, newOwner.address)).to.eq(allowancesBefore.sub(total)); }); it("owner burn reduces holder balance and total supply", async () => { // Ensure user1 has enough to burn const burnAmt = ethers.utils.parseEther("10"); - const user1Bal = await zeroDAOTokenV2.balanceOf(user1.address); + const user1Bal = await zeroDAOTokenV2.balanceOf(newOwner.address); if (user1Bal.lt(burnAmt)) { - await zeroDAOTokenV2.connect(creator).mint(user1.address, burnAmt.sub(user1Bal)); + await zeroDAOTokenV2.connect(newOwner).mint(newOwner.address, burnAmt.sub(user1Bal)); } - await expect(zeroDAOTokenV2.connect(user1).burn(user1.address, burnAmt)).to.be.revertedWith( + await expect(zeroDAOTokenV2.connect(creator).burn(newOwner.address, burnAmt)).to.be.revertedWith( "Ownable: caller is not the owner" ); - const user1Before = await zeroDAOTokenV2.balanceOf(user1.address); + const user1Before = await zeroDAOTokenV2.balanceOf(newOwner.address); const supplyBefore = await zeroDAOTokenV2.totalSupply(); - await zeroDAOTokenV2.connect(creator).burn(user1.address, burnAmt); + await zeroDAOTokenV2.connect(newOwner).burn(newOwner.address, burnAmt); - expect(await zeroDAOTokenV2.balanceOf(user1.address)).to.eq(user1Before.sub(burnAmt)); + expect(await zeroDAOTokenV2.balanceOf(newOwner.address)).to.eq(user1Before.sub(burnAmt)); expect(await zeroDAOTokenV2.totalSupply()).to.eq(supplyBefore.sub(burnAmt)); }); }); diff --git a/yarn.lock b/yarn.lock index ebbc96b..531e41d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -30,11 +30,25 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@colors/colors@1.6.0", "@colors/colors@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.6.0.tgz#ec6cd237440700bc23ca23087f513c75508958b0" + integrity sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA== + "@cto.af/textdecoder@^0.0.0": version "0.0.0" resolved "https://registry.yarnpkg.com/@cto.af/textdecoder/-/textdecoder-0.0.0.tgz#e1e8d84c936c30a0f4619971f19ca41941af9fdc" integrity sha512-sJpx3F5xcVV/9jNYJQtvimo4Vfld/nD3ph+ZWtQzZ03Zo8rJC7QKQTRcIGS13Rcz80DwFNthCWMrd58vpY4ZAQ== +"@dabh/diagnostics@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a" + integrity sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA== + dependencies: + colorspace "1.1.x" + enabled "2.0.x" + kuler "^2.0.0" + "@ensdomains/ens@^0.4.4": version "0.4.5" resolved "https://registry.yarnpkg.com/@ensdomains/ens/-/ens-0.4.5.tgz#e0aebc005afdc066447c6e22feb4eda89a5edbfc" @@ -134,7 +148,7 @@ "@ethersproject/properties" ">=5.0.0-beta.131" "@ethersproject/strings" ">=5.0.0-beta.130" -"@ethersproject/abi@5.1.0", "@ethersproject/abi@^5.0.1", "@ethersproject/abi@^5.0.2", "@ethersproject/abi@^5.1.0": +"@ethersproject/abi@5.1.0", "@ethersproject/abi@^5.0.1", "@ethersproject/abi@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.1.0.tgz#d582c9f6a8e8192778b5f2c991ce19d7b336b0c5" integrity sha512-N/W9Sbn1/C6Kh2kuHRjf/hX6euMK4+9zdJRBB8sDWmihVntjUAfxbusGZKzDQD8i3szAHhTz8K7XADV5iFNfJw== @@ -149,6 +163,21 @@ "@ethersproject/properties" "^5.1.0" "@ethersproject/strings" "^5.1.0" +"@ethersproject/abi@^5.1.2": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.8.0.tgz#e79bb51940ac35fe6f3262d7fe2cdb25ad5f07d9" + integrity sha512-b9YS/43ObplgyV6SlyQsG53/vkSal0MNA1fskSC4mbnCMi8R+NkcH8K9FPYNESf6jUefBUniE4SOKms0E/KK1Q== + dependencies: + "@ethersproject/address" "^5.8.0" + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/constants" "^5.8.0" + "@ethersproject/hash" "^5.8.0" + "@ethersproject/keccak256" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/strings" "^5.8.0" + "@ethersproject/abstract-provider@5.1.0", "@ethersproject/abstract-provider@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.1.0.tgz#1f24c56cda5524ef4ed3cfc562a01d6b6f8eeb0b" @@ -162,6 +191,19 @@ "@ethersproject/transactions" "^5.1.0" "@ethersproject/web" "^5.1.0" +"@ethersproject/abstract-provider@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.8.0.tgz#7581f9be601afa1d02b95d26b9d9840926a35b0c" + integrity sha512-wC9SFcmh4UK0oKuLJQItoQdzS/qZ51EJegK6EmAWlh+OptpQ/npECOR3QqECd8iGHC0RJb4WKbVdSfif4ammrg== + dependencies: + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/networks" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/transactions" "^5.8.0" + "@ethersproject/web" "^5.8.0" + "@ethersproject/abstract-signer@5.1.0", "@ethersproject/abstract-signer@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.1.0.tgz#744c7a2d0ebe3cc0bc38294d0f53d5ca3f4e49e3" @@ -173,6 +215,17 @@ "@ethersproject/logger" "^5.1.0" "@ethersproject/properties" "^5.1.0" +"@ethersproject/abstract-signer@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.8.0.tgz#8d7417e95e4094c1797a9762e6789c7356db0754" + integrity sha512-N0XhZTswXcmIZQdYtUnd79VJzvEwXQw6PK0dTl9VoYrEBxxCPXqS0Eod7q5TNKRxe1/5WUMuR0u0nqTF/avdCA== + dependencies: + "@ethersproject/abstract-provider" "^5.8.0" + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/address@5.1.0", "@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.0.2", "@ethersproject/address@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.1.0.tgz#3854fd7ebcb6af7597de66f847c3345dae735b58" @@ -184,6 +237,17 @@ "@ethersproject/logger" "^5.1.0" "@ethersproject/rlp" "^5.1.0" +"@ethersproject/address@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.8.0.tgz#3007a2c352eee566ad745dca1dbbebdb50a6a983" + integrity sha512-GhH/abcC46LJwshoN+uBNoKVFPxUuZm6dA257z0vZkKmU1+t8xTn8oK7B9qrj8W2rFRMch4gbJl6PmVxjxBEBA== + dependencies: + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/keccak256" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/rlp" "^5.8.0" + "@ethersproject/base64@5.1.0", "@ethersproject/base64@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.1.0.tgz#27240c174d0a4e13f6eae87416fd876caf7f42b6" @@ -191,6 +255,13 @@ dependencies: "@ethersproject/bytes" "^5.1.0" +"@ethersproject/base64@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.8.0.tgz#61c669c648f6e6aad002c228465d52ac93ee83eb" + integrity sha512-lN0oIwfkYj9LbPx4xEkie6rAMJtySbpOAFXSDVQaBnAzYfB4X2Qr+FXJGxMoc3Bxp2Sm8OwvzMrywxyw0gLjIQ== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/basex@5.1.0", "@ethersproject/basex@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.1.0.tgz#80da2e86f9da0cb5ccd446b337364d791f6a131c" @@ -208,6 +279,15 @@ "@ethersproject/logger" "^5.1.0" bn.js "^4.4.0" +"@ethersproject/bignumber@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.8.0.tgz#c381d178f9eeb370923d389284efa19f69efa5d7" + integrity sha512-ZyaT24bHaSeJon2tGPKIiHszWjD/54Sz8t57Toch475lCLljC6MgPmxk7Gtzz+ddNN5LuHea9qhAe0x3D+uYPA== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + bn.js "^5.2.1" + "@ethersproject/bytes@5.1.0", "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.1.0.tgz#55dfa9c4c21df1b1b538be3accb50fb76d5facfd" @@ -215,6 +295,13 @@ dependencies: "@ethersproject/logger" "^5.1.0" +"@ethersproject/bytes@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.8.0.tgz#9074820e1cac7507a34372cadeb035461463be34" + integrity sha512-vTkeohgJVCPVHu5c25XWaWQOZ4v+DkGoC42/TS2ond+PARCxTJvgTFUNDZovyQ/uAQ4EcpqqowKydcdmRKjg7A== + dependencies: + "@ethersproject/logger" "^5.8.0" + "@ethersproject/constants@5.1.0", "@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.1.0.tgz#4e7da6367ea0e9be87585d8b09f3fccf384b1452" @@ -222,6 +309,13 @@ dependencies: "@ethersproject/bignumber" "^5.1.0" +"@ethersproject/constants@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.8.0.tgz#12f31c2f4317b113a4c19de94e50933648c90704" + integrity sha512-wigX4lrf5Vu+axVTIvNsuL6YrV4O5AXl5ubcURKMEME5TnWBouUh0CDTWxZ2GpnRn1kcCgE7l8O5+VbV9QTTcg== + dependencies: + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/contracts@5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.1.0.tgz#f7c3451f1af77e029005733ccab3419d07d23f6b" @@ -252,6 +346,21 @@ "@ethersproject/properties" "^5.1.0" "@ethersproject/strings" "^5.1.0" +"@ethersproject/hash@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.8.0.tgz#b8893d4629b7f8462a90102572f8cd65a0192b4c" + integrity sha512-ac/lBcTbEWW/VGJij0CNSw/wPcw9bSRgCB0AIBz8CvED/jfvDoV9hsIIiWfvWmFEi8RcXtlNwp2jv6ozWOsooA== + dependencies: + "@ethersproject/abstract-signer" "^5.8.0" + "@ethersproject/address" "^5.8.0" + "@ethersproject/base64" "^5.8.0" + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/keccak256" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/strings" "^5.8.0" + "@ethersproject/hdnode@5.1.0", "@ethersproject/hdnode@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.1.0.tgz#2bf5c4048935136ce83e9242e1bd570afcc0bc83" @@ -297,11 +406,24 @@ "@ethersproject/bytes" "^5.1.0" js-sha3 "0.5.7" +"@ethersproject/keccak256@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.8.0.tgz#d2123a379567faf2d75d2aaea074ffd4df349e6a" + integrity sha512-A1pkKLZSz8pDaQ1ftutZoaN46I6+jvuqugx5KYNeQOPqq+JZ0Txm7dlWesCHB5cndJSu5vP2VKptKf7cksERng== + dependencies: + "@ethersproject/bytes" "^5.8.0" + js-sha3 "0.8.0" + "@ethersproject/logger@5.1.0", "@ethersproject/logger@>=5.0.0-beta.129", "@ethersproject/logger@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.1.0.tgz#4cdeeefac029373349d5818f39c31b82cc6d9bbf" integrity sha512-wtUaD1lBX10HBXjjKV9VHCBnTdUaKQnQ2XSET1ezglqLdPdllNOIlLfhyCRqXm5xwcjExVI5ETokOYfjPtaAlw== +"@ethersproject/logger@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.8.0.tgz#f0232968a4f87d29623a0481690a2732662713d6" + integrity sha512-Qe6knGmY+zPPWTC+wQrpitodgBfH7XoceCGL5bJVejmH+yCS3R8jJm8iiWuvWbG76RUmyEG53oqv6GMVWqunjA== + "@ethersproject/networks@5.1.0", "@ethersproject/networks@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.1.0.tgz#f537290cb05aa6dc5e81e910926c04cfd5814bca" @@ -309,6 +431,13 @@ dependencies: "@ethersproject/logger" "^5.1.0" +"@ethersproject/networks@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.8.0.tgz#8b4517a3139380cba9fb00b63ffad0a979671fde" + integrity sha512-egPJh3aPVAzbHwq8DD7Po53J4OUSsA1MjQp8Vf/OZPav5rlmWUaFLiq8cvQiGK0Z5K6LYzm29+VA/p4RL1FzNg== + dependencies: + "@ethersproject/logger" "^5.8.0" + "@ethersproject/pbkdf2@5.1.0", "@ethersproject/pbkdf2@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.1.0.tgz#6b740a85dc780e879338af74856ca2c0d3b24d19" @@ -324,6 +453,13 @@ dependencies: "@ethersproject/logger" "^5.1.0" +"@ethersproject/properties@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.8.0.tgz#405a8affb6311a49a91dabd96aeeae24f477020e" + integrity sha512-PYuiEoQ+FMaZZNGrStmN7+lWjlsoufGIHdww7454FIaGdbe/p5rnaCXTr5MtBYl3NkeoVhHZuyzChPeGeKIpQw== + dependencies: + "@ethersproject/logger" "^5.8.0" + "@ethersproject/providers@5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.1.0.tgz#27695a02cfafa370428cde1c7a4abab13afb6a35" @@ -365,6 +501,14 @@ "@ethersproject/bytes" "^5.1.0" "@ethersproject/logger" "^5.1.0" +"@ethersproject/rlp@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.8.0.tgz#5a0d49f61bc53e051532a5179472779141451de5" + integrity sha512-LqZgAznqDbiEunaUvykH2JAoXTT9NV0Atqk8rQN9nx9SEgThA/WMx5DnW8a9FOufo//6FZOCHZ+XiClzgbqV9Q== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/sha2@5.1.0", "@ethersproject/sha2@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.1.0.tgz#6ca42d1a26884b3e32ffa943fe6494af7211506c" @@ -385,6 +529,18 @@ bn.js "^4.4.0" elliptic "6.5.4" +"@ethersproject/signing-key@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.8.0.tgz#9797e02c717b68239c6349394ea85febf8893119" + integrity sha512-LrPW2ZxoigFi6U6aVkFN/fa9Yx/+4AtIUe4/HACTvKJdhm0eeb107EVCIQcrLZkxaSIgc/eCrX8Q1GtbH+9n3w== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + bn.js "^5.2.1" + elliptic "6.6.1" + hash.js "1.1.7" + "@ethersproject/solidity@5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.1.0.tgz#095a9c75244edccb26c452c155736d363399b954" @@ -405,6 +561,15 @@ "@ethersproject/constants" "^5.1.0" "@ethersproject/logger" "^5.1.0" +"@ethersproject/strings@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.8.0.tgz#ad79fafbf0bd272d9765603215ac74fd7953908f" + integrity sha512-qWEAk0MAvl0LszjdfnZ2uC8xbR2wdv4cDabyHiBh3Cldq/T8dPH3V4BbBsAYJUeonwD+8afVXld274Ls+Y1xXg== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/constants" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/transactions@5.1.0", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.1.0.tgz#da7fcd7e77e23dcfcca317a945f60bc228c61b36" @@ -420,6 +585,21 @@ "@ethersproject/rlp" "^5.1.0" "@ethersproject/signing-key" "^5.1.0" +"@ethersproject/transactions@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.8.0.tgz#1e518822403abc99def5a043d1c6f6fe0007e46b" + integrity sha512-UglxSDjByHG0TuU17bDfCemZ3AnKO2vYrL5/2n2oXvKzvb7Cz+W9gOWXKARjp2URVwcWlQlPOEQyAviKwT4AHg== + dependencies: + "@ethersproject/address" "^5.8.0" + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/constants" "^5.8.0" + "@ethersproject/keccak256" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/rlp" "^5.8.0" + "@ethersproject/signing-key" "^5.8.0" + "@ethersproject/units@5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.1.0.tgz#b6ab3430ebc22adc3cb4839516496f167bee3ad5" @@ -461,6 +641,17 @@ "@ethersproject/properties" "^5.1.0" "@ethersproject/strings" "^5.1.0" +"@ethersproject/web@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.8.0.tgz#3e54badc0013b7a801463a7008a87988efce8a37" + integrity sha512-j7+Ksi/9KfGviws6Qtf9Q7KCqRhpwrYKQPs+JBA/rKVFF/yaWLHJEH3zfVP2plVu+eys0d2DlFmhoQJayFewcw== + dependencies: + "@ethersproject/base64" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/strings" "^5.8.0" + "@ethersproject/wordlists@5.1.0", "@ethersproject/wordlists@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.1.0.tgz#54eb9ef3a00babbff90ffe124e19c89e07e6aace" @@ -472,6 +663,11 @@ "@ethersproject/properties" "^5.1.0" "@ethersproject/strings" "^5.1.0" +"@fastify/busboy@^2.0.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d" + integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== + "@multiformats/base-x@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" @@ -524,18 +720,21 @@ resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.2.tgz#c472abcba0c5185aaa4ad4070146e95213c68511" integrity sha512-6quxWe8wwS4X5v3Au8q1jOvXYEPkS1Fh+cME5u6AwNdnI4uERvPlVjlgRWzpnb+Rrt1l/cEqiNRH9GlsBMSDQg== -"@nomiclabs/hardhat-etherscan@^2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-2.1.1.tgz#186f3fa652a0ca20fb77aa857cfad2da845d5cbf" - integrity sha512-8TNUFsO5DpAfwNlXMDhcEtFAMOYsVNaQL2vq5vuCD45kUKBgL8H21++zOk231ha9D7LQWBMCIg7A7iPxw6Jwmg== +"@nomiclabs/hardhat-etherscan@3.1.8": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.1.8.tgz#3c12ee90b3733e0775e05111146ef9418d4f5a38" + integrity sha512-v5F6IzQhrsjHh6kQz4uNrym49brK9K5bYCq2zQZ729RYRaifI9hHbtmK+KkIVevfhut7huQFEQ77JLRMAzWYjQ== dependencies: - "@ethersproject/abi" "^5.0.2" + "@ethersproject/abi" "^5.1.2" "@ethersproject/address" "^5.0.2" - cbor "^5.0.2" + cbor "^8.1.0" + chalk "^2.4.2" debug "^4.1.1" fs-extra "^7.0.1" - node-fetch "^2.6.0" + lodash "^4.17.11" semver "^6.3.0" + table "^6.8.0" + undici "^5.14.0" "@nomiclabs/hardhat-waffle@^2.0.1": version "2.0.1" @@ -868,6 +1067,11 @@ resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz#3a84cf5ec3249439015e14049bd3161419bf9eae" integrity sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg== +"@types/triple-beam@^1.3.2": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.5.tgz#74fef9ffbaa198eb8b588be029f38b00299caa2c" + integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw== + "@types/underscore@*": version "1.11.1" resolved "https://registry.yarnpkg.com/@types/underscore/-/underscore-1.11.1.tgz#5b773d04c44897137485615dbef56a2919b9fa5a" @@ -956,6 +1160,13 @@ resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== +"@zero-tech/protocol-utils@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@zero-tech/protocol-utils/-/protocol-utils-0.1.0.tgz#8a4c680837cee0431f54997f8623e8231728fbb7" + integrity sha512-0qL1lCPuTyi1HX8jB++tw7FC9RZHrKkzz44dDHVX+mBtwSN4baC+PjZ4mbakKZElJPab+EBHynRv57gsdzfRHA== + dependencies: + winston "^3.11.0" + "@zxing/text-encoding@0.9.0": version "0.9.0" resolved "https://registry.yarnpkg.com/@zxing/text-encoding/-/text-encoding-0.9.0.tgz#fb50ffabc6c7c66a0c96b4c03e3d9be74864b70b" @@ -1123,6 +1334,11 @@ ansi-regex@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -1302,6 +1518,11 @@ async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.5.0, async@^2.6.1: dependencies: lodash "^4.17.14" +async@^3.2.3: + version "3.2.6" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" + integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -1976,6 +2197,11 @@ bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.1.3: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== +bn.js@^5.2.1: + version "5.2.2" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.2.tgz#82c09f9ebbb17107cd72cb7fd39bd1f9d0aaa566" + integrity sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw== + body-parser@1.19.0, body-parser@^1.16.0: version "1.19.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" @@ -2282,14 +2508,6 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -cbor@^5.0.2: - version "5.2.0" - resolved "https://registry.yarnpkg.com/cbor/-/cbor-5.2.0.tgz#4cca67783ccd6de7b50ab4ed62636712f287a67c" - integrity sha512-5IMhi9e1QU76ppa5/ajP1BmMWZ2FHkhAhjeVKQ/EFCgYSEaeVaoGtL7cxJskf9oCCk+XjzaIdc3IuU/dbA/o2A== - dependencies: - bignumber.js "^9.0.1" - nofilter "^1.0.4" - cbor@^7.0.0: version "7.0.5" resolved "https://registry.yarnpkg.com/cbor/-/cbor-7.0.5.tgz#ed54cdbc19fa7352bb328d00a5393aa7ce45a10f" @@ -2298,6 +2516,13 @@ cbor@^7.0.0: "@cto.af/textdecoder" "^0.0.0" nofilter "^2.0.3" +cbor@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5" + integrity sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg== + dependencies: + nofilter "^3.1.0" + chai@^4.3.3: version "4.3.4" resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.4.tgz#b55e655b31e1eac7099be4c08c21964fce2e6c49" @@ -2494,7 +2719,7 @@ collection-visit@^1.0.0: map-visit "^1.0.0" object-visit "^1.0.0" -color-convert@^1.9.0: +color-convert@^1.9.0, color-convert@^1.9.3: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== @@ -2513,11 +2738,35 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= -color-name@~1.1.4: +color-name@^1.0.0, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-string@^1.6.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^3.1.3: + version "3.2.1" + resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" + integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== + dependencies: + color-convert "^1.9.3" + color-string "^1.6.0" + +colorspace@1.1.x: + version "1.1.4" + resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243" + integrity sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w== + dependencies: + color "^3.1.3" + text-hex "1.0.x" + combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -3052,6 +3301,19 @@ elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +elliptic@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.1.tgz#3b8ffb02670bf69e382c7f65bf524c97c5405c06" + integrity sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -3067,6 +3329,11 @@ emoji-regex@^9.2.1: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== +enabled@2.0.x: + version "2.0.0" + resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" + integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -4089,6 +4356,11 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +fecha@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" + integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== + fetch-ponyfill@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz#ae3ce5f732c645eab87e4ae8793414709b239893" @@ -4236,6 +4508,11 @@ flow-stoplight@^1.0.0: resolved "https://registry.yarnpkg.com/flow-stoplight/-/flow-stoplight-1.0.0.tgz#4a292c5bcff8b39fa6cc0cb1a853d86f27eeff7b" integrity sha1-SiksW8/4s5+mzAyxqFPYbyfu/3s= +fn.name@1.x.x: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" + integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== + follow-redirects@^1.12.1: version "1.13.3" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267" @@ -4845,7 +5122,7 @@ hash.js@1.1.3: inherits "^2.0.3" minimalistic-assert "^1.0.0" -hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== @@ -5246,6 +5523,11 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= +is-arrayish@^0.3.1: + version "0.3.4" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.4.tgz#1ee5553818511915685d33bb13d31bf854e5059d" + integrity sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA== + is-bigint@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.1.tgz#6923051dfcbc764278540b9ce0e6b3213aa5ebc2" @@ -5493,6 +5775,11 @@ is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + is-string@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" @@ -5902,6 +6189,11 @@ klaw@^1.0.0: optionalDependencies: graceful-fs "^4.1.9" +kuler@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" + integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== + lcid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" @@ -6168,6 +6460,18 @@ logdown@^3.3.1: dependencies: chalk "^2.3.0" +logform@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.7.0.tgz#cfca97528ef290f2e125a08396805002b2d060d1" + integrity sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ== + dependencies: + "@colors/colors" "1.6.0" + "@types/triple-beam" "^1.3.2" + fecha "^4.2.0" + ms "^2.1.1" + safe-stable-stringify "^2.3.1" + triple-beam "^1.3.0" + looper@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/looper/-/looper-2.0.0.tgz#66cd0c774af3d4fedac53794f742db56da8f09ec" @@ -6794,11 +7098,6 @@ node-gyp-build@^4.2.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg== -nofilter@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-1.0.4.tgz#78d6f4b6a613e7ced8b015cec534625f7667006e" - integrity sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA== - nofilter@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-2.0.3.tgz#f5460f3cb33147005883e3f5d4476239501fa187" @@ -6806,6 +7105,11 @@ nofilter@^2.0.3: dependencies: "@cto.af/textdecoder" "^0.0.0" +nofilter@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-3.1.0.tgz#c757ba68801d41ff930ba2ec55bab52ca184aa66" + integrity sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g== + nopt@3.x: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" @@ -6969,6 +7273,13 @@ once@1.x, once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" +one-time@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45" + integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g== + dependencies: + fn.name "1.x.x" + onetime@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" @@ -7685,6 +7996,15 @@ readable-stream@^3.0.6, readable-stream@^3.4.0, readable-stream@^3.6.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@^3.6.2: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readable-stream@~1.0.15: version "1.0.34" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" @@ -8037,6 +8357,11 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" +safe-stable-stringify@^2.3.1: + version "2.5.0" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd" + integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA== + "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -8271,6 +8596,13 @@ simple-get@^2.7.0: once "^1.3.1" simple-concat "^1.0.0" +simple-swizzle@^0.2.2: + version "0.2.4" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.4.tgz#a8d11a45a11600d6a1ecdff6363329e3648c3667" + integrity sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw== + dependencies: + is-arrayish "^0.3.1" + slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" @@ -8552,6 +8884,11 @@ stable@^0.1.8: resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== +stack-trace@0.0.x: + version "0.0.10" + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" + integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== + stacktrace-parser@^0.1.10: version "0.1.10" resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" @@ -8627,6 +8964,15 @@ string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string.prototype.trim@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.4.tgz#6014689baf5efaf106ad031a5fa45157666ed1bd" @@ -8699,6 +9045,13 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -8803,6 +9156,17 @@ table@^6.0.4: slice-ansi "^4.0.0" string-width "^4.2.0" +table@^6.8.0: + version "6.9.0" + resolved "https://registry.yarnpkg.com/table/-/table-6.9.0.tgz#50040afa6264141c7566b3b81d4d82c47a8668f5" + integrity sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A== + dependencies: + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + tape@^4.6.3: version "4.13.3" resolved "https://registry.yarnpkg.com/tape/-/tape-4.13.3.tgz#51b3d91c83668c7a45b1a594b607dee0a0b46278" @@ -8850,6 +9214,11 @@ testrpc@0.0.1: resolved "https://registry.yarnpkg.com/testrpc/-/testrpc-0.0.1.tgz#83e2195b1f5873aec7be1af8cbe6dcf39edb7aed" integrity sha512-afH1hO+SQ/VPlmaLUFj2636QMeDvPCeQMc/9RBMW0IfjNe9gFD9Ra3ShqYkB7py0do1ZcCna/9acHyzTJ+GcNA== +text-hex@1.0.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" + integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -8955,6 +9324,11 @@ trim-right@^1.0.1: resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= +triple-beam@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984" + integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== + "true-case-path@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-2.2.1.tgz#c5bf04a5bbec3fd118be4084461b3a27c4d796bf" @@ -9203,6 +9577,13 @@ underscore@1.9.1: resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg== +undici@^5.14.0: + version "5.29.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.29.0.tgz#419595449ae3f2cdcba3580a2e8903399bd1f5a3" + integrity sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg== + dependencies: + "@fastify/busboy" "^2.0.0" + union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" @@ -10011,6 +10392,32 @@ window-size@^0.2.0: resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" integrity sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU= +winston-transport@^4.9.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.9.0.tgz#3bba345de10297654ea6f33519424560003b3bf9" + integrity sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A== + dependencies: + logform "^2.7.0" + readable-stream "^3.6.2" + triple-beam "^1.3.0" + +winston@^3.11.0: + version "3.17.0" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.17.0.tgz#74b8665ce9b4ea7b29d0922cfccf852a08a11423" + integrity sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw== + dependencies: + "@colors/colors" "^1.6.0" + "@dabh/diagnostics" "^2.0.2" + async "^3.2.3" + is-stream "^2.0.0" + logform "^2.7.0" + one-time "^1.0.0" + readable-stream "^3.4.0" + safe-stable-stringify "^2.3.1" + stack-trace "0.0.x" + triple-beam "^1.3.0" + winston-transport "^4.9.0" + word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"