diff --git a/.circleci/config.yml b/.circleci/config.yml index 21f570048..f3efe8dd5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -8,6 +8,7 @@ defaults: &defaults working_directory: ~/repo docker: - image: cimg/node:18.15.0 + - image: mongo:7.0.0-rc5-jammy jobs: test and coverage: diff --git a/.gitignore b/.gitignore index aad343cdd..bc60a8bec 100644 --- a/.gitignore +++ b/.gitignore @@ -10,10 +10,9 @@ typechain-types cache artifacts -# Local DB -db - # hardhat-tenderly plugin deployments *.env +docker +docker*.tgz diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..cd5c9abff --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,10 @@ +version: "2" +services: + mongo: + image: mongo:7.0.0-rc5-jammy + ports: + - "27018:27017" + volumes: + - mongo-data:/data/db +volumes: + mongo-data: \ No newline at end of file diff --git a/hardhat.config.ts b/hardhat.config.ts index 579cc26ce..dcba3fc3a 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -1,121 +1,135 @@ -/* eslint-disable @typescript-eslint/no-var-requires, @typescript-eslint/no-unused-vars */ -require("dotenv").config(); - -import { HardhatUserConfig } from "hardhat/config"; -import * as tenderly from "@tenderly/hardhat-tenderly"; -import "@nomicfoundation/hardhat-toolbox"; -import "@nomiclabs/hardhat-ethers"; -import "@nomicfoundation/hardhat-network-helpers"; -import "@nomicfoundation/hardhat-chai-matchers"; -import "@openzeppelin/hardhat-upgrades"; -import "solidity-coverage"; -import "solidity-docgen"; -import "hardhat-gas-reporter"; - -// This call is needed to initialize Tenderly with Hardhat, -// the automatic verifications, though, don't seem to work, -// needing us to verify explicitly in code, however, -// for Tenderly to work properly with Hardhat this method -// needs to be called. The call below is commented out -// because if we leave it here, solidity-coverage -// does not work properly locally or in CI, so we -// keep it commented out and uncomment when using DevNet -// locally. -// !!! Uncomment this when using Tenderly DevNet !!! -// tenderly.setup({ automaticVerifications: false }); - -const config : HardhatUserConfig = { - solidity: { - compilers: [ - { - version: "0.8.18", - settings: { - optimizer: { - enabled: true, - runs: 200, - }, - }, - }, - { - version: "0.8.3", - settings: { - optimizer: { - enabled: true, - runs: 200, - }, - }, - }, - ], - overrides: { - "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol": { - version: "0.8.9", - settings: { - optimizer: { - enabled: true, - runs: 200, - }, - }, - }, - "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol": { - version: "0.8.9", - settings: { - optimizer: { - enabled: true, - runs: 200, - }, - }, - }, - }, - }, - paths: { - sources: "./contracts", - tests: "./test", - cache: "./cache", - artifacts: "./artifacts", - }, - typechain: { - outDir: "typechain", - }, - mocha: { - timeout: 5000000, - }, - gasReporter: { - enabled: false, - }, - networks: { - mainnet: { - url: "https://mainnet.infura.io/v3/97e75e0bbc6a4419a5dd7fe4a518b917", - gasPrice: 80000000000, - }, - goerli: { - url: "https://goerli.infura.io/v3/77c3d733140f4c12a77699e24cb30c27", - timeout: 10000000, - }, - devnet: { - // Add current URL that you spawned if not using automated spawning - url: `${process.env.DEVNET_RPC_URL}`, - chainId: 1, - }, - }, - etherscan: { - apiKey: `${process.env.ETHERSCAN_API_KEY}`, - }, - tenderly: { - project: `${process.env.TENDERLY_PROJECT_SLUG}`, - username: `${process.env.TENDERLY_ACCOUNT_ID}`, - }, - docgen: { - pages: "files", - templates: "docs/docgen-templates", - outputDir: "docs/contracts", - exclude: [ - "upgrade-test-mocks/", - "upgradeMocks/", - "token/mocks/", - "utils/", - "oz-proxies/", - ], - }, -}; - -export default config; +/* eslint-disable @typescript-eslint/no-var-requires, @typescript-eslint/no-unused-vars */ + +import { mochaGlobalSetup, mochaGlobalTeardown } from "./test/mocha-global"; + +require("dotenv").config(); + +import * as tenderly from "@tenderly/hardhat-tenderly"; +import "@nomicfoundation/hardhat-toolbox"; +import "@nomiclabs/hardhat-ethers"; +import "@nomicfoundation/hardhat-network-helpers"; +import "@nomicfoundation/hardhat-chai-matchers"; +import "@openzeppelin/hardhat-upgrades"; +import "solidity-coverage"; +import "solidity-docgen"; +import "hardhat-gas-reporter"; +import { HardhatUserConfig, subtask } from "hardhat/config"; +import { TASK_TEST_RUN_MOCHA_TESTS } from "hardhat/builtin-tasks/task-names"; + + +subtask(TASK_TEST_RUN_MOCHA_TESTS) + .setAction(async (args, hre, runSuper) => { + await mochaGlobalSetup(); + const testFailures = await runSuper(args); + await mochaGlobalTeardown(); + + return testFailures; + }); + +// This call is needed to initialize Tenderly with Hardhat, +// the automatic verifications, though, don't seem to work, +// needing us to verify explicitly in code, however, +// for Tenderly to work properly with Hardhat this method +// needs to be called. The call below is commented out +// because if we leave it here, solidity-coverage +// does not work properly locally or in CI, so we +// keep it commented out and uncomment when using DevNet +// locally. +// !!! Uncomment this when using Tenderly DevNet !!! +// tenderly.setup({ automaticVerifications: false }); + +const config : HardhatUserConfig = { + solidity: { + compilers: [ + { + version: "0.8.18", + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + { + version: "0.8.3", + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + ], + overrides: { + "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol": { + version: "0.8.9", + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol": { + version: "0.8.9", + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + }, + }, + paths: { + sources: "./contracts", + tests: "./test", + cache: "./cache", + artifacts: "./artifacts", + }, + typechain: { + outDir: "typechain", + }, + mocha: { + timeout: 5000000, + }, + gasReporter: { + enabled: false, + }, + networks: { + mainnet: { + url: "https://mainnet.infura.io/v3/97e75e0bbc6a4419a5dd7fe4a518b917", + gasPrice: 80000000000, + }, + goerli: { + url: "https://goerli.infura.io/v3/77c3d733140f4c12a77699e24cb30c27", + timeout: 10000000, + }, + devnet: { + // Add current URL that you spawned if not using automated spawning + url: `${process.env.DEVNET_RPC_URL}`, + chainId: 1, + }, + }, + etherscan: { + apiKey: `${process.env.ETHERSCAN_API_KEY}`, + }, + tenderly: { + project: `${process.env.TENDERLY_PROJECT_SLUG}`, + username: `${process.env.TENDERLY_ACCOUNT_ID}`, + }, + docgen: { + pages: "files", + templates: "docs/docgen-templates", + outputDir: "docs/contracts", + exclude: [ + "upgrade-test-mocks/", + "upgradeMocks/", + "token/mocks/", + "utils/", + "oz-proxies/", + ], + }, +}; + +export default config; diff --git a/package.json b/package.json index 6f964eb26..2b61d49e6 100644 --- a/package.json +++ b/package.json @@ -17,13 +17,20 @@ "clean": "hardhat clean", "build": "yarn run clean && yarn run compile", "typechain": "hardhat typechain", + "pretest": "yarn mongo:start", "test": "hardhat test", + "test-local": "yarn test", + "posttest": "yarn mongo:stop", "semantic-release": "semantic-release --tag-format='v${version}-dev'", "coverage": "hardhat coverage", "check-coverage": "istanbul check-coverage --statements 90 --branches 87 --functions 89 --lines 90", "devnet": "ts-node src/tenderly/devnet/devnet-execute.ts", "gas-cost": "ts-node src/utils/gas-costs.ts", - "docgen": "hardhat docgen" + "docgen": "hardhat docgen", + "mongo:start": "docker-compose up -d", + "mongo:stop": "docker-compose stop", + "mongo:down": "docker-compose down", + "mongo:drop": "ts-node src/utils/drop-db.ts" }, "pre-commit": [ "lint" @@ -49,15 +56,14 @@ "@types/mocha": "9.1.0", "@types/node": "^18.15.11", "@zero-tech/eslint-config-cpt": "0.2.7", - "@zero-tech/ztoken": "^2.0.0", + "@zero-tech/ztoken": "2.0.0", "chai": "4.2.0", "eslint": "^8.37.0", "ethers": "5.5.1", "hardhat": "2.13.0", "hardhat-gas-reporter": "1.0.8", - "logdown": "3.3.1", "semantic-release": "^21.0.1", - "solhint": "^3.4.1", + "solhint": "4.0.0", "solidity-coverage": "^0.8.2", "solidity-docgen": "^0.6.0-beta.35", "ts-node": "10.9.1", @@ -67,9 +73,7 @@ "dependencies": { "axios": "^1.4.0", "dotenv": "16.0.3", + "mongodb": "^6.1.0", "winston": "^3.11.0" - }, - "resolutions": { - "@solidity-parser/parser": "0.16.0" } } diff --git a/src/deploy/campaign/deploy-campaign.ts b/src/deploy/campaign/deploy-campaign.ts index 03914fe2d..030042966 100644 --- a/src/deploy/campaign/deploy-campaign.ts +++ b/src/deploy/campaign/deploy-campaign.ts @@ -1,15 +1,22 @@ -import { ICampaignArgs, ICampaignState, IDeployCampaignConfig, TLogger, TZNSContractState } from "./types"; +import { + ICampaignArgs, + ICampaignState, + IDeployCampaignConfig, + TLogger, + IMissionInstances, + TZNSContractState, +} from "./types"; import { HardhatDeployer } from "../deployer/hardhat-deployer"; import { TDeployMissionCtor } from "../missions/types"; import { BaseDeployMission } from "../missions/base-deploy-mission"; import { Contract } from "ethers"; -import { BaseStorageAdapter } from "../storage/base-storage-adapter"; +import { MongoDBAdapter } from "../db/mongo-adapter/mongo-adapter"; export class DeployCampaign { state : ICampaignState; deployer : HardhatDeployer; - dbAdapter : BaseStorageAdapter; + dbAdapter : MongoDBAdapter; logger : TLogger; config : IDeployCampaignConfig; version : string; @@ -41,7 +48,7 @@ export class DeployCampaign { } : ICampaignArgs) { this.state = { missions, - instances: [], + instances: {}, contracts: {} as TZNSContractState, }; this.deployer = deployer; @@ -53,12 +60,18 @@ export class DeployCampaign { const campaignProxy = new Proxy(this, DeployCampaign.indexedHandler); // instantiate all missions - this.state.instances = missions.map( - (mission : TDeployMissionCtor) => new mission({ - campaign: campaignProxy, - logger, - config, - }) + this.state.instances = missions.reduce( + (acc : IMissionInstances, mission : TDeployMissionCtor) => { + const instance = new mission({ + campaign: campaignProxy, + logger, + config, + }); + + acc[instance.instanceName] = instance; + return acc; + }, + {} ); this.logger.debug("Deploy Campaign initialized."); @@ -69,13 +82,13 @@ export class DeployCampaign { async execute () { this.logger.debug("Deploy Campaign execution started."); - await this.state.instances.reduce( + await Object.values(this.state.instances).reduce( async ( acc : Promise, - mission : BaseDeployMission, + missionInstance : BaseDeployMission, ) : Promise => { await acc; - return mission.execute(); + return missionInstance.execute(); }, Promise.resolve() ); diff --git a/src/deploy/campaign/types.ts b/src/deploy/campaign/types.ts index 4be03b0bc..e894d5358 100644 --- a/src/deploy/campaign/types.ts +++ b/src/deploy/campaign/types.ts @@ -6,6 +6,7 @@ import { BaseStorageAdapter } from "../storage/base-storage-adapter"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { IZNSContracts } from "../../../test/helpers/types"; import { Logger as WinstonLogger } from "winston"; +import { MongoDBAdapter } from "../db/mongo-adapter/mongo-adapter"; export interface IDeployCampaignConfig { @@ -19,9 +20,9 @@ export interface IDeployCampaignConfig { defaultRoyaltyFraction : BigNumber; }; rootPriceConfig : ICurvePriceConfig; - stakingTokenAddress ?: string; zeroVaultAddress : string; - // TODO dep: add more props when opts expanded + mockMeowToken : boolean; + stakingTokenAddress : string; } export type TLogger = WinstonLogger | Console; @@ -30,16 +31,20 @@ export interface IContractState { [key : string] : Contract; } +export interface IMissionInstances { + [key : string] : BaseDeployMission; +} + export interface ICampaignState { missions : Array; - instances : Array; + instances : IMissionInstances; contracts : TZNSContractState; } export interface ICampaignArgs { missions : Array; deployer : HardhatDeployer; - dbAdapter : BaseStorageAdapter; + dbAdapter : MongoDBAdapter; logger : TLogger; config : IDeployCampaignConfig; } diff --git a/src/deploy/constants.ts b/src/deploy/constants.ts index d5a996078..0d574108b 100644 --- a/src/deploy/constants.ts +++ b/src/deploy/constants.ts @@ -1,60 +1,13 @@ import { ethers } from "ethers"; +import { IProxyKinds } from "./missions/types"; -export const ProxyKinds = { + +export const ProxyKinds : IProxyKinds = { uups: "uups", transparent: "transparent", beacon: "beacon", }; -export const erc1967ProxyName = "ERC1967Proxy"; - -export const znsNames = { - accessController: { - contract: "ZNSAccessController", - instance: "accessController", - }, - registry: { - contract: "ZNSRegistry", - instance: "registry", - }, - domainToken: { - contract: "ZNSDomainToken", - instance: "domainToken", - }, - meowToken: { - // TODO dep: figure out the proper naming between the Mock and the prod token! - contract: "MeowTokenMock", - instance: "meowToken", - }, - addressResolver: { - contract: "ZNSAddressResolver", - instance: "addressResolver", - }, - curvePricer: { - contract: "ZNSCurvePricer", - instance: "curvePricer", - }, - fixedPricer: { - contract: "ZNSFixedPricer", - instance: "fixedPricer", - }, - treasury: { - contract: "ZNSTreasury", - instance: "treasury", - }, - rootRegistrar: { - contract: "ZNSRootRegistrar", - instance: "rootRegistrar", - }, - subRegistrar: { - contract: "ZNSSubRegistrar", - instance: "subRegistrar", - }, - erc1967Proxy: { - contract: erc1967ProxyName, - instance: "erc1967Proxy", - }, -}; // role names export const GOVERNOR_ROLE = ethers.utils.solidityKeccak256( ["string"], diff --git a/src/deploy/db/mongo-adapter/constants.ts b/src/deploy/db/mongo-adapter/constants.ts new file mode 100644 index 000000000..3fb076194 --- /dev/null +++ b/src/deploy/db/mongo-adapter/constants.ts @@ -0,0 +1,15 @@ + +export const COLL_NAMES = { + contracts: "contracts", + versions: "versions", +}; + +export const VERSION_TYPES = { + temp: "TEMP", + deployed: "DEPLOYED", + archived: "ARCHIVED", +}; + +// TODO dep: move these to default ENV config +export const mongoURILocal = "mongodb://localhost:27018"; +export const mongoDbName = "zns-campaign"; diff --git a/src/deploy/db/mongo-adapter/get-adapter.ts b/src/deploy/db/mongo-adapter/get-adapter.ts new file mode 100644 index 000000000..e3b3394a6 --- /dev/null +++ b/src/deploy/db/mongo-adapter/get-adapter.ts @@ -0,0 +1,64 @@ +import { MongoDBAdapter } from "./mongo-adapter"; +import { getLogger } from "../../logger/create-logger"; +import { mongoDbName, mongoURILocal } from "./constants"; + +let mongoAdapter : MongoDBAdapter | null = null; + + +export const getMongoAdapter = async () : Promise => { + const checkParams = { + // TODO dep: fix type assertion error here + dbUri: process.env.MONGO_DB_URI!, + dbName: process.env.MONGO_DB_NAME!, + }; + + const logger = getLogger(); + + const params = { + logger, + clientOpts: !!process.env.MONGO_DB_CLIENT_OPTS + ? JSON.parse(process.env.MONGO_DB_CLIENT_OPTS) + : undefined, + // TODO dep: add better way to set version ENV var is not the best ! + version: process.env.MONGO_DB_VERSION, + }; + + if (!checkParams.dbUri && !checkParams.dbName) { + logger.info( + "`MONGO_DB_URI` and `MONGO_DB_NAME` have not been provided by the ENV. Proceeding to use local defaults." + ); + checkParams.dbUri = mongoURILocal; + checkParams.dbName = mongoDbName; + } + + let createNew = false; + if (mongoAdapter) { + Object.values(checkParams).forEach( + ([key, value]) => { + if (key === "version") key = "curVersion"; + + // if the existing adapter was created with different options than the currently needed one + // we create a new one and overwrite + if (JSON.stringify(mongoAdapter?.[key]) !== JSON.stringify(value)) { + createNew = true; + return; + } + } + ); + } else { + createNew = true; + } + + if (createNew) { + logger.debug("Creating new MongoDBAdapter instance"); + mongoAdapter = new MongoDBAdapter({ + ...checkParams, + ...params, + }); + await mongoAdapter.initialize(params.version); + } else { + logger.debug("Returning existing MongoDBAdapter instance"); + } + + return mongoAdapter as MongoDBAdapter; +}; diff --git a/src/deploy/db/mongo-adapter/mongo-adapter.ts b/src/deploy/db/mongo-adapter/mongo-adapter.ts new file mode 100644 index 000000000..3e265b916 --- /dev/null +++ b/src/deploy/db/mongo-adapter/mongo-adapter.ts @@ -0,0 +1,244 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { TLogger } from "../../campaign/types"; +import { Collection, Db, MongoClient, MongoClientOptions } from "mongodb"; +import { IDBVersion, IMongoDBAdapterArgs } from "./types"; +import { COLL_NAMES, VERSION_TYPES } from "./constants"; +import { IContractDbData } from "../types"; +// eslint-disable-next-line @typescript-eslint/no-var-requires +require("dotenv").config(); + + +export class MongoDBAdapter { + logger : TLogger; + client : MongoClient; + dbUri : string; + dbName : string; + db : Db; + curVersion : string; + clientOpts ?: MongoClientOptions; + + // Collection pointers + contracts : Collection; + versions : Collection; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [name : string | symbol] : any; + + constructor ({ + logger, + dbUri, + dbName, + clientOpts, + } : IMongoDBAdapterArgs) { + // TODO dep: add a way to get these from ENV + this.logger = logger; + this.client = new MongoClient(dbUri, clientOpts); + this.clientOpts = clientOpts; + this.dbUri = dbUri; + this.dbName = dbName; + this.db = {} as Db; + this.contracts = {} as Collection; + this.versions = {} as Collection; + this.curVersion = "0"; + } + + // call this to actually start the adapter + async initialize (version ?: string) { + try { + await this.client.connect(); + this.db = this.client.db(this.dbName); + + this.logger.info({ + message: `MongoDB connected at ${this.dbUri}`, + }); + } catch (e) { + this.logger.error({ + message: `MongoDB connection failed at ${this.dbUri}`, + error: e, + }); + throw e; + } + + this.contracts = this.db.collection(COLL_NAMES.contracts); + this.versions = this.db.collection(COLL_NAMES.versions); + + // TODO dep: can we use this prop in all the contract getters to not + // have to determine them dynamically every time ?? is this reliable enough? + this.curVersion = await this.configureVersioning(version); + + return this.db; + } + + async close (forceClose = false) { + try { + await this.client.close(forceClose); + this.logger.info(`MongoDB connection closed at ${this.dbUri}`); + } catch (e) { + this.logger.error({ + message: `MongoDB connection failed to close at ${this.dbUri}`, + error: e, + }); + throw e; + } + } + + // Contract methods + async getContract (contractName : string, version ?: string) { + if (!version) version = await this.getCheckLatestVersion(); + + return this.contracts.findOne({ + name: contractName, + version, + }); + } + + async writeContract (contractName : string, data : IContractDbData, version ?: string) { + if (!version) version = await this.getCheckLatestVersion(); + + return this.contracts.insertOne({ + ...data, + version, + }); + } + + async dropDB () { + await this.db.dropDatabase(); + this.logger.info("Database dropped successfully."); + } + + // Versioning methods + // TODO dep: add logging to all versioning stages and methods !! + async configureVersioning (version ?: string) { + // TODO dep: add archiving logic once determined on how to handle it + const tempV = await this.getTempVersion(); + const deployedV = await this.getDeployedVersion(); + + let finalVersion : string; + if (version) { + finalVersion = version; + + if (version !== deployedV || !deployedV) { + // we should only have a single TEMP version at any given time + if (version !== tempV && tempV) { + await this.clearDBForVersion(tempV); + } + + await this.createUpdateTempVersion(finalVersion); + } + } else { + if (!tempV && !deployedV) { + this.logger.info("No version provided to MongoDBAdapter, using current timestamp as version"); + finalVersion = Date.now().toString(); + await this.createUpdateTempVersion(finalVersion); + } else if (!deployedV) { + finalVersion = tempV as string; + } else { + finalVersion = deployedV; + } + } + + return finalVersion; + } + + async finalizeDeployedVersion (version ?: string) { + const finalV = version || await this.getTempVersion(); + + if (!finalV) return; + + const deployedV = await this.getDeployedVersion(); + if (finalV !== deployedV) { + // archive the current DEPLOYED version + await this.versions.updateOne( + { + type: VERSION_TYPES.deployed, + }, + { + $set: { + type: VERSION_TYPES.archived, + }, + }); + + // create new DEPLOYED version + await this.versions.insertOne({ + type: VERSION_TYPES.deployed, + version: finalV, + }); + + // now remove the TEMP version + await this.versions.deleteOne({ + type: VERSION_TYPES.temp, + version: finalV, + }); + } + + // archive the current TEMP version if any + return this.versions.updateOne( + { + type: VERSION_TYPES.temp, + }, + { + $set: { + type: VERSION_TYPES.archived, + }, + }); + } + + async getCheckLatestVersion () { + const v = await this.getLatestVersion(); + + if (!v) throw new Error("No version found in DB!"); + + return v; + } + + async getTempVersion () : Promise { + const v = await this.versions.findOne({ + type: VERSION_TYPES.temp, + }); + + if (!v) return null; + + return v.version; + } + + async getDeployedVersion () : Promise { + const v = await this.versions.findOne({ + type: VERSION_TYPES.deployed, + }); + + if (!v) return null; + + return v.version; + } + + async getLatestVersion () : Promise { + const v = await this.getTempVersion(); + + if (v) return v; + + return this.getDeployedVersion(); + } + + async createUpdateTempVersion (version : string) { + return this.versions.updateOne({ + type: VERSION_TYPES.temp, + }, { + $set: { + version, + }, + }, { + upsert: true, + }); + } + + async clearDBForVersion (version : string) { + // TODO dep: add more collections here when added + await this.contracts.deleteMany({ + version, + }); + + return this.versions.deleteMany({ + version, + }); + } +} diff --git a/src/deploy/db/mongo-adapter/types.ts b/src/deploy/db/mongo-adapter/types.ts new file mode 100644 index 000000000..a8184e824 --- /dev/null +++ b/src/deploy/db/mongo-adapter/types.ts @@ -0,0 +1,15 @@ +import { TLogger } from "../../campaign/types"; + + +export interface IMongoDBAdapterArgs { + logger : TLogger; + dbUri : string; + dbName : string; + version ?: string; + clientOpts ?: Record; +} + +export interface IDBVersion { + version : string; + type : string; +} diff --git a/src/deploy/db/service/mongo-service.ts b/src/deploy/db/service/mongo-service.ts new file mode 100644 index 000000000..0d8ca150f --- /dev/null +++ b/src/deploy/db/service/mongo-service.ts @@ -0,0 +1,40 @@ +import { exec } from "child_process"; +import { getLogger } from "../../logger/create-logger"; +import { promisify } from "util"; + +const execAsync = promisify(exec); + +export const startMongo = async () => { + const logger = getLogger(); + + try { + exec("npm run mongo:start"); + logger.info("MongoDB started"); + } catch (e) { + logger.error({ + message: "Failed to start MongoDB Docker", + error: e, + }); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + throw new Error(e.message); + } +}; + +export const stopMongo = async () => { + const logger = getLogger(); + + try { + // TODO db: possibly change this to delete the container + await execAsync("npm run mongo:stop"); + logger.info("MongoDB stopped"); + } catch (e) { + logger.error({ + message: "Failed to stop MongoDB Docker", + error: e, + }); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + throw new Error(e.message); + } +}; diff --git a/src/deploy/db/types.ts b/src/deploy/db/types.ts new file mode 100644 index 000000000..547896642 --- /dev/null +++ b/src/deploy/db/types.ts @@ -0,0 +1,13 @@ +import { WithId } from "mongodb"; + + +export interface IContractDbData { + name : string; + address : string; + abi : string; + bytecode : string; + implementation : string | null; + version : string; +} + +export type TContractDBDoc = WithId & IContractDbData; diff --git a/src/deploy/deployer/hardhat-deployer.ts b/src/deploy/deployer/hardhat-deployer.ts index 1b61ae3b6..5bd022f18 100644 --- a/src/deploy/deployer/hardhat-deployer.ts +++ b/src/deploy/deployer/hardhat-deployer.ts @@ -9,6 +9,10 @@ export class HardhatDeployer { this.hre = hre; } + async getFactory (contractName : string) { + return this.hre.ethers.getContractFactory(contractName); + } + async deployProxy ({ contractName, args, @@ -44,5 +48,8 @@ export class HardhatDeployer { async getProxyImplAddress (proxyContract : string) { return this.hre.upgrades.erc1967.getImplementationAddress(proxyContract); } -} + async getBytecodeFromChain (address : string) { + return this.hre.ethers.provider.getCode(address); + } +} diff --git a/src/deploy/logger/create-logger.ts b/src/deploy/logger/create-logger.ts index bafb17317..5f5bf5979 100644 --- a/src/deploy/logger/create-logger.ts +++ b/src/deploy/logger/create-logger.ts @@ -1,8 +1,11 @@ import winston from "winston"; +import { TLogger } from "../campaign/types"; + +let logger : TLogger | null = null; // TODO dep: refine this function and configurability of this logger -export const createLogger = (logLevel ?: string) => winston.createLogger({ +export const createLogger = (logLevel ?: string, silent ?: boolean) => winston.createLogger({ level: logLevel, format: winston.format.combine( // TODO dep: adjust the format to what we need @@ -16,4 +19,17 @@ export const createLogger = (logLevel ?: string) => winston.createLogger({ ], // TODO dep: make sure we need this to be set! exitOnError: false, + silent, }); + +// TODO dep: add more ENV vars here so we don't have to pass anything +export const getLogger = () : TLogger => { + if (logger) return logger; + + logger = createLogger( + process.env.LOG_LEVEL || "debug", + process.env.SILENT_LOGGER === "true" + ); + + return logger; +}; diff --git a/src/deploy/missions/base-deploy-mission.ts b/src/deploy/missions/base-deploy-mission.ts index 1d342544a..3ba3e9a68 100644 --- a/src/deploy/missions/base-deploy-mission.ts +++ b/src/deploy/missions/base-deploy-mission.ts @@ -1,12 +1,12 @@ import { Contract } from "ethers"; import { TDeployArgs, - IContractDbObject, IProxyData, IDeployMissionArgs, } from "./types"; import { DeployCampaign } from "../campaign/deploy-campaign"; import { IDeployCampaignConfig, TLogger } from "../campaign/types"; +import { IContractDbData } from "../db/types"; // TODO dep: @@ -71,14 +71,16 @@ export class BaseDeployMission { return this.campaign.deployer.getContractArtifact(this.contractName); } - buildDbObject (hhContract : Contract, implAddress : string | null) : IContractDbObject { + buildDbObject (hhContract : Contract, implAddress : string | null) : IContractDbData { const { abi, bytecode } = this.getArtifact(); return { + name: this.contractName, address: hhContract.address, abi: JSON.stringify(abi), bytecode, - args: JSON.stringify(this.deployArgs()), implementation: implAddress, + // TODO dep: this might not be needed here since MongoAdapter will add it + // upon writing to DB version: this.campaign.version, }; } @@ -92,7 +94,7 @@ export class BaseDeployMission { contract = await this.campaign.deployer.deployProxy({ contractName: this.contractName, args: deployArgs, - kind: this.proxyData.proxyKind, + kind: this.proxyData.kind, }); } else { contract = await this.campaign.deployer.deployContract(this.contractName, deployArgs); @@ -106,7 +108,7 @@ export class BaseDeployMission { } async needsPostDeploy () { - return Promise.resolve(true); + return Promise.resolve(false); } async postDeploy () { diff --git a/src/deploy/missions/contracts/access-controller.ts b/src/deploy/missions/contracts/access-controller.ts index 39ba1d8e3..a2b95c65a 100644 --- a/src/deploy/missions/contracts/access-controller.ts +++ b/src/deploy/missions/contracts/access-controller.ts @@ -1,5 +1,6 @@ import { BaseDeployMission } from "../base-deploy-mission"; -import { znsNames } from "../../constants"; + +import { znsNames } from "./names"; export class ZNSAccessControllerDM extends BaseDeployMission { diff --git a/src/deploy/missions/contracts/address-resolver.ts b/src/deploy/missions/contracts/address-resolver.ts index d6577301e..ce65e0ed3 100644 --- a/src/deploy/missions/contracts/address-resolver.ts +++ b/src/deploy/missions/contracts/address-resolver.ts @@ -1,6 +1,7 @@ import { BaseDeployMission } from "../base-deploy-mission"; -import { ProxyKinds, ResolverTypes, znsNames } from "../../constants"; +import { ProxyKinds, ResolverTypes } from "../../constants"; import { TDeployArgs } from "../types"; +import { znsNames } from "./names"; export class ZNSAddressResolverDM extends BaseDeployMission { diff --git a/src/deploy/missions/contracts/curve-pricer.ts b/src/deploy/missions/contracts/curve-pricer.ts index a1f4b3355..385fd025b 100644 --- a/src/deploy/missions/contracts/curve-pricer.ts +++ b/src/deploy/missions/contracts/curve-pricer.ts @@ -1,6 +1,7 @@ import { BaseDeployMission } from "../base-deploy-mission"; -import { ProxyKinds, znsNames } from "../../constants"; +import { ProxyKinds } from "../../constants"; import { TDeployArgs } from "../types"; +import { znsNames } from "./names"; export class ZNSCurvePricerDM extends BaseDeployMission { diff --git a/src/deploy/missions/contracts/domain-token.ts b/src/deploy/missions/contracts/domain-token.ts index 86ff5eb33..f261cdbe7 100644 --- a/src/deploy/missions/contracts/domain-token.ts +++ b/src/deploy/missions/contracts/domain-token.ts @@ -1,6 +1,7 @@ import { BaseDeployMission } from "../base-deploy-mission"; -import { ProxyKinds, znsNames } from "../../constants"; +import { ProxyKinds } from "../../constants"; import { TDeployArgs } from "../types"; +import { znsNames } from "./names"; export class ZNSDomainTokenDM extends BaseDeployMission { proxyData = { diff --git a/src/deploy/missions/contracts/fixed-pricer.ts b/src/deploy/missions/contracts/fixed-pricer.ts index d41d9d4b6..57e00cc98 100644 --- a/src/deploy/missions/contracts/fixed-pricer.ts +++ b/src/deploy/missions/contracts/fixed-pricer.ts @@ -1,6 +1,7 @@ -import { ProxyKinds, znsNames } from "../../constants"; +import { ProxyKinds } from "../../constants"; import { TDeployArgs } from "../types"; import { BaseDeployMission } from "../base-deploy-mission"; +import { znsNames } from "./names"; export class ZNSFixedPricerDM extends BaseDeployMission { diff --git a/src/deploy/missions/contracts/index.ts b/src/deploy/missions/contracts/index.ts index 7f77939ab..9d86beae1 100644 --- a/src/deploy/missions/contracts/index.ts +++ b/src/deploy/missions/contracts/index.ts @@ -5,6 +5,6 @@ export * from "./domain-token"; export * from "./treasury"; export * from "./curve-pricer"; export * from "./access-controller"; -export * from "./mocks/meow-token-mock"; +export * from "./meow-token/meow-token"; export * from "./fixed-pricer"; export * from "./sub-registrar"; diff --git a/src/deploy/missions/contracts/meow-token/mainnet-data.ts b/src/deploy/missions/contracts/meow-token/mainnet-data.ts new file mode 100644 index 000000000..9e8569ed1 --- /dev/null +++ b/src/deploy/missions/contracts/meow-token/mainnet-data.ts @@ -0,0 +1,513 @@ + +export const MeowMainnet = { + address: "0x0eC78ED49C2D27b315D462d43B5BAB94d2C79bf8", + implementation: "0xcDb3C5e9b4760f93437aBaD33dBEC9a2cE3704f6", + abi: [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address", + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address", + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256", + }, + ], + "name": "Approval", + "type": "event", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8", + }, + ], + "name": "Initialized", + "type": "event", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address", + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address", + }, + ], + "name": "OwnershipTransferred", + "type": "event", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address", + }, + ], + "name": "Paused", + "type": "event", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "id", + "type": "uint256", + }, + ], + "name": "Snapshot", + "type": "event", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256", + }, + ], + "name": "Transfer", + "type": "event", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address", + }, + ], + "name": "Unpaused", + "type": "event", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address", + }, + { + "internalType": "address", + "name": "spender", + "type": "address", + }, + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address", + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256", + }, + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool", + }, + ], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address", + }, + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address", + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256", + }, + ], + "name": "balanceOfAt", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address", + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256", + }, + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool", + }, + ], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address", + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256", + }, + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool", + }, + ], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name_", + "type": "string", + }, + { + "internalType": "string", + "name": "symbol_", + "type": "string", + }, + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256", + }, + ], + "name": "totalSupplyAt", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256", + }, + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool", + }, + ], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "recipients", + "type": "address[]", + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256", + }, + ], + "name": "transferBulk", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool", + }, + ], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256", + }, + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool", + }, + ], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address", + }, + { + "internalType": "address[]", + "name": "recipients", + "type": "address[]", + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256", + }, + ], + "name": "transferFromBulk", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool", + }, + ], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address", + }, + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], +}; diff --git a/src/deploy/missions/contracts/meow-token/meow-token.ts b/src/deploy/missions/contracts/meow-token/meow-token.ts new file mode 100644 index 000000000..46df1a664 --- /dev/null +++ b/src/deploy/missions/contracts/meow-token/meow-token.ts @@ -0,0 +1,86 @@ +import { BaseDeployMission } from "../../base-deploy-mission"; +import { ProxyKinds } from "../../../constants"; +import { IDeployMissionArgs, TDeployArgs } from "../../types"; +import { ethers } from "ethers"; +import { znsNames } from "../names"; + + +export const meowTokenName = "MEOW"; +export const meowTokenSymbol = "MEOW"; + + +export class MeowTokenDM extends BaseDeployMission { + proxyData = { + isProxy: true, + kind: ProxyKinds.transparent, + }; + + contractName = znsNames.meowToken.contract; + instanceName = znsNames.meowToken.instance; + + constructor (args : IDeployMissionArgs) { + super(args); + + if (this.config.mockMeowToken) { + this.contractName = znsNames.meowToken.contractMock; + } else { + this.contractName = znsNames.meowToken.contract; + } + } + + async deploy () { + if (!this.config.mockMeowToken) { + this.logger.info("Using MEOW token from Mainnet"); + + // TODO dep: add proper bytecode comparison here and throw if different! + // const bytecodeFromChain = await this.campaign.deployer.getBytecodeFromChain(this.config.stakingTokenAddress); + + // const { + // bytecode, + // } = this.getArtifact(); + + // if (!compareBytecodeStrict(bytecode, bytecodeFromChain)) { + // this.logger.error("MEOW token bytecode compiled in this module differs from Mainnet"); + // throw new Error( + // "MEOW token bytecode compiled in this module differs from Mainnet" + // ); + // } + + this.logger.debug(`Writing ${this.contractName} to DB...`); + + const factory = await this.campaign.deployer.getFactory(this.contractName); + const contract = factory.attach(this.config.stakingTokenAddress); + + await this.saveToDB(contract); + + this.campaign.updateStateContract(this.instanceName, this.contractName, contract); + + this.logger.info(`Successfully created ${this.contractName} contract from Mainnet data at ${contract.address}`); + } else { + await super.deploy(); + } + } + + deployArgs () : TDeployArgs { + return [meowTokenName, meowTokenSymbol]; + } + + async needsPostDeploy () { + return this.config.mockMeowToken; + } + + async postDeploy () { + const { + meowToken, + config: { + deployAdmin, + }, + } = this.campaign; + + // Mint 100,000 MEOW to the deployer + await meowToken.connect(deployAdmin).mint( + deployAdmin.address, + ethers.utils.parseEther("100000") + ); + } +} diff --git a/src/deploy/missions/contracts/mocks/meow-token-mock.ts b/src/deploy/missions/contracts/mocks/meow-token-mock.ts deleted file mode 100644 index 0a72b831d..000000000 --- a/src/deploy/missions/contracts/mocks/meow-token-mock.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { BaseDeployMission } from "../../base-deploy-mission"; -import { ProxyKinds, znsNames } from "../../../constants"; -import { TDeployArgs } from "../../types"; -import { ethers } from "ethers"; - - -export const meowTokenName = "Meow Token"; -export const meowTokenSymbol = "MEOW"; - - -// TODO dep !IMPORTANT!: refine this to create an object if using -// the actual deployed MEOW, so that we have it's full object present -// here for easy access and to make sure we get it's ABI and bytecode for the DB -export class MeowTokenMockDM extends BaseDeployMission { - proxyData = { - isProxy: true, - kind: ProxyKinds.transparent, - }; - - contractName = znsNames.meowToken.contract; - instanceName = znsNames.meowToken.instance; - - deployArgs () : TDeployArgs { - return [meowTokenName, meowTokenSymbol]; - } - - async postDeploy () { - const { - meowToken, - config: { - deployAdmin, - }, - } = this.campaign; - - // Mint 10,000 ZERO to the deployer - await meowToken.connect(deployAdmin).mint( - deployAdmin.address, - ethers.utils.parseEther("100000") - ); - } -} diff --git a/src/deploy/missions/contracts/names.ts b/src/deploy/missions/contracts/names.ts new file mode 100644 index 000000000..effc15222 --- /dev/null +++ b/src/deploy/missions/contracts/names.ts @@ -0,0 +1,49 @@ +export const erc1967ProxyName = "ERC1967Proxy"; + +export const znsNames = { + accessController: { + contract: "ZNSAccessController", + instance: "accessController", + }, + registry: { + contract: "ZNSRegistry", + instance: "registry", + }, + domainToken: { + contract: "ZNSDomainToken", + instance: "domainToken", + }, + meowToken: { + contract: "MeowToken", + contractMock: "MeowTokenMock", + instance: "meowToken", + }, + addressResolver: { + contract: "ZNSAddressResolver", + instance: "addressResolver", + }, + curvePricer: { + contract: "ZNSCurvePricer", + instance: "curvePricer", + }, + fixedPricer: { + contract: "ZNSFixedPricer", + instance: "fixedPricer", + }, + treasury: { + contract: "ZNSTreasury", + instance: "treasury", + }, + rootRegistrar: { + contract: "ZNSRootRegistrar", + instance: "rootRegistrar", + }, + subRegistrar: { + contract: "ZNSSubRegistrar", + instance: "subRegistrar", + }, + erc1967Proxy: { + contract: erc1967ProxyName, + instance: "erc1967Proxy", + }, +}; diff --git a/src/deploy/missions/contracts/registry.ts b/src/deploy/missions/contracts/registry.ts index 3b71d05a4..8b6c7ee6b 100644 --- a/src/deploy/missions/contracts/registry.ts +++ b/src/deploy/missions/contracts/registry.ts @@ -1,6 +1,7 @@ import { BaseDeployMission } from "../base-deploy-mission"; -import { ProxyKinds, znsNames } from "../../constants"; +import { ProxyKinds } from "../../constants"; import { TDeployArgs } from "../types"; +import { znsNames } from "./names"; export class ZNSRegistryDM extends BaseDeployMission { diff --git a/src/deploy/missions/contracts/root-registrar.ts b/src/deploy/missions/contracts/root-registrar.ts index 47edc2610..d9567948a 100644 --- a/src/deploy/missions/contracts/root-registrar.ts +++ b/src/deploy/missions/contracts/root-registrar.ts @@ -1,6 +1,7 @@ import { BaseDeployMission } from "../base-deploy-mission"; -import { ProxyKinds, REGISTRAR_ROLE, znsNames } from "../../constants"; +import { ProxyKinds, REGISTRAR_ROLE } from "../../constants"; import { TDeployArgs } from "../types"; +import { znsNames } from "./names"; export class ZNSRootRegistrarDM extends BaseDeployMission { diff --git a/src/deploy/missions/contracts/sub-registrar.ts b/src/deploy/missions/contracts/sub-registrar.ts index 2345b78da..c80010a8a 100644 --- a/src/deploy/missions/contracts/sub-registrar.ts +++ b/src/deploy/missions/contracts/sub-registrar.ts @@ -1,6 +1,7 @@ import { BaseDeployMission } from "../base-deploy-mission"; -import { ProxyKinds, REGISTRAR_ROLE, znsNames } from "../../constants"; +import { ProxyKinds, REGISTRAR_ROLE } from "../../constants"; import { TDeployArgs } from "../types"; +import { znsNames } from "./names"; export class ZNSSubRegistrarDM extends BaseDeployMission { diff --git a/src/deploy/missions/contracts/treasury.ts b/src/deploy/missions/contracts/treasury.ts index 1f5677b03..28f99ff7c 100644 --- a/src/deploy/missions/contracts/treasury.ts +++ b/src/deploy/missions/contracts/treasury.ts @@ -1,8 +1,8 @@ import { BaseDeployMission } from "../base-deploy-mission"; -import { ProxyKinds, znsNames } from "../../constants"; -import { IDeployMissionArgs, TDeployArgs } from "../types"; -import { ethers } from "ethers"; -import { MeowTokenMockDM } from "./mocks/meow-token-mock"; +import { ProxyKinds } from "../../constants"; +import { TDeployArgs } from "../types"; +import { znsNames } from "./names"; + export class ZNSTreasuryDM extends BaseDeployMission { proxyData = { @@ -10,77 +10,24 @@ export class ZNSTreasuryDM extends BaseDeployMission { kind: ProxyKinds.uups, }; - // bool for determining token setup behaviour - // determined in constructor - isMockedMeowToken : boolean; contractName = znsNames.treasury.contract; instanceName = znsNames.treasury.instance; - constructor (args : IDeployMissionArgs) { - super(args); - - const { - config: { - stakingTokenAddress, - }, - } = this.campaign; - - if (!!stakingTokenAddress) { - this.isMockedMeowToken = false; - } else { - // TODO dep: is this a correct check? - if (!this.campaign.state.missions.includes(MeowTokenMockDM)) throw new Error( - `No staking token found! - Please make sure to provide 'stakingTokenAddress' to the config - or add mocked token to the Deploy Campaign if this is a test.` - ); - - // TODO dep: possibly make an ENV var out of this so that it is known before we get here - this.isMockedMeowToken = true; - } - } - deployArgs () : TDeployArgs { const { accessController, registry, meowToken, config: { - stakingTokenAddress, zeroVaultAddress, }, } = this.campaign; - const stakingToken = !this.isMockedMeowToken - ? stakingTokenAddress - : meowToken.address; - return [ accessController.address, registry.address, - stakingToken, + meowToken.address, zeroVaultAddress, ]; } - - async needsPostDeploy () : Promise { - return this.isMockedMeowToken; - } - - // this should launch ONLY if the Meow Token was mocked in test ! - async postDeploy () { - const { - meowToken, - treasury, - config: { - deployAdmin, - }, - } = this.campaign; - - // Give allowance to the treasury - await meowToken.connect(deployAdmin).approve( - treasury.address, - ethers.constants.MaxUint256 - ); - } } diff --git a/src/deploy/missions/types.ts b/src/deploy/missions/types.ts index 1c01055be..e4aefe89c 100644 --- a/src/deploy/missions/types.ts +++ b/src/deploy/missions/types.ts @@ -12,24 +12,21 @@ export interface IDeployMissionArgs { export type TDeployMissionCtor = new (args : IDeployMissionArgs) => BaseDeployMission; -export interface IContractDbObject { - address : string; - abi : string; - bytecode : string; - args : string; - implementation : string | null; - version : string; -} - export type TDeployArg = string | Array | BigNumber | ICurvePriceConfig; export type TDeployArgs = Array; export type TProxyKind = "uups" | "transparent" | "beacon" | undefined; +export interface IProxyKinds { + uups : TProxyKind; + transparent : TProxyKind; + beacon : TProxyKind; +} + export interface IProxyData { isProxy : boolean; - proxyKind ?: TProxyKind; + kind ?: TProxyKind; } export interface ICurvePriceConfig { diff --git a/src/deploy/storage/base-storage-adapter.ts b/src/deploy/storage/base-storage-adapter.ts index 384d48cc6..1f99370e5 100644 --- a/src/deploy/storage/base-storage-adapter.ts +++ b/src/deploy/storage/base-storage-adapter.ts @@ -1,21 +1,22 @@ -import { IContractDbObject } from "../missions/types"; -import { TLogger } from "../campaign/types"; - - -export class BaseStorageAdapter { - logger : TLogger; - - constructor (logger : TLogger) { - this.logger = logger; - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async writeContract (contractDbName : string, data : IContractDbObject) { - throw new Error("This class can NOT be used as storage adapter. It needs to be inherited and implemented."); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async getContract (contractDbName : string) : Promise { - throw new Error("This class can NOT be used as storage adapter. It needs to be inherited and implemented."); - } -} +import { TLogger } from "../campaign/types"; +import { IContractDbData, TContractDBDoc } from "../db/types"; + + +// TODO dep: do we need this class at all? remove this if not used +export class BaseStorageAdapter { + logger : TLogger; + + constructor (logger : TLogger) { + this.logger = logger; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async writeContract (contractDbName : string, data : IContractDbData) { + throw new Error("This class can NOT be used as storage adapter. It needs to be inherited and implemented."); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async getContract (contractDbName : string) : Promise { + throw new Error("This class can NOT be used as storage adapter. It needs to be inherited and implemented."); + } +} diff --git a/src/deploy/storage/file-storage.ts b/src/deploy/storage/file-storage.ts index adf180644..68b394b0b 100644 --- a/src/deploy/storage/file-storage.ts +++ b/src/deploy/storage/file-storage.ts @@ -1,12 +1,10 @@ import fs from "fs"; import path from "path"; -import { IContractDbObject } from "../missions/types"; import { BaseStorageAdapter } from "./base-storage-adapter"; import { TLogger } from "../campaign/types"; +import { IContractDbData } from "../db/types"; -// TODO dep: remove temp db folder and possibly add to .gitignore -// when testing is done export const fileStoragePath = path.join(process.cwd(), "./db"); @@ -28,7 +26,7 @@ export class FileStorageAdapter extends BaseStorageAdapter { } } - async writeContract (contractDbName : string, data : IContractDbObject) { + async writeContract (contractDbName : string, data : IContractDbData) { if (!this.writeLocal) return; const filePath = path.join(fileStoragePath, `/${contractDbName}.json`); @@ -39,7 +37,7 @@ export class FileStorageAdapter extends BaseStorageAdapter { this.logger.info(`Contract data for ${contractDbName} saved to file: ${filePath}.`); } - async getContract (contractDbName : string) : Promise { + async getContract (contractDbName : string) : Promise { const filePath = path.join(fileStoragePath, `/${contractDbName}.json`); if (!fs.existsSync(filePath)) { diff --git a/src/deploy/zns-campaign.ts b/src/deploy/zns-campaign.ts index 1b108e979..c7f86f7ba 100644 --- a/src/deploy/zns-campaign.ts +++ b/src/deploy/zns-campaign.ts @@ -1,9 +1,8 @@ import { ICampaignArgs, IDeployCampaignConfig, TLogger } from "./campaign/types"; import { HardhatDeployer } from "./deployer/hardhat-deployer"; -import { FileStorageAdapter } from "./storage/file-storage"; import { DeployCampaign } from "./campaign/deploy-campaign"; import { - MeowTokenMockDM, + MeowTokenDM, ZNSAccessControllerDM, ZNSAddressResolverDM, ZNSDomainTokenDM, ZNSCurvePricerDM, ZNSRootRegistrarDM, @@ -11,32 +10,33 @@ import { } from "./missions/contracts"; import * as hre from "hardhat"; +import { getMongoAdapter } from "./db/mongo-adapter/get-adapter"; + // TODO dep: add configs for ENV vars in this repo export const runZnsCampaign = async ({ config, logger, - writeLocal, + dbVersion, } : { config : IDeployCampaignConfig; logger : TLogger; - writeLocal ?: boolean; + dbVersion ?: string; }) => { // TODO dep: figure out the best place to put this at! hre.upgrades.silenceWarnings(); const deployer = new HardhatDeployer(); - const dbAdapterIn = new FileStorageAdapter(logger, writeLocal); + + // TODO dep: remove all hardcoded stuff and turn into constants or ENV vars! + const dbAdapter = await getMongoAdapter(); const campaign = new DeployCampaign({ missions: [ ZNSAccessControllerDM, ZNSRegistryDM, ZNSDomainTokenDM, - // TODO dep: add proper class for MeowToken in prod, - // that is able to determine to deploy a mock for test - // or use the data for existing Meow on mainnet to create and object and save to state - MeowTokenMockDM, + MeowTokenDM, ZNSAddressResolverDM, ZNSCurvePricerDM, ZNSTreasuryDM, @@ -45,12 +45,15 @@ export const runZnsCampaign = async ({ ZNSSubRegistrarDM, ], deployer, - dbAdapter: dbAdapterIn, + dbAdapter, logger, config, } as ICampaignArgs); await campaign.execute(); + // TODO dep: find the best place to call these ! + await dbAdapter.finalizeDeployedVersion(dbVersion); + return campaign; }; diff --git a/src/utils/compare-bytecode.ts b/src/utils/compare-bytecode.ts new file mode 100644 index 000000000..4eddba822 --- /dev/null +++ b/src/utils/compare-bytecode.ts @@ -0,0 +1,10 @@ + +// TODO dep: this does not work. find a better way and uncomment the call in meow-token.ts +const replacePattern = /a165627a7a72305820.{64}0029.*/g; + +export const compareBytecodeStrict = (bytecodeA : string, bytecodeB : string) => { + const bytecodeAWithoutMetadata = bytecodeA.replace(replacePattern, ".{86}"); + const bytecodeBWithoutMetadata = bytecodeB.replace(replacePattern, ".{86}"); + + return bytecodeAWithoutMetadata === bytecodeBWithoutMetadata; +}; diff --git a/src/utils/drop-db.ts b/src/utils/drop-db.ts new file mode 100644 index 000000000..0fd76541d --- /dev/null +++ b/src/utils/drop-db.ts @@ -0,0 +1,24 @@ +import { getMongoAdapter } from "../deploy/db/mongo-adapter/get-adapter"; +import { startMongo, stopMongo } from "../deploy/db/service/mongo-service"; +import { getLogger } from "../deploy/logger/create-logger"; + + +const logger = getLogger(); + +export const dropDB = async () => { + try { + const adapter = await getMongoAdapter(); + await adapter.dropDB(); + await stopMongo(); + } catch (e) { + await startMongo(); + await dropDB(); + } +}; + +dropDB() + .then(() => process.exit(0)) + .catch(error => { + logger.debug(error); + process.exit(1); + }); diff --git a/test/DeployCampaign.test.ts b/test/DeployCampaign.test.ts new file mode 100644 index 000000000..690f1b088 --- /dev/null +++ b/test/DeployCampaign.test.ts @@ -0,0 +1,127 @@ +import * as hre from "hardhat"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import { + defaultRoyaltyFraction, + priceConfigDefault, + ZNS_DOMAIN_TOKEN_NAME, + ZNS_DOMAIN_TOKEN_SYMBOL, +} from "./helpers"; +import { expect } from "chai"; +import { + meowTokenName, meowTokenSymbol, +} from "../src/deploy/missions/contracts"; +import { znsNames } from "../src/deploy/missions/contracts/names"; +import { IDeployCampaignConfig } from "../src/deploy/campaign/types"; +import { getLogger } from "../src/deploy/logger/create-logger"; +import { runZnsCampaign } from "../src/deploy/zns-campaign"; +import { MeowMainnet } from "../src/deploy/missions/contracts/meow-token/mainnet-data"; + + +describe("Deploy Campaign Test", () => { + let deployAdmin : SignerWithAddress; + let admin : SignerWithAddress; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + let user : SignerWithAddress; + let zeroVault : SignerWithAddress; + let campaignConfig : IDeployCampaignConfig; + + // TODO dep: move logger to runZNSCampaign() + const logger = getLogger(); + + + describe("MEOW Token Ops", () => { + before(async () => { + [deployAdmin, admin, zeroVault, user] = await hre.ethers.getSigners(); + + campaignConfig = { + deployAdmin, + governorAddresses: [ deployAdmin.address ], + adminAddresses: [ deployAdmin.address, admin.address ], + domainToken: { + name: ZNS_DOMAIN_TOKEN_NAME, + symbol: ZNS_DOMAIN_TOKEN_SYMBOL, + defaultRoyaltyReceiver: deployAdmin.address, + defaultRoyaltyFraction, + }, + rootPriceConfig: priceConfigDefault, + zeroVaultAddress: zeroVault.address, + stakingTokenAddress: MeowMainnet.address, + mockMeowToken: true, + }; + }); + + it("should deploy new MeowTokenMock when `mockMeowToken` is true", async () => { + const campaign = await runZnsCampaign({ + config: campaignConfig, + logger, + }); + + const { meowToken, dbAdapter } = campaign; + + const toMint = hre.ethers.utils.parseEther("972315"); + // `mint()` only exists on the Mocked contract + await meowToken.connect(deployAdmin).mint( + user.address, + toMint + ); + + const balance = await meowToken.balanceOf(user.address); + expect(balance).to.equal(toMint); + + await dbAdapter.dropDB(); + }); + + it("should use existing deployed non-mocked MeowToken contract when `mockMeowToken` is false", async () => { + campaignConfig.mockMeowToken = false; + + // deploy MeowToken contract + const factory = await hre.ethers.getContractFactory("MeowToken"); + const meow = await hre.upgrades.deployProxy( + factory, + [meowTokenName, meowTokenSymbol], + { + kind: "transparent", + }); + + await meow.deployed(); + + campaignConfig.stakingTokenAddress = meow.address; + + const campaign = await runZnsCampaign({ + config: campaignConfig, + logger, + }); + + const { + meowToken, + dbAdapter, + state: { + instances: { + meowToken: meowDMInstance, + }, + }, + } = campaign; + + expect(meowToken.address).to.equal(meow.address); + expect(meowDMInstance.contractName).to.equal(znsNames.meowToken.contract); + // TODO dep: what else ?? + + const toMint = hre.ethers.utils.parseEther("972315"); + // `mint()` only exists on the Mocked contract + try { + await meowToken.connect(deployAdmin).mint( + user.address, + toMint + ); + } catch (e) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + expect(e.message).to.include( + ".mint is not a function" + ); + } + + await dbAdapter.dropDB(); + }); + }); +}); diff --git a/test/ZNSRootRegistrar.test.ts b/test/ZNSRootRegistrar.test.ts index 10142798c..9d210eb58 100644 --- a/test/ZNSRootRegistrar.test.ts +++ b/test/ZNSRootRegistrar.test.ts @@ -1,1211 +1,1229 @@ -import * as hre from "hardhat"; -import { expect } from "chai"; -import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; -import { - AccessType, defaultRoyaltyFraction, defaultTokenURI, - distrConfigEmpty, - hashDomainLabel, INVALID_LENGTH_ERR, - INITIALIZED_ERR, - INVALID_TOKENID_ERC_ERR, - normalizeName, - NOT_AUTHORIZED_REG_ERR, - NOT_BOTH_OWNER_RAR_ERR, - NOT_TOKEN_OWNER_RAR_ERR, - ONLY_NAME_OWNER_REG_ERR, - ONLY_OWNER_REGISTRAR_REG_ERR, OwnerOf, PaymentType, REGISTRAR_ROLE, - validateUpgrade, - ZNS_DOMAIN_TOKEN_NAME, ZNS_DOMAIN_TOKEN_SYMBOL, -} from "./helpers"; -import { IDistributionConfig } from "./helpers/types"; -import * as ethers from "ethers"; -import { BigNumber } from "ethers"; -import { defaultRootRegistration } from "./helpers/register-setup"; -import { checkBalance } from "./helpers/balances"; -import { precisionMultiDefault, priceConfigDefault, registrationFeePercDefault } from "./helpers/constants"; -import { getPriceObject } from "./helpers/pricing"; -import { getDomainHashFromReceipt, getTokenIdFromReceipt } from "./helpers/events"; -import { runZnsCampaign } from "../src/deploy/zns-campaign"; -import { IDeployCampaignConfig, TZNSContractState } from "../src/deploy/campaign/types"; -import { createLogger } from "../src/deploy/logger/create-logger"; -import { getAccessRevertMsg, INVALID_NAME_ERR } from "./helpers/errors"; -import { ADMIN_ROLE, GOVERNOR_ROLE } from "../src/deploy/constants"; -import { IERC20, ZNSRootRegistrar__factory, ZNSRootRegistrarUpgradeMock__factory } from "../typechain"; -import { PaymentConfigStruct } from "../typechain/contracts/treasury/IZNSTreasury"; -import { parseEther } from "ethers/lib/utils"; -import { getProxyImplAddress } from "./helpers/utils"; -import { upgrades } from "hardhat"; - -// import * as m from "@zero-tech/ztoken"; - -require("@nomicfoundation/hardhat-chai-matchers"); - - -// TODO dep: this is the only test converted to use the new Campaign -// others need to be converted once the Campaign is ready in full -describe("ZNSRootRegistrar", () => { - let deployer : SignerWithAddress; - let user : SignerWithAddress; - let governor : SignerWithAddress; - let admin : SignerWithAddress; - let randomUser : SignerWithAddress; - - let zns : TZNSContractState; - let zeroVault : SignerWithAddress; - let operator : SignerWithAddress; - const defaultDomain = normalizeName("wilder"); - let userBalanceInitial : BigNumber; - - beforeEach(async () => { - [deployer, zeroVault, user, operator, governor, admin, randomUser] = await hre.ethers.getSigners(); - // zeroVault address is used to hold the fee charged to the user when registering - - // TODO dep: this whole config should be passed safely through ENV var injection - const config : IDeployCampaignConfig = { - deployAdmin: deployer, - governorAddresses: [ deployer.address ], - adminAddresses: [ deployer.address, admin.address ], - domainToken: { - name: ZNS_DOMAIN_TOKEN_NAME, - symbol: ZNS_DOMAIN_TOKEN_SYMBOL, - defaultRoyaltyReceiver: deployer.address, - defaultRoyaltyFraction, - }, - rootPriceConfig: priceConfigDefault, - // registrationFee: registrationFeePercDefault, - // stakingTokenAddress: "0x0eC78ED49C2D27b315D462d43B5BAB94d2C79bf8", // mainnet - zeroVaultAddress: zeroVault.address, - }; - - const logger = createLogger("debug"); - - const campaign = await runZnsCampaign({ - config, - logger, - writeLocal: false, - }); - - zns = campaign.state.contracts; - - userBalanceInitial = ethers.utils.parseEther("100000000000"); - // Give funds to user - await zns.meowToken.connect(user).approve(zns.treasury.address, ethers.constants.MaxUint256); - await zns.meowToken.mint(user.address, userBalanceInitial); - }); - - it("Gas tests", async () => { - const tokenURI = "https://example.com/817c64af"; - const distrConfig : IDistributionConfig = { - pricerContract: zns.curvePricer.address, - paymentType: 1, - accessType: 1, - }; - - const tx = await zns.rootRegistrar.connect(deployer).registerRootDomain( - defaultDomain, - deployer.address, - tokenURI, - distrConfig - ); - - const receipt = await tx.wait(); - - const domainHash = await getDomainHashFromReceipt(receipt); - - // Registering as deployer (owner of parent) and user is different gas values - await zns.subRegistrar.connect(deployer).registerSubdomain( - domainHash, - "subdomain", - deployer.address, - tokenURI, - distrConfigEmpty - ); - - const candidates = [ - deployer.address, - user.address, - governor.address, - admin.address, - randomUser.address, - ]; - - const allowed = [ - true, - true, - true, - true, - true, - ]; - - await zns.subRegistrar.updateMintlistForDomain( - domainHash, - candidates, - allowed - ); - }); - - it("Should NOT let initialize the implementation contract", async () => { - const factory = new ZNSRootRegistrar__factory(deployer); - const impl = await getProxyImplAddress(zns.rootRegistrar.address); - const implContract = factory.attach(impl); - - await expect( - implContract.initialize( - operator.address, - operator.address, - operator.address, - operator.address, - operator.address, - ) - ).to.be.revertedWith(INITIALIZED_ERR); - }); - - it("Allows transfer of 0x0 domain ownership after deployment", async () => { - await zns.registry.updateDomainOwner(ethers.constants.HashZero, user.address); - expect(await zns.registry.getDomainOwner(ethers.constants.HashZero)).to.equal(user.address); - }); - - it("Confirms a new 0x0 owner can modify the configs in the treasury and curve pricer", async () => { - await zns.registry.updateDomainOwner(ethers.constants.HashZero, user.address); - - const newTreasuryConfig : PaymentConfigStruct = { - token: zeroVault.address, // Just needs to be a different address - beneficiary: user.address, - }; - - // Modify the treasury - const treasuryTx = await zns.treasury.connect(user).setPaymentConfig(ethers.constants.HashZero, newTreasuryConfig); - - await expect(treasuryTx).to.emit( - zns.treasury, - "BeneficiarySet" - ).withArgs( - ethers.constants.HashZero, - user.address - ); - await expect(treasuryTx).to.emit( - zns.treasury, - "PaymentTokenSet" - ).withArgs( - ethers.constants.HashZero, - zeroVault.address - ); - - // Modify the curve pricer - const newPricerConfig = { - baseLength: BigNumber.from("6"), - maxLength: BigNumber.from("35"), - maxPrice: parseEther("150"), - minPrice: parseEther("10"), - precisionMultiplier: precisionMultiDefault, - feePercentage: registrationFeePercDefault, - isSet: true, - }; - - const pricerTx = await zns.curvePricer.connect(user).setPriceConfig( - ethers.constants.HashZero, - newPricerConfig, - ); - - await expect(pricerTx).to.emit(zns.curvePricer, "PriceConfigSet").withArgs( - ethers.constants.HashZero, - newPricerConfig.maxPrice, - newPricerConfig.minPrice, - newPricerConfig.maxLength, - newPricerConfig.baseLength, - newPricerConfig.precisionMultiplier, - newPricerConfig.feePercentage, - ); - }); - - it("Confirms a user has funds and allowance for the Registrar", async () => { - const balance = await zns.meowToken.balanceOf(user.address); - expect(balance).to.eq(userBalanceInitial); - - const allowance = await zns.meowToken.allowance(user.address, zns.treasury.address); - expect(allowance).to.eq(ethers.constants.MaxUint256); - }); - - it("Should revert when initialize() without ADMIN_ROLE", async () => { - const userHasAdmin = await zns.accessController.hasRole(ADMIN_ROLE, user.address); - expect(userHasAdmin).to.be.false; - - const registrarFactory = new ZNSRootRegistrar__factory(user); - - const tx = upgrades.deployProxy( - registrarFactory, - [ - zns.accessController.address, - zns.registry.address, - zns.curvePricer.address, - zns.treasury.address, - zns.domainToken.address, - ], - { - kind: "uups", - } - ); - - await expect(tx).to.be.revertedWith(getAccessRevertMsg(user.address, ADMIN_ROLE)); - }); - - it("Should NOT initialize twice", async () => { - const tx = zns.rootRegistrar.connect(deployer).initialize( - zns.accessController.address, - randomUser.address, - randomUser.address, - randomUser.address, - randomUser.address, - ); - - await expect(tx).to.be.revertedWith("Initializable: contract is already initialized"); - }); - - describe("General functionality", () => { - it("#coreRegister() should revert if called by address without REGISTRAR_ROLE", async () => { - const isRegistrar = await zns.accessController.hasRole(REGISTRAR_ROLE, randomUser.address); - expect(isRegistrar).to.be.false; - - await expect( - zns.rootRegistrar.connect(randomUser).coreRegister({ - parentHash: ethers.constants.HashZero, - domainHash: ethers.constants.HashZero, - label: "randomname", - registrant: ethers.constants.AddressZero, - price: "0", - stakeFee: "0", - domainAddress: ethers.constants.AddressZero, - tokenURI: "", - isStakePayment: false, - }) - ).to.be.revertedWith( - getAccessRevertMsg(randomUser.address, REGISTRAR_ROLE) - ); - }); - - it("#isOwnerOf() returns correct bools", async () => { - const topLevelTx = await defaultRootRegistration({ - user, - zns, - domainName: defaultDomain, - }); - const domainHash = await getDomainHashFromReceipt(topLevelTx); - const tokenId = BigNumber.from(domainHash); - - const isOwnerOfBothUser = await zns.rootRegistrar.isOwnerOf( - domainHash, - user.address, - OwnerOf.BOTH - ); - expect(isOwnerOfBothUser).to.be.true; - - const isOwnerOfBothRandom = await zns.rootRegistrar.isOwnerOf( - domainHash, - randomUser.address, - OwnerOf.BOTH - ); - expect(isOwnerOfBothRandom).to.be.false; - - // transfer token - await zns.domainToken.connect(user).transferFrom(user.address, randomUser.address, tokenId); - const isOwnerOfTokenUser = await zns.rootRegistrar.isOwnerOf( - domainHash, - user.address, - OwnerOf.TOKEN - ); - expect(isOwnerOfTokenUser).to.be.false; - - const isOwnerOfTokenRandom = await zns.rootRegistrar.isOwnerOf( - domainHash, - randomUser.address, - OwnerOf.TOKEN - ); - expect(isOwnerOfTokenRandom).to.be.true; - - const isOwnerOfNameUser = await zns.rootRegistrar.isOwnerOf( - domainHash, - user.address, - OwnerOf.NAME - ); - expect(isOwnerOfNameUser).to.be.true; - - const isOwnerOfNameRandom = await zns.rootRegistrar.isOwnerOf( - domainHash, - randomUser.address, - OwnerOf.NAME - ); - expect(isOwnerOfNameRandom).to.be.false; - - await expect( - zns.rootRegistrar.isOwnerOf(domainHash, user.address, 3) - ).to.be.reverted; - }); - - it("#setSubRegistrar() should revert if called by address without ADMIN_ROLE", async () => { - const isAdmin = await zns.accessController.hasRole(ADMIN_ROLE, randomUser.address); - expect(isAdmin).to.be.false; - - await expect( - zns.rootRegistrar.connect(randomUser).setSubRegistrar(randomUser.address) - ).to.be.revertedWith( - getAccessRevertMsg(randomUser.address, ADMIN_ROLE) - ); - }); - - it("#setSubRegistrar() should set the correct address", async () => { - await zns.rootRegistrar.connect(admin).setSubRegistrar(randomUser.address); - - expect( - await zns.rootRegistrar.subRegistrar() - ).to.equal(randomUser.address); - }); - - it("#setSubRegistrar() should NOT set the address to zero address", async () => { - await expect( - zns.rootRegistrar.connect(admin).setSubRegistrar(ethers.constants.AddressZero) - ).to.be.revertedWith( - "ZNSRootRegistrar: subRegistrar_ is 0x0 address" - ); - }); - }); - - describe("Registers a root domain", () => { - it("Can NOT register a TLD with an empty name", async () => { - const emptyName = ""; - - await expect( - defaultRootRegistration({ - user: deployer, - zns, - domainName: emptyName, - }) - ).to.be.revertedWith(INVALID_LENGTH_ERR); - }); - - it("Can register a TLD with characters [a-z0-9-]", async () => { - const letters = "world"; - const lettersHash = hashDomainLabel(letters); - - const alphaNumeric = "0x0dwidler0x0"; - const alphaNumericHash = hashDomainLabel(alphaNumeric); - - const withHyphen = "0x0-dwidler-0x0"; - const withHyphenHash = hashDomainLabel(withHyphen); - - const tx1 = zns.rootRegistrar.connect(deployer).registerRootDomain( - letters, - ethers.constants.AddressZero, - defaultTokenURI, - distrConfigEmpty - ); - - await expect(tx1).to.emit(zns.rootRegistrar, "DomainRegistered").withArgs( - ethers.constants.HashZero, - lettersHash, - BigNumber.from(lettersHash), - letters, - deployer.address, - ethers.constants.AddressZero, - ); - - const tx2 = zns.rootRegistrar.connect(deployer).registerRootDomain( - alphaNumeric, - ethers.constants.AddressZero, - defaultTokenURI, - distrConfigEmpty - ); - - await expect(tx2).to.emit(zns.rootRegistrar, "DomainRegistered").withArgs( - ethers.constants.HashZero, - alphaNumericHash, - BigNumber.from(alphaNumericHash), - alphaNumeric, - deployer.address, - ethers.constants.AddressZero, - ); - - const tx3 = zns.rootRegistrar.connect(deployer).registerRootDomain( - withHyphen, - ethers.constants.AddressZero, - defaultTokenURI, - distrConfigEmpty - ); - - await expect(tx3).to.emit(zns.rootRegistrar, "DomainRegistered").withArgs( - ethers.constants.HashZero, - withHyphenHash, - BigNumber.from(withHyphenHash), - withHyphen, - deployer.address, - ethers.constants.AddressZero, - ); - }); - - it("Fails for domains that use any invalid character", async () => { - // Valid names must match the pattern [a-z0-9] - const nameA = "WILDER"; - const nameB = "!?w1Id3r!?"; - const nameC = "!%$#^*?!#👍3^29"; - const nameD = "wo.rld"; - - await expect( - defaultRootRegistration({ - user: deployer, - zns, - domainName: nameA, - }) - ).to.be.revertedWith(INVALID_NAME_ERR); - - await expect( - defaultRootRegistration({ - user: deployer, - zns, - domainName: nameB, - }) - ).to.be.revertedWith(INVALID_NAME_ERR); - - await expect( - defaultRootRegistration({ - user: deployer, - zns, - domainName: nameC, - }) - ).to.be.revertedWith(INVALID_NAME_ERR); - - await expect( - defaultRootRegistration({ - user: deployer, - zns, - domainName: nameD, - }) - ).to.be.revertedWith(INVALID_NAME_ERR); - }); - - // eslint-disable-next-line max-len - it("Successfully registers a domain without a resolver or resolver content and fires a #DomainRegistered event", async () => { - const tokenURI = "https://example.com/817c64af"; - const tx = await zns.rootRegistrar.connect(user).registerRootDomain( - defaultDomain, - ethers.constants.AddressZero, - tokenURI, - distrConfigEmpty - ); - - const hashFromTS = hashDomainLabel(defaultDomain); - - await expect(tx).to.emit(zns.rootRegistrar, "DomainRegistered").withArgs( - ethers.constants.HashZero, - hashFromTS, - BigNumber.from(hashFromTS), - defaultDomain, - user.address, - ethers.constants.AddressZero, - ); - - const tokenURISC = await zns.domainToken.tokenURI(hashFromTS); - expect(tokenURISC).to.eq(tokenURI); - }); - - it("Successfully registers a domain with distrConfig and adds it to state properly", async () => { - const distrConfig = { - pricerContract: zns.fixedPricer.address, - accessType: AccessType.OPEN, - paymentType: PaymentType.DIRECT, - }; - const tokenURI = "https://example.com/817c64af"; - - const tx = await zns.rootRegistrar.connect(user).registerRootDomain( - defaultDomain, - ethers.constants.AddressZero, - tokenURI, - distrConfig - ); - - const receipt = await tx.wait(0); - - const domainHash = await getDomainHashFromReceipt(receipt); - - const { - pricerContract, - accessType, - paymentType, - } = await zns.subRegistrar.distrConfigs(domainHash); - - expect(pricerContract).to.eq(distrConfig.pricerContract); - expect(paymentType).to.eq(distrConfig.paymentType); - expect(accessType).to.eq(distrConfig.accessType); - - const tokenURISC = await zns.domainToken.tokenURI(domainHash); - expect(tokenURISC).to.eq(tokenURI); - }); - - it("Stakes and saves the correct amount and token, takes the correct fee and sends fee to Zero Vault", async () => { - const balanceBeforeUser = await zns.meowToken.balanceOf(user.address); - const balanceBeforeVault = await zns.meowToken.balanceOf(zeroVault.address); - - // Deploy "wilder" with default configuration - const tx = await defaultRootRegistration({ - user, - zns, - domainName: defaultDomain, - }); - const domainHash = await getDomainHashFromReceipt(tx); - const { - totalPrice, - expectedPrice, - stakeFee, - } = getPriceObject(defaultDomain, priceConfigDefault); - - await checkBalance({ - token: zns.meowToken as IERC20, - balanceBefore: balanceBeforeUser, - userAddress: user.address, - target: totalPrice, - }); - - await checkBalance({ - token: zns.meowToken as IERC20, - balanceBefore: balanceBeforeVault, - userAddress: zeroVault.address, - target: stakeFee, - shouldDecrease: false, - }); - - const { amount: staked, token } = await zns.treasury.stakedForDomain(domainHash); - - expect(staked).to.eq(expectedPrice); - expect(token).to.eq(zns.meowToken.address); - }); - - it("Sets the correct data in Registry", async () => { - const tx = await defaultRootRegistration({ - user, - zns, - domainName: defaultDomain, - }); - - const namehashRef = hashDomainLabel(defaultDomain); - const domainHash = await getDomainHashFromReceipt(tx); - expect(domainHash).to.eq(namehashRef); - - const { - owner: ownerFromReg, - resolver: resolverFromReg, - } = await zns.registry.getDomainRecord(domainHash); - - expect(ownerFromReg).to.eq(user.address); - expect(resolverFromReg).to.eq(zns.addressResolver.address); - }); - - it("Fails when the user does not have enough funds", async () => { - const balance = await zns.meowToken.balanceOf(user.address); - await zns.meowToken.connect(user).transfer(randomUser.address, balance); - - const tx = defaultRootRegistration({ - user, - zns, - domainName: defaultDomain, - }); - await expect(tx).to.be.revertedWith("ERC20: transfer amount exceeds balance"); - }); - - it("Disallows creation of a duplicate domain", async () => { - await defaultRootRegistration({ - user, - zns, - domainName: defaultDomain, - }); - const failTx = defaultRootRegistration({ - user: deployer, - zns, - domainName: defaultDomain, - }); - - await expect(failTx).to.be.revertedWith("ZNSRootRegistrar: Domain already exists"); - }); - - it("Successfully registers a domain without resolver content", async () => { - const tx = zns.rootRegistrar.connect(user).registerRootDomain( - defaultDomain, - ethers.constants.AddressZero, - defaultTokenURI, - distrConfigEmpty - ); - - await expect(tx).to.not.be.reverted; - }); - - it("Records the correct domain hash", async () => { - const tx = await defaultRootRegistration({ - user, - zns, - domainName: defaultDomain, - }); - - const domainHash = await getDomainHashFromReceipt(tx); - - const exists = await zns.registry.exists(domainHash); - expect(exists).to.be.true; - expect(domainHash).to.eq(hashDomainLabel(defaultDomain)); - }); - - it("Creates and finds the correct tokenId", async () => { - const tx = await defaultRootRegistration({ - user, - zns, - domainName: defaultDomain, - }); - - const tokenId = await getTokenIdFromReceipt(tx); - const owner = await zns.domainToken.ownerOf(tokenId); - expect(owner).to.eq(user.address); - }); - - it("Resolves the correct address from the domain", async () => { - const tx = await defaultRootRegistration({ - user, - zns, - domainName: defaultDomain, - domainContent: zns.rootRegistrar.address, - }); - const domainHash = await getDomainHashFromReceipt(tx); - - const resolvedAddress = await zns.addressResolver.getAddress(domainHash); - expect(resolvedAddress).to.eq(zns.rootRegistrar.address); - }); - - it("Should NOT charge any tokens if price and/or stake fee is 0", async () => { - // set config on CurvePricer for the price to be 0 - await zns.curvePricer.connect(deployer).setMaxPrice(ethers.constants.HashZero, "0"); - await zns.curvePricer.connect(deployer).setMinPrice(ethers.constants.HashZero, "0"); - - const userBalanceBefore = await zns.meowToken.balanceOf(user.address); - const vaultBalanceBefore = await zns.meowToken.balanceOf(zeroVault.address); - - // register a domain - await zns.rootRegistrar.connect(user).registerRootDomain( - defaultDomain, - ethers.constants.AddressZero, - defaultTokenURI, - distrConfigEmpty - ); - - const userBalanceAfter = await zns.meowToken.balanceOf(user.address); - const vaultBalanceAfter = await zns.meowToken.balanceOf(zeroVault.address); - - expect(userBalanceBefore).to.eq(userBalanceAfter); - expect(vaultBalanceBefore).to.eq(vaultBalanceAfter); - - // check existence in Registry - const domainHash = hashDomainLabel(defaultDomain); - const exists = await zns.registry.exists(domainHash); - expect(exists).to.be.true; - - // make sure no transfers happened - const transferEventFilter = zns.meowToken.filters.Transfer( - user.address, - ); - const events = await zns.meowToken.queryFilter(transferEventFilter); - expect(events.length).to.eq(0); - }); - }); - - describe("Reclaiming Domains", () => { - it("Can reclaim name/stake if Token is owned", async () => { - // Register Top level - const topLevelTx = await defaultRootRegistration({ user: deployer, zns, domainName: defaultDomain }); - const domainHash = await getDomainHashFromReceipt(topLevelTx); - const tokenId = await getTokenIdFromReceipt(topLevelTx); - const { amount: staked, token } = await zns.treasury.stakedForDomain(domainHash); - - // Transfer the domain token - await zns.domainToken.connect(deployer).transferFrom(deployer.address, user.address, tokenId); - - // Verify owner in registry - const originalOwner = await zns.registry.connect(deployer).getDomainOwner(domainHash); - expect(originalOwner).to.equal(deployer.address); - - // Reclaim the Domain - await zns.rootRegistrar.connect(user).reclaimDomain(domainHash); - - // Verify domain token is still owned - const owner = await zns.domainToken.connect(user).ownerOf(tokenId); - expect(owner).to.equal(user.address); - - // Verify domain is owned in registry - const registryOwner = await zns.registry.connect(user).getDomainOwner(domainHash); - expect(registryOwner).to.equal(user.address); - - // Verify same amount is staked - const { amount: stakedAfterReclaim, token: tokenAfterReclaim } = await zns.treasury.stakedForDomain(domainHash); - expect(staked).to.equal(stakedAfterReclaim); - expect(tokenAfterReclaim).to.equal(zns.meowToken.address); - expect(token).to.equal(tokenAfterReclaim); - }); - - it("Reclaiming domain token emits DomainReclaimed event", async () => { - const topLevelTx = await defaultRootRegistration({ user: deployer, zns, domainName: defaultDomain }); - const domainHash = await getDomainHashFromReceipt(topLevelTx); - const tokenId = await getTokenIdFromReceipt(topLevelTx); - - // Transfer the domain token - await zns.domainToken.connect(deployer).transferFrom(deployer.address, user.address, tokenId); - // Reclaim the Domain - const tx = await zns.rootRegistrar.connect(user).reclaimDomain(domainHash); - const receipt = await tx.wait(0); - - // Verify Transfer event is emitted - expect(receipt.events?.[1].event).to.eq("DomainReclaimed"); - expect(receipt.events?.[1].args?.domainHash).to.eq( - domainHash - ); - expect(receipt.events?.[1].args?.registrant).to.eq( - user.address - ); - }); - - it("Cannot reclaim name/stake if token is not owned", async () => { - const topLevelTx = await defaultRootRegistration({ user: deployer, zns, domainName: defaultDomain }); - const domainHash = await getDomainHashFromReceipt(topLevelTx); - // Reclaim the Domain - const tx = zns.rootRegistrar.connect(user).reclaimDomain(domainHash); - - // Verify Domain is not reclaimed - await expect(tx).to.be.revertedWith(NOT_TOKEN_OWNER_RAR_ERR); - - // Verify domain is not owned in registrar - const registryOwner = await zns.registry.connect(user).getDomainOwner(domainHash); - expect(registryOwner).to.equal(deployer.address); - }); - - it("Cannot reclaim if domain does not exist", async () => { - const domainHash = "0xd34cfa279afd55afc6aa9c00aa5d01df60179840a93d10eed730058b8dd4146c"; - // Reclaim the Domain - const tx = zns.rootRegistrar.connect(user).reclaimDomain(domainHash); - - // Verify Domain is not reclaimed - await expect(tx).to.be.revertedWith(INVALID_TOKENID_ERC_ERR); - }); - - it("Domain Token can be reclaimed, transferred, and then reclaimed again", async () => { - // Register Top level - const topLevelTx = await defaultRootRegistration({ user: deployer, zns, domainName: defaultDomain }); - const domainHash = await getDomainHashFromReceipt(topLevelTx); - const tokenId = await getTokenIdFromReceipt(topLevelTx); - const { amount: staked, token } = await zns.treasury.stakedForDomain(domainHash); - - // Transfer the domain token - await zns.domainToken.connect(deployer).transferFrom(deployer.address, user.address, tokenId); - - // Reclaim the Domain - await zns.rootRegistrar.connect(user).reclaimDomain(domainHash); - // Verify domain token is still owned - let owner = await zns.domainToken.connect(user).ownerOf(tokenId); - expect(owner).to.equal(user.address); - - // Transfer the domain token back - await zns.domainToken.connect(user).transferFrom(user.address, deployer.address, tokenId); - - // Reclaim the Domain again - await zns.rootRegistrar.connect(deployer).reclaimDomain(domainHash); - - // Verify domain token is owned - owner = await zns.domainToken.connect(deployer).ownerOf(tokenId); - expect(owner).to.equal(deployer.address); - - // Verify domain is owned in registrar - const registryOwner = await zns.registry.connect(deployer).getDomainOwner(domainHash); - expect(registryOwner).to.equal(deployer.address); - - // Verify same amount is staked - const { amount: stakedAfterReclaim, token: tokenAfterReclaim } = await zns.treasury.stakedForDomain(domainHash); - expect(staked).to.equal(stakedAfterReclaim); - expect(tokenAfterReclaim).to.equal(zns.meowToken.address); - expect(token).to.equal(tokenAfterReclaim); - }); - - it("Can revoke and unstake after reclaiming", async () => { - // Verify Balance - const balance = await zns.meowToken.balanceOf(user.address); - expect(balance).to.eq(userBalanceInitial); - - // Register Top level - const topLevelTx = await defaultRootRegistration({ user: deployer, zns, domainName: defaultDomain }); - const domainHash = await getDomainHashFromReceipt(topLevelTx); - const tokenId = await getTokenIdFromReceipt(topLevelTx); - - // Validated staked values - const { - expectedPrice: expectedStaked, - } = getPriceObject(defaultDomain, priceConfigDefault); - const { amount: staked, token } = await zns.treasury.stakedForDomain(domainHash); - expect(staked).to.eq(expectedStaked); - expect(token).to.eq(zns.meowToken.address); - - // Transfer the domain token - await zns.domainToken.connect(deployer).transferFrom(deployer.address, user.address, tokenId); - - // Reclaim the Domain - await zns.rootRegistrar.connect(user).reclaimDomain(domainHash); - - // Revoke the Domain - await zns.rootRegistrar.connect(user).revokeDomain(domainHash); - - // Validated funds are unstaked - const { amount: finalstaked, token: finalToken } = await zns.treasury.stakedForDomain(domainHash); - expect(finalstaked).to.equal(ethers.BigNumber.from("0")); - expect(finalToken).to.equal(ethers.constants.AddressZero); - - // Verify final balances - const computedFinalBalance = balance.add(staked); - const finalBalance = await zns.meowToken.balanceOf(user.address); - expect(computedFinalBalance).to.equal(finalBalance); - }); - }); - - describe("Revoking Domains", () => { - it("Revokes a Top level Domain, locks distribution and removes mintlist", async () => { - // Register Top level - const topLevelTx = await defaultRootRegistration({ - user, - zns, - domainName: defaultDomain, - distrConfig: { - pricerContract: zns.fixedPricer.address, - paymentType: PaymentType.DIRECT, - accessType: AccessType.OPEN, - }, - }); - - const domainHash = await getDomainHashFromReceipt(topLevelTx); - - // add mintlist to check revocation - await zns.subRegistrar.connect(user).updateMintlistForDomain( - domainHash, - [user.address, zeroVault.address], - [true, true] - ); - - const ogPrice = BigNumber.from(135); - await zns.fixedPricer.connect(user).setPriceConfig( - domainHash, - { - price: ogPrice, - feePercentage: BigNumber.from(0), - isSet: true, - } - ); - expect(await zns.fixedPricer.getPrice(domainHash, defaultDomain, false)).to.eq(ogPrice); - - const tokenId = await getTokenIdFromReceipt(topLevelTx); - - // Revoke the domain and then verify - await zns.rootRegistrar.connect(user).revokeDomain(domainHash); - - // Verify token has been burned - const ownerOfTx = zns.domainToken.connect(user).ownerOf(tokenId); - await expect(ownerOfTx).to.be.revertedWith( - INVALID_TOKENID_ERC_ERR - ); - - // Verify Domain Record Deleted - const exists = await zns.registry.exists(domainHash); - expect(exists).to.be.false; - - // validate access type has been set to LOCKED - const { accessType } = await zns.subRegistrar.distrConfigs(domainHash); - expect(accessType).to.eq(AccessType.LOCKED); - - // validate mintlist has been removed - expect(await zns.subRegistrar.isMintlistedForDomain(domainHash, user.address)).to.be.false; - expect(await zns.subRegistrar.isMintlistedForDomain(domainHash, zeroVault.address)).to.be.false; - }); - - it("Cannot revoke a domain that doesnt exist", async () => { - // Register Top level - const fakeHash = "0xd34cfa279afd55afc6aa9c00aa5d01df60179840a93d10eed730058b8dd4146c"; - const exists = await zns.registry.exists(fakeHash); - expect(exists).to.be.false; - - // Verify transaction is reverted - const tx = zns.rootRegistrar.connect(user).revokeDomain(fakeHash); - await expect(tx).to.be.revertedWith(NOT_BOTH_OWNER_RAR_ERR); - }); - - it("Revoking domain unstakes", async () => { - // Verify Balance - const balance = await zns.meowToken.balanceOf(user.address); - expect(balance).to.eq(userBalanceInitial); - - // Register Top level - const tx = await defaultRootRegistration({ user, zns, domainName: defaultDomain }); - const domainHash = await getDomainHashFromReceipt(tx); - - // Validated staked values - const { - expectedPrice: expectedStaked, - stakeFee: expectedStakeFee, - } = getPriceObject(defaultDomain, priceConfigDefault); - const { amount: staked, token } = await zns.treasury.stakedForDomain(domainHash); - expect(staked).to.eq(expectedStaked); - expect(token).to.eq(zns.meowToken.address); - - // Get balance after staking - const balanceAfterStaking = await zns.meowToken.balanceOf(user.address); - - // Revoke the domain - await zns.rootRegistrar.connect(user).revokeDomain(domainHash); - - // Validated funds are unstaked - const { amount: finalstaked, token: finalToken } = await zns.treasury.stakedForDomain(domainHash); - expect(finalstaked).to.equal(ethers.BigNumber.from("0")); - expect(finalToken).to.equal(ethers.constants.AddressZero); - - // Verify final balances - const computedBalanceAfterStaking = balanceAfterStaking.add(staked); - const balanceMinusFee = balance.sub(expectedStakeFee); - expect(computedBalanceAfterStaking).to.equal(balanceMinusFee); - const finalBalance = await zns.meowToken.balanceOf(user.address); - expect(computedBalanceAfterStaking).to.equal(finalBalance); - }); - - it("Cannot revoke if Name is owned by another user", async () => { - // Register Top level - const topLevelTx = await defaultRootRegistration({ user: deployer, zns, domainName: defaultDomain }); - const parentDomainHash = await getDomainHashFromReceipt(topLevelTx); - const owner = await zns.registry.connect(user).getDomainOwner(parentDomainHash); - expect(owner).to.not.equal(user.address); - - // Try to revoke domain - const tx = zns.rootRegistrar.connect(user).revokeDomain(parentDomainHash); - await expect(tx).to.be.revertedWith(NOT_BOTH_OWNER_RAR_ERR); - }); - - it("No one can revoke if Token and Name have different owners", async () => { - // Register Top level - const topLevelTx = await defaultRootRegistration({ user: deployer, zns, domainName: defaultDomain }); - const parentDomainHash = await getDomainHashFromReceipt(topLevelTx); - const owner = await zns.registry.connect(user).getDomainOwner(parentDomainHash); - expect(owner).to.not.equal(user.address); - - const tokenId = BigNumber.from(parentDomainHash); - - await zns.domainToken.transferFrom(deployer.address, user.address, tokenId); - - // Try to revoke domain as a new owner of the token - const tx = zns.rootRegistrar.connect(user).revokeDomain(parentDomainHash); - await expect(tx).to.be.revertedWith(NOT_BOTH_OWNER_RAR_ERR); - - const tx2 = zns.rootRegistrar.connect(deployer).revokeDomain(parentDomainHash); - await expect(tx2).to.be.revertedWith(NOT_BOTH_OWNER_RAR_ERR); - }); - - it("After domain has been revoked, an old operator can NOT access Registry", async () => { - // Register Top level - const tx = await defaultRootRegistration({ user, zns, domainName: defaultDomain }); - const domainHash = await getDomainHashFromReceipt(tx); - - // assign an operator - await zns.registry.connect(user).setOwnersOperator(operator.address, true); - - // Revoke the domain - await zns.rootRegistrar.connect(user).revokeDomain(domainHash); - - // check operator access to the revoked domain - const tx2 = zns.registry - .connect(operator) - .updateDomainOwner( - domainHash, - operator.address - ); - await expect(tx2).to.be.revertedWith( - ONLY_OWNER_REGISTRAR_REG_ERR - ); - - const tx3 = zns.registry - .connect(operator) - .updateDomainRecord( - domainHash, - user.address, - operator.address - ); - await expect(tx3).to.be.revertedWith( - ONLY_NAME_OWNER_REG_ERR - ); - - const tx4 = zns.registry - .connect(operator) - .updateDomainResolver( - domainHash, - zeroVault.address - ); - await expect(tx4).to.be.revertedWith( - NOT_AUTHORIZED_REG_ERR - ); - }); - }); - - describe("State Setters", () => { - describe("#setAccessController", () => { - it("Should set AccessController and fire AccessControllerSet event", async () => { - const currentAC = await zns.rootRegistrar.getAccessController(); - const tx = await zns.rootRegistrar.connect(deployer).setAccessController(randomUser.address); - const newAC = await zns.rootRegistrar.getAccessController(); - - await expect(tx).to.emit(zns.rootRegistrar, "AccessControllerSet").withArgs(randomUser.address); - - expect(newAC).to.equal(randomUser.address); - expect(currentAC).to.not.equal(newAC); - }); - - it("Should revert if not called by ADMIN", async () => { - const tx = zns.rootRegistrar.connect(user).setAccessController(randomUser.address); - await expect(tx).to.be.revertedWith( - getAccessRevertMsg(user.address, ADMIN_ROLE) - ); - }); - - it("Should revert if new AccessController is address zero", async () => { - const tx = zns.rootRegistrar.connect(deployer).setAccessController(ethers.constants.AddressZero); - await expect(tx).to.be.revertedWith("AC: _accessController is 0x0 address"); - }); - }); - - describe("#setRegistry", () => { - it("Should set ZNSRegistry and fire RegistrySet event", async () => { - const currentRegistry = await zns.rootRegistrar.registry(); - const tx = await zns.rootRegistrar.connect(deployer).setRegistry(randomUser.address); - const newRegistry = await zns.rootRegistrar.registry(); - - await expect(tx).to.emit(zns.rootRegistrar, "RegistrySet").withArgs(randomUser.address); - - expect(newRegistry).to.equal(randomUser.address); - expect(currentRegistry).to.not.equal(newRegistry); - }); - - it("Should revert if not called by ADMIN", async () => { - const tx = zns.rootRegistrar.connect(user).setRegistry(randomUser.address); - await expect(tx).to.be.revertedWith( - getAccessRevertMsg(user.address, ADMIN_ROLE) - ); - }); - - it("Should revert if ZNSRegistry is address zero", async () => { - const tx = zns.rootRegistrar.connect(deployer).setRegistry(ethers.constants.AddressZero); - await expect(tx).to.be.revertedWith("ARegistryWired: _registry can not be 0x0 address"); - }); - }); - - describe("#setTreasury", () => { - it("Should set Treasury and fire TreasurySet event", async () => { - const currentTreasury = await zns.rootRegistrar.treasury(); - const tx = await zns.rootRegistrar.connect(deployer).setTreasury(randomUser.address); - const newTreasury = await zns.rootRegistrar.treasury(); - - await expect(tx).to.emit(zns.rootRegistrar, "TreasurySet").withArgs(randomUser.address); - - expect(newTreasury).to.equal(randomUser.address); - expect(currentTreasury).to.not.equal(newTreasury); - }); - - it("Should revert if not called by ADMIN", async () => { - const tx = zns.rootRegistrar.connect(user).setTreasury(randomUser.address); - await expect(tx).to.be.revertedWith( - getAccessRevertMsg(user.address, ADMIN_ROLE) - ); - }); - - it("Should revert if Treasury is address zero", async () => { - const tx = zns.rootRegistrar.connect(deployer).setTreasury(ethers.constants.AddressZero); - await expect(tx).to.be.revertedWith("ZNSRootRegistrar: treasury_ is 0x0 address"); - }); - }); - - describe("#setDomainToken", () => { - it("Should set DomainToken and fire DomainTokenSet event", async () => { - const currentToken = await zns.rootRegistrar.domainToken(); - const tx = await zns.rootRegistrar.connect(deployer).setDomainToken(randomUser.address); - const newToken = await zns.rootRegistrar.domainToken(); - - await expect(tx).to.emit(zns.rootRegistrar, "DomainTokenSet").withArgs(randomUser.address); - - expect(newToken).to.equal(randomUser.address); - expect(currentToken).to.not.equal(newToken); - }); - - it("Should revert if not called by ADMIN", async () => { - const tx = zns.rootRegistrar.connect(user).setDomainToken(randomUser.address); - await expect(tx).to.be.revertedWith( - getAccessRevertMsg(user.address, ADMIN_ROLE) - ); - }); - - it("Should revert if DomainToken is address zero", async () => { - const tx = zns.rootRegistrar.connect(deployer).setDomainToken(ethers.constants.AddressZero); - await expect(tx).to.be.revertedWith("ZNSRootRegistrar: domainToken_ is 0x0 address"); - }); - }); - }); - - describe("UUPS", () => { - it("Allows an authorized user to upgrade the contract", async () => { - // Confirm deployer has the correct role first - await expect(zns.accessController.checkGovernor(deployer.address)).to.not.be.reverted; - - const registrarFactory = new ZNSRootRegistrar__factory(deployer); - const registrar = await registrarFactory.deploy(); - await registrar.deployed(); - - const upgradeTx = zns.rootRegistrar.connect(deployer).upgradeTo(registrar.address); - await expect(upgradeTx).to.not.be.reverted; - }); - - it("Fails to upgrade when an unauthorized users calls", async () => { - const registrarFactory = new ZNSRootRegistrar__factory(deployer); - const registrar = await registrarFactory.deploy(); - await registrar.deployed(); - - const tx = zns.rootRegistrar.connect(randomUser).upgradeTo(registrar.address); - - await expect(tx).to.be.revertedWith( - getAccessRevertMsg(randomUser.address, GOVERNOR_ROLE) - ); - }); - - it("Verifies that variable values are not changed in the upgrade process", async () => { - // Confirm deployer has the correct role first - await expect(zns.accessController.checkGovernor(deployer.address)).to.not.be.reverted; - - const registrarFactory = new ZNSRootRegistrarUpgradeMock__factory(deployer); - const registrar = await registrarFactory.deploy(); - await registrar.deployed(); - - const domainName = "world"; - const domainHash = hashDomainLabel(domainName); - - await zns.meowToken.connect(randomUser).approve(zns.treasury.address, ethers.constants.MaxUint256); - await zns.meowToken.mint(randomUser.address, priceConfigDefault.maxPrice); - - await zns.rootRegistrar.connect(randomUser).registerRootDomain( - domainName, - randomUser.address, - defaultTokenURI, - distrConfigEmpty - ); - - - const contractCalls = [ - zns.rootRegistrar.getAccessController(), - zns.rootRegistrar.registry(), - zns.rootRegistrar.treasury(), - zns.rootRegistrar.domainToken(), - zns.registry.exists(domainHash), - zns.treasury.stakedForDomain(domainHash), - zns.domainToken.name(), - zns.domainToken.symbol(), - zns.curvePricer.getPrice(ethers.constants.HashZero, domainName, false), - ]; - - await validateUpgrade(deployer, zns.rootRegistrar, registrar, registrarFactory, contractCalls); - }); - }); -}); +import * as hre from "hardhat"; +import { expect } from "chai"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import { + AccessType, + defaultRoyaltyFraction, + defaultTokenURI, + distrConfigEmpty, + hashDomainLabel, + INVALID_LENGTH_ERR, + INITIALIZED_ERR, + INVALID_TOKENID_ERC_ERR, + normalizeName, + NOT_AUTHORIZED_REG_ERR, + NOT_BOTH_OWNER_RAR_ERR, + NOT_TOKEN_OWNER_RAR_ERR, + ONLY_NAME_OWNER_REG_ERR, + ONLY_OWNER_REGISTRAR_REG_ERR, + OwnerOf, + PaymentType, + REGISTRAR_ROLE, + validateUpgrade, + ZNS_DOMAIN_TOKEN_NAME, + ZNS_DOMAIN_TOKEN_SYMBOL, +} from "./helpers"; +import { IDistributionConfig } from "./helpers/types"; +import * as ethers from "ethers"; +import { BigNumber } from "ethers"; +import { defaultRootRegistration } from "./helpers/register-setup"; +import { checkBalance } from "./helpers/balances"; +import { precisionMultiDefault, priceConfigDefault, registrationFeePercDefault } from "./helpers/constants"; +import { getPriceObject } from "./helpers/pricing"; +import { getDomainHashFromReceipt, getTokenIdFromReceipt } from "./helpers/events"; +import { IDeployCampaignConfig, TZNSContractState } from "../src/deploy/campaign/types"; +import { getAccessRevertMsg, INVALID_NAME_ERR } from "./helpers/errors"; +import { ADMIN_ROLE, GOVERNOR_ROLE } from "../src/deploy/constants"; +import { IERC20, ZNSRootRegistrar__factory, ZNSRootRegistrarUpgradeMock__factory } from "../typechain"; +import { PaymentConfigStruct } from "../typechain/contracts/treasury/IZNSTreasury"; +import { parseEther } from "ethers/lib/utils"; +import { runZnsCampaign } from "../src/deploy/zns-campaign"; +import { getLogger } from "../src/deploy/logger/create-logger"; +import { getProxyImplAddress } from "./helpers/utils"; +import { upgrades } from "hardhat"; +import { MongoDBAdapter } from "../src/deploy/db/mongo-adapter/mongo-adapter"; + +require("@nomicfoundation/hardhat-chai-matchers"); + + +// TODO dep: this is the only test converted to use the new Campaign +// others need to be converted once the Campaign is ready in full +describe("ZNSRootRegistrar", () => { + let deployer : SignerWithAddress; + let user : SignerWithAddress; + let governor : SignerWithAddress; + let admin : SignerWithAddress; + let randomUser : SignerWithAddress; + + let zns : TZNSContractState; + let zeroVault : SignerWithAddress; + let operator : SignerWithAddress; + const defaultDomain = normalizeName("wilder"); + let userBalanceInitial : BigNumber; + + let mongoAdapter : MongoDBAdapter; + + beforeEach(async () => { + [deployer, zeroVault, user, operator, governor, admin, randomUser] = await hre.ethers.getSigners(); + // zeroVault address is used to hold the fee charged to the user when registering + + // TODO dep: this whole config should be passed safely through ENV var injection + // TODO dep: extract this to it's own file and function that will work everywhere as part of the flow + const config : IDeployCampaignConfig = { + deployAdmin: deployer, + governorAddresses: [ deployer.address ], + adminAddresses: [ deployer.address, admin.address ], + domainToken: { + name: ZNS_DOMAIN_TOKEN_NAME, + symbol: ZNS_DOMAIN_TOKEN_SYMBOL, + defaultRoyaltyReceiver: deployer.address, + defaultRoyaltyFraction, + }, + rootPriceConfig: priceConfigDefault, + zeroVaultAddress: zeroVault.address, + stakingTokenAddress: "", + mockMeowToken: true, + }; + + const logger = getLogger(); + + const campaign = await runZnsCampaign({ + config, + logger, + }); + + zns = campaign.state.contracts; + mongoAdapter = campaign.dbAdapter; + + await zns.meowToken.connect(deployer).approve( + zns.treasury.address, + ethers.constants.MaxUint256 + ); + + userBalanceInitial = ethers.utils.parseEther("100000000000"); + // Give funds to user + await zns.meowToken.connect(user).approve(zns.treasury.address, ethers.constants.MaxUint256); + await zns.meowToken.mint(user.address, userBalanceInitial); + }); + + afterEach(async () => { + await mongoAdapter.dropDB(); + }); + + it("Gas tests", async () => { + const tokenURI = "https://example.com/817c64af"; + const distrConfig : IDistributionConfig = { + pricerContract: zns.curvePricer.address, + paymentType: 1, + accessType: 1, + }; + + const tx = await zns.rootRegistrar.connect(deployer).registerRootDomain( + defaultDomain, + deployer.address, + tokenURI, + distrConfig + ); + + const receipt = await tx.wait(); + + const domainHash = await getDomainHashFromReceipt(receipt); + + // Registering as deployer (owner of parent) and user is different gas values + await zns.subRegistrar.connect(deployer).registerSubdomain( + domainHash, + "subdomain", + deployer.address, + tokenURI, + distrConfigEmpty + ); + + const candidates = [ + deployer.address, + user.address, + governor.address, + admin.address, + randomUser.address, + ]; + + const allowed = [ + true, + true, + true, + true, + true, + ]; + + await zns.subRegistrar.updateMintlistForDomain( + domainHash, + candidates, + allowed + ); + }); + + it("Should NOT let initialize the implementation contract", async () => { + const factory = new ZNSRootRegistrar__factory(deployer); + const impl = await getProxyImplAddress(zns.rootRegistrar.address); + const implContract = factory.attach(impl); + + await expect( + implContract.initialize( + operator.address, + operator.address, + operator.address, + operator.address, + operator.address, + ) + ).to.be.revertedWith(INITIALIZED_ERR); + }); + + it("Allows transfer of 0x0 domain ownership after deployment", async () => { + await zns.registry.updateDomainOwner(ethers.constants.HashZero, user.address); + expect(await zns.registry.getDomainOwner(ethers.constants.HashZero)).to.equal(user.address); + }); + + it("Confirms a new 0x0 owner can modify the configs in the treasury and curve pricer", async () => { + await zns.registry.updateDomainOwner(ethers.constants.HashZero, user.address); + + const newTreasuryConfig : PaymentConfigStruct = { + token: zeroVault.address, // Just needs to be a different address + beneficiary: user.address, + }; + + // Modify the treasury + const treasuryTx = await zns.treasury.connect(user).setPaymentConfig(ethers.constants.HashZero, newTreasuryConfig); + + await expect(treasuryTx).to.emit( + zns.treasury, + "BeneficiarySet" + ).withArgs( + ethers.constants.HashZero, + user.address + ); + await expect(treasuryTx).to.emit( + zns.treasury, + "PaymentTokenSet" + ).withArgs( + ethers.constants.HashZero, + zeroVault.address + ); + + // Modify the curve pricer + const newPricerConfig = { + baseLength: BigNumber.from("6"), + maxLength: BigNumber.from("35"), + maxPrice: parseEther("150"), + minPrice: parseEther("10"), + precisionMultiplier: precisionMultiDefault, + feePercentage: registrationFeePercDefault, + isSet: true, + }; + + const pricerTx = await zns.curvePricer.connect(user).setPriceConfig( + ethers.constants.HashZero, + newPricerConfig, + ); + + await expect(pricerTx).to.emit(zns.curvePricer, "PriceConfigSet").withArgs( + ethers.constants.HashZero, + newPricerConfig.maxPrice, + newPricerConfig.minPrice, + newPricerConfig.maxLength, + newPricerConfig.baseLength, + newPricerConfig.precisionMultiplier, + newPricerConfig.feePercentage, + ); + }); + + it("Confirms a user has funds and allowance for the Registrar", async () => { + const balance = await zns.meowToken.balanceOf(user.address); + expect(balance).to.eq(userBalanceInitial); + + const allowance = await zns.meowToken.allowance(user.address, zns.treasury.address); + expect(allowance).to.eq(ethers.constants.MaxUint256); + }); + + it("Should revert when initialize() without ADMIN_ROLE", async () => { + const userHasAdmin = await zns.accessController.hasRole(ADMIN_ROLE, user.address); + expect(userHasAdmin).to.be.false; + + const registrarFactory = new ZNSRootRegistrar__factory(user); + + const tx = upgrades.deployProxy( + registrarFactory, + [ + zns.accessController.address, + zns.registry.address, + zns.curvePricer.address, + zns.treasury.address, + zns.domainToken.address, + ], + { + kind: "uups", + } + ); + + await expect(tx).to.be.revertedWith(getAccessRevertMsg(user.address, ADMIN_ROLE)); + }); + + it("Should NOT initialize twice", async () => { + const tx = zns.rootRegistrar.connect(deployer).initialize( + zns.accessController.address, + randomUser.address, + randomUser.address, + randomUser.address, + randomUser.address, + ); + + await expect(tx).to.be.revertedWith("Initializable: contract is already initialized"); + }); + + describe("General functionality", () => { + it("#coreRegister() should revert if called by address without REGISTRAR_ROLE", async () => { + const isRegistrar = await zns.accessController.hasRole(REGISTRAR_ROLE, randomUser.address); + expect(isRegistrar).to.be.false; + + await expect( + zns.rootRegistrar.connect(randomUser).coreRegister({ + parentHash: ethers.constants.HashZero, + domainHash: ethers.constants.HashZero, + label: "randomname", + registrant: ethers.constants.AddressZero, + price: "0", + stakeFee: "0", + domainAddress: ethers.constants.AddressZero, + tokenURI: "", + isStakePayment: false, + }) + ).to.be.revertedWith( + getAccessRevertMsg(randomUser.address, REGISTRAR_ROLE) + ); + }); + + it("#isOwnerOf() returns correct bools", async () => { + const topLevelTx = await defaultRootRegistration({ + user, + zns, + domainName: defaultDomain, + }); + const domainHash = await getDomainHashFromReceipt(topLevelTx); + const tokenId = BigNumber.from(domainHash); + + const isOwnerOfBothUser = await zns.rootRegistrar.isOwnerOf( + domainHash, + user.address, + OwnerOf.BOTH + ); + expect(isOwnerOfBothUser).to.be.true; + + const isOwnerOfBothRandom = await zns.rootRegistrar.isOwnerOf( + domainHash, + randomUser.address, + OwnerOf.BOTH + ); + expect(isOwnerOfBothRandom).to.be.false; + + // transfer token + await zns.domainToken.connect(user).transferFrom(user.address, randomUser.address, tokenId); + const isOwnerOfTokenUser = await zns.rootRegistrar.isOwnerOf( + domainHash, + user.address, + OwnerOf.TOKEN + ); + expect(isOwnerOfTokenUser).to.be.false; + + const isOwnerOfTokenRandom = await zns.rootRegistrar.isOwnerOf( + domainHash, + randomUser.address, + OwnerOf.TOKEN + ); + expect(isOwnerOfTokenRandom).to.be.true; + + const isOwnerOfNameUser = await zns.rootRegistrar.isOwnerOf( + domainHash, + user.address, + OwnerOf.NAME + ); + expect(isOwnerOfNameUser).to.be.true; + + const isOwnerOfNameRandom = await zns.rootRegistrar.isOwnerOf( + domainHash, + randomUser.address, + OwnerOf.NAME + ); + expect(isOwnerOfNameRandom).to.be.false; + + await expect( + zns.rootRegistrar.isOwnerOf(domainHash, user.address, 3) + ).to.be.reverted; + }); + + it("#setSubRegistrar() should revert if called by address without ADMIN_ROLE", async () => { + const isAdmin = await zns.accessController.hasRole(ADMIN_ROLE, randomUser.address); + expect(isAdmin).to.be.false; + + await expect( + zns.rootRegistrar.connect(randomUser).setSubRegistrar(randomUser.address) + ).to.be.revertedWith( + getAccessRevertMsg(randomUser.address, ADMIN_ROLE) + ); + }); + + it("#setSubRegistrar() should set the correct address", async () => { + await zns.rootRegistrar.connect(admin).setSubRegistrar(randomUser.address); + + expect( + await zns.rootRegistrar.subRegistrar() + ).to.equal(randomUser.address); + }); + + it("#setSubRegistrar() should NOT set the address to zero address", async () => { + await expect( + zns.rootRegistrar.connect(admin).setSubRegistrar(ethers.constants.AddressZero) + ).to.be.revertedWith( + "ZNSRootRegistrar: subRegistrar_ is 0x0 address" + ); + }); + }); + + describe("Registers a root domain", () => { + it("Can NOT register a TLD with an empty name", async () => { + const emptyName = ""; + + await expect( + defaultRootRegistration({ + user: deployer, + zns, + domainName: emptyName, + }) + ).to.be.revertedWith(INVALID_LENGTH_ERR); + }); + + it("Can register a TLD with characters [a-z0-9-]", async () => { + const letters = "world"; + const lettersHash = hashDomainLabel(letters); + + const alphaNumeric = "0x0dwidler0x0"; + const alphaNumericHash = hashDomainLabel(alphaNumeric); + + const withHyphen = "0x0-dwidler-0x0"; + const withHyphenHash = hashDomainLabel(withHyphen); + + const tx1 = zns.rootRegistrar.connect(deployer).registerRootDomain( + letters, + ethers.constants.AddressZero, + defaultTokenURI, + distrConfigEmpty + ); + + await expect(tx1).to.emit(zns.rootRegistrar, "DomainRegistered").withArgs( + ethers.constants.HashZero, + lettersHash, + BigNumber.from(lettersHash), + letters, + deployer.address, + ethers.constants.AddressZero, + ); + + const tx2 = zns.rootRegistrar.connect(deployer).registerRootDomain( + alphaNumeric, + ethers.constants.AddressZero, + defaultTokenURI, + distrConfigEmpty + ); + + await expect(tx2).to.emit(zns.rootRegistrar, "DomainRegistered").withArgs( + ethers.constants.HashZero, + alphaNumericHash, + BigNumber.from(alphaNumericHash), + alphaNumeric, + deployer.address, + ethers.constants.AddressZero, + ); + + const tx3 = zns.rootRegistrar.connect(deployer).registerRootDomain( + withHyphen, + ethers.constants.AddressZero, + defaultTokenURI, + distrConfigEmpty + ); + + await expect(tx3).to.emit(zns.rootRegistrar, "DomainRegistered").withArgs( + ethers.constants.HashZero, + withHyphenHash, + BigNumber.from(withHyphenHash), + withHyphen, + deployer.address, + ethers.constants.AddressZero, + ); + }); + + it("Fails for domains that use any invalid character", async () => { + // Valid names must match the pattern [a-z0-9] + const nameA = "WILDER"; + const nameB = "!?w1Id3r!?"; + const nameC = "!%$#^*?!#👍3^29"; + const nameD = "wo.rld"; + + await expect( + defaultRootRegistration({ + user: deployer, + zns, + domainName: nameA, + }) + ).to.be.revertedWith(INVALID_NAME_ERR); + + await expect( + defaultRootRegistration({ + user: deployer, + zns, + domainName: nameB, + }) + ).to.be.revertedWith(INVALID_NAME_ERR); + + await expect( + defaultRootRegistration({ + user: deployer, + zns, + domainName: nameC, + }) + ).to.be.revertedWith(INVALID_NAME_ERR); + + await expect( + defaultRootRegistration({ + user: deployer, + zns, + domainName: nameD, + }) + ).to.be.revertedWith(INVALID_NAME_ERR); + }); + + // eslint-disable-next-line max-len + it("Successfully registers a domain without a resolver or resolver content and fires a #DomainRegistered event", async () => { + const tokenURI = "https://example.com/817c64af"; + const tx = await zns.rootRegistrar.connect(user).registerRootDomain( + defaultDomain, + ethers.constants.AddressZero, + tokenURI, + distrConfigEmpty + ); + + const hashFromTS = hashDomainLabel(defaultDomain); + + await expect(tx).to.emit(zns.rootRegistrar, "DomainRegistered").withArgs( + ethers.constants.HashZero, + hashFromTS, + BigNumber.from(hashFromTS), + defaultDomain, + user.address, + ethers.constants.AddressZero, + ); + + const tokenURISC = await zns.domainToken.tokenURI(hashFromTS); + expect(tokenURISC).to.eq(tokenURI); + }); + + it("Successfully registers a domain with distrConfig and adds it to state properly", async () => { + const distrConfig = { + pricerContract: zns.fixedPricer.address, + accessType: AccessType.OPEN, + paymentType: PaymentType.DIRECT, + }; + const tokenURI = "https://example.com/817c64af"; + + const tx = await zns.rootRegistrar.connect(user).registerRootDomain( + defaultDomain, + ethers.constants.AddressZero, + tokenURI, + distrConfig + ); + + const receipt = await tx.wait(0); + + const domainHash = await getDomainHashFromReceipt(receipt); + + const { + pricerContract, + accessType, + paymentType, + } = await zns.subRegistrar.distrConfigs(domainHash); + + expect(pricerContract).to.eq(distrConfig.pricerContract); + expect(paymentType).to.eq(distrConfig.paymentType); + expect(accessType).to.eq(distrConfig.accessType); + + const tokenURISC = await zns.domainToken.tokenURI(domainHash); + expect(tokenURISC).to.eq(tokenURI); + }); + + it("Stakes and saves the correct amount and token, takes the correct fee and sends fee to Zero Vault", async () => { + const balanceBeforeUser = await zns.meowToken.balanceOf(user.address); + const balanceBeforeVault = await zns.meowToken.balanceOf(zeroVault.address); + + // Deploy "wilder" with default configuration + const tx = await defaultRootRegistration({ + user, + zns, + domainName: defaultDomain, + }); + const domainHash = await getDomainHashFromReceipt(tx); + const { + totalPrice, + expectedPrice, + stakeFee, + } = getPriceObject(defaultDomain, priceConfigDefault); + + await checkBalance({ + token: zns.meowToken as IERC20, + balanceBefore: balanceBeforeUser, + userAddress: user.address, + target: totalPrice, + }); + + await checkBalance({ + token: zns.meowToken as IERC20, + balanceBefore: balanceBeforeVault, + userAddress: zeroVault.address, + target: stakeFee, + shouldDecrease: false, + }); + + const { amount: staked, token } = await zns.treasury.stakedForDomain(domainHash); + + expect(staked).to.eq(expectedPrice); + expect(token).to.eq(zns.meowToken.address); + }); + + it("Sets the correct data in Registry", async () => { + const tx = await defaultRootRegistration({ + user, + zns, + domainName: defaultDomain, + }); + + const namehashRef = hashDomainLabel(defaultDomain); + const domainHash = await getDomainHashFromReceipt(tx); + expect(domainHash).to.eq(namehashRef); + + const { + owner: ownerFromReg, + resolver: resolverFromReg, + } = await zns.registry.getDomainRecord(domainHash); + + expect(ownerFromReg).to.eq(user.address); + expect(resolverFromReg).to.eq(zns.addressResolver.address); + }); + + it("Fails when the user does not have enough funds", async () => { + const balance = await zns.meowToken.balanceOf(user.address); + await zns.meowToken.connect(user).transfer(randomUser.address, balance); + + const tx = defaultRootRegistration({ + user, + zns, + domainName: defaultDomain, + }); + await expect(tx).to.be.revertedWith("ERC20: transfer amount exceeds balance"); + }); + + it("Disallows creation of a duplicate domain", async () => { + await defaultRootRegistration({ + user, + zns, + domainName: defaultDomain, + }); + const failTx = defaultRootRegistration({ + user: deployer, + zns, + domainName: defaultDomain, + }); + + await expect(failTx).to.be.revertedWith("ZNSRootRegistrar: Domain already exists"); + }); + + it("Successfully registers a domain without resolver content", async () => { + const tx = zns.rootRegistrar.connect(user).registerRootDomain( + defaultDomain, + ethers.constants.AddressZero, + defaultTokenURI, + distrConfigEmpty + ); + + await expect(tx).to.not.be.reverted; + }); + + it("Records the correct domain hash", async () => { + const tx = await defaultRootRegistration({ + user, + zns, + domainName: defaultDomain, + }); + + const domainHash = await getDomainHashFromReceipt(tx); + + const exists = await zns.registry.exists(domainHash); + expect(exists).to.be.true; + expect(domainHash).to.eq(hashDomainLabel(defaultDomain)); + }); + + it("Creates and finds the correct tokenId", async () => { + const tx = await defaultRootRegistration({ + user, + zns, + domainName: defaultDomain, + }); + + const tokenId = await getTokenIdFromReceipt(tx); + const owner = await zns.domainToken.ownerOf(tokenId); + expect(owner).to.eq(user.address); + }); + + it("Resolves the correct address from the domain", async () => { + const tx = await defaultRootRegistration({ + user, + zns, + domainName: defaultDomain, + domainContent: zns.rootRegistrar.address, + }); + const domainHash = await getDomainHashFromReceipt(tx); + + const resolvedAddress = await zns.addressResolver.getAddress(domainHash); + expect(resolvedAddress).to.eq(zns.rootRegistrar.address); + }); + + it("Should NOT charge any tokens if price and/or stake fee is 0", async () => { + // set config on CurvePricer for the price to be 0 + await zns.curvePricer.connect(deployer).setMaxPrice(ethers.constants.HashZero, "0"); + await zns.curvePricer.connect(deployer).setMinPrice(ethers.constants.HashZero, "0"); + + const userBalanceBefore = await zns.meowToken.balanceOf(user.address); + const vaultBalanceBefore = await zns.meowToken.balanceOf(zeroVault.address); + + // register a domain + await zns.rootRegistrar.connect(user).registerRootDomain( + defaultDomain, + ethers.constants.AddressZero, + defaultTokenURI, + distrConfigEmpty + ); + + const userBalanceAfter = await zns.meowToken.balanceOf(user.address); + const vaultBalanceAfter = await zns.meowToken.balanceOf(zeroVault.address); + + expect(userBalanceBefore).to.eq(userBalanceAfter); + expect(vaultBalanceBefore).to.eq(vaultBalanceAfter); + + // check existence in Registry + const domainHash = hashDomainLabel(defaultDomain); + const exists = await zns.registry.exists(domainHash); + expect(exists).to.be.true; + + // make sure no transfers happened + const transferEventFilter = zns.meowToken.filters.Transfer( + user.address, + ); + const events = await zns.meowToken.queryFilter(transferEventFilter); + expect(events.length).to.eq(0); + }); + }); + + describe("Reclaiming Domains", () => { + it("Can reclaim name/stake if Token is owned", async () => { + // Register Top level + const topLevelTx = await defaultRootRegistration({ user: deployer, zns, domainName: defaultDomain }); + const domainHash = await getDomainHashFromReceipt(topLevelTx); + const tokenId = await getTokenIdFromReceipt(topLevelTx); + const { amount: staked, token } = await zns.treasury.stakedForDomain(domainHash); + + // Transfer the domain token + await zns.domainToken.connect(deployer).transferFrom(deployer.address, user.address, tokenId); + + // Verify owner in registry + const originalOwner = await zns.registry.connect(deployer).getDomainOwner(domainHash); + expect(originalOwner).to.equal(deployer.address); + + // Reclaim the Domain + await zns.rootRegistrar.connect(user).reclaimDomain(domainHash); + + // Verify domain token is still owned + const owner = await zns.domainToken.connect(user).ownerOf(tokenId); + expect(owner).to.equal(user.address); + + // Verify domain is owned in registry + const registryOwner = await zns.registry.connect(user).getDomainOwner(domainHash); + expect(registryOwner).to.equal(user.address); + + // Verify same amount is staked + const { amount: stakedAfterReclaim, token: tokenAfterReclaim } = await zns.treasury.stakedForDomain(domainHash); + expect(staked).to.equal(stakedAfterReclaim); + expect(tokenAfterReclaim).to.equal(zns.meowToken.address); + expect(token).to.equal(tokenAfterReclaim); + }); + + it("Reclaiming domain token emits DomainReclaimed event", async () => { + const topLevelTx = await defaultRootRegistration({ user: deployer, zns, domainName: defaultDomain }); + const domainHash = await getDomainHashFromReceipt(topLevelTx); + const tokenId = await getTokenIdFromReceipt(topLevelTx); + + // Transfer the domain token + await zns.domainToken.connect(deployer).transferFrom(deployer.address, user.address, tokenId); + // Reclaim the Domain + const tx = await zns.rootRegistrar.connect(user).reclaimDomain(domainHash); + const receipt = await tx.wait(0); + + // Verify Transfer event is emitted + expect(receipt.events?.[1].event).to.eq("DomainReclaimed"); + expect(receipt.events?.[1].args?.domainHash).to.eq( + domainHash + ); + expect(receipt.events?.[1].args?.registrant).to.eq( + user.address + ); + }); + + it("Cannot reclaim name/stake if token is not owned", async () => { + const topLevelTx = await defaultRootRegistration({ user: deployer, zns, domainName: defaultDomain }); + const domainHash = await getDomainHashFromReceipt(topLevelTx); + // Reclaim the Domain + const tx = zns.rootRegistrar.connect(user).reclaimDomain(domainHash); + + // Verify Domain is not reclaimed + await expect(tx).to.be.revertedWith(NOT_TOKEN_OWNER_RAR_ERR); + + // Verify domain is not owned in registrar + const registryOwner = await zns.registry.connect(user).getDomainOwner(domainHash); + expect(registryOwner).to.equal(deployer.address); + }); + + it("Cannot reclaim if domain does not exist", async () => { + const domainHash = "0xd34cfa279afd55afc6aa9c00aa5d01df60179840a93d10eed730058b8dd4146c"; + // Reclaim the Domain + const tx = zns.rootRegistrar.connect(user).reclaimDomain(domainHash); + + // Verify Domain is not reclaimed + await expect(tx).to.be.revertedWith(INVALID_TOKENID_ERC_ERR); + }); + + it("Domain Token can be reclaimed, transferred, and then reclaimed again", async () => { + // Register Top level + const topLevelTx = await defaultRootRegistration({ user: deployer, zns, domainName: defaultDomain }); + const domainHash = await getDomainHashFromReceipt(topLevelTx); + const tokenId = await getTokenIdFromReceipt(topLevelTx); + const { amount: staked, token } = await zns.treasury.stakedForDomain(domainHash); + + // Transfer the domain token + await zns.domainToken.connect(deployer).transferFrom(deployer.address, user.address, tokenId); + + // Reclaim the Domain + await zns.rootRegistrar.connect(user).reclaimDomain(domainHash); + // Verify domain token is still owned + let owner = await zns.domainToken.connect(user).ownerOf(tokenId); + expect(owner).to.equal(user.address); + + // Transfer the domain token back + await zns.domainToken.connect(user).transferFrom(user.address, deployer.address, tokenId); + + // Reclaim the Domain again + await zns.rootRegistrar.connect(deployer).reclaimDomain(domainHash); + + // Verify domain token is owned + owner = await zns.domainToken.connect(deployer).ownerOf(tokenId); + expect(owner).to.equal(deployer.address); + + // Verify domain is owned in registrar + const registryOwner = await zns.registry.connect(deployer).getDomainOwner(domainHash); + expect(registryOwner).to.equal(deployer.address); + + // Verify same amount is staked + const { amount: stakedAfterReclaim, token: tokenAfterReclaim } = await zns.treasury.stakedForDomain(domainHash); + expect(staked).to.equal(stakedAfterReclaim); + expect(tokenAfterReclaim).to.equal(zns.meowToken.address); + expect(token).to.equal(tokenAfterReclaim); + }); + + it("Can revoke and unstake after reclaiming", async () => { + // Verify Balance + const balance = await zns.meowToken.balanceOf(user.address); + expect(balance).to.eq(userBalanceInitial); + + // Register Top level + const topLevelTx = await defaultRootRegistration({ user: deployer, zns, domainName: defaultDomain }); + const domainHash = await getDomainHashFromReceipt(topLevelTx); + const tokenId = await getTokenIdFromReceipt(topLevelTx); + + // Validated staked values + const { + expectedPrice: expectedStaked, + } = getPriceObject(defaultDomain, priceConfigDefault); + const { amount: staked, token } = await zns.treasury.stakedForDomain(domainHash); + expect(staked).to.eq(expectedStaked); + expect(token).to.eq(zns.meowToken.address); + + // Transfer the domain token + await zns.domainToken.connect(deployer).transferFrom(deployer.address, user.address, tokenId); + + // Reclaim the Domain + await zns.rootRegistrar.connect(user).reclaimDomain(domainHash); + + // Revoke the Domain + await zns.rootRegistrar.connect(user).revokeDomain(domainHash); + + // Validated funds are unstaked + const { amount: finalstaked, token: finalToken } = await zns.treasury.stakedForDomain(domainHash); + expect(finalstaked).to.equal(ethers.BigNumber.from("0")); + expect(finalToken).to.equal(ethers.constants.AddressZero); + + // Verify final balances + const computedFinalBalance = balance.add(staked); + const finalBalance = await zns.meowToken.balanceOf(user.address); + expect(computedFinalBalance).to.equal(finalBalance); + }); + }); + + describe("Revoking Domains", () => { + it("Revokes a Top level Domain, locks distribution and removes mintlist", async () => { + // Register Top level + const topLevelTx = await defaultRootRegistration({ + user, + zns, + domainName: defaultDomain, + distrConfig: { + pricerContract: zns.fixedPricer.address, + paymentType: PaymentType.DIRECT, + accessType: AccessType.OPEN, + }, + }); + + const domainHash = await getDomainHashFromReceipt(topLevelTx); + + // add mintlist to check revocation + await zns.subRegistrar.connect(user).updateMintlistForDomain( + domainHash, + [user.address, zeroVault.address], + [true, true] + ); + + const ogPrice = BigNumber.from(135); + await zns.fixedPricer.connect(user).setPriceConfig( + domainHash, + { + price: ogPrice, + feePercentage: BigNumber.from(0), + isSet: true, + } + ); + expect(await zns.fixedPricer.getPrice(domainHash, defaultDomain, false)).to.eq(ogPrice); + + const tokenId = await getTokenIdFromReceipt(topLevelTx); + + // Revoke the domain and then verify + await zns.rootRegistrar.connect(user).revokeDomain(domainHash); + + // Verify token has been burned + const ownerOfTx = zns.domainToken.connect(user).ownerOf(tokenId); + await expect(ownerOfTx).to.be.revertedWith( + INVALID_TOKENID_ERC_ERR + ); + + // Verify Domain Record Deleted + const exists = await zns.registry.exists(domainHash); + expect(exists).to.be.false; + + // validate access type has been set to LOCKED + const { accessType } = await zns.subRegistrar.distrConfigs(domainHash); + expect(accessType).to.eq(AccessType.LOCKED); + + // validate mintlist has been removed + expect(await zns.subRegistrar.isMintlistedForDomain(domainHash, user.address)).to.be.false; + expect(await zns.subRegistrar.isMintlistedForDomain(domainHash, zeroVault.address)).to.be.false; + }); + + it("Cannot revoke a domain that doesnt exist", async () => { + // Register Top level + const fakeHash = "0xd34cfa279afd55afc6aa9c00aa5d01df60179840a93d10eed730058b8dd4146c"; + const exists = await zns.registry.exists(fakeHash); + expect(exists).to.be.false; + + // Verify transaction is reverted + const tx = zns.rootRegistrar.connect(user).revokeDomain(fakeHash); + await expect(tx).to.be.revertedWith(NOT_BOTH_OWNER_RAR_ERR); + }); + + it("Revoking domain unstakes", async () => { + // Verify Balance + const balance = await zns.meowToken.balanceOf(user.address); + expect(balance).to.eq(userBalanceInitial); + + // Register Top level + const tx = await defaultRootRegistration({ user, zns, domainName: defaultDomain }); + const domainHash = await getDomainHashFromReceipt(tx); + + // Validated staked values + const { + expectedPrice: expectedStaked, + stakeFee: expectedStakeFee, + } = getPriceObject(defaultDomain, priceConfigDefault); + const { amount: staked, token } = await zns.treasury.stakedForDomain(domainHash); + expect(staked).to.eq(expectedStaked); + expect(token).to.eq(zns.meowToken.address); + + // Get balance after staking + const balanceAfterStaking = await zns.meowToken.balanceOf(user.address); + + // Revoke the domain + await zns.rootRegistrar.connect(user).revokeDomain(domainHash); + + // Validated funds are unstaked + const { amount: finalstaked, token: finalToken } = await zns.treasury.stakedForDomain(domainHash); + expect(finalstaked).to.equal(ethers.BigNumber.from("0")); + expect(finalToken).to.equal(ethers.constants.AddressZero); + + // Verify final balances + const computedBalanceAfterStaking = balanceAfterStaking.add(staked); + const balanceMinusFee = balance.sub(expectedStakeFee); + expect(computedBalanceAfterStaking).to.equal(balanceMinusFee); + const finalBalance = await zns.meowToken.balanceOf(user.address); + expect(computedBalanceAfterStaking).to.equal(finalBalance); + }); + + it("Cannot revoke if Name is owned by another user", async () => { + // Register Top level + const topLevelTx = await defaultRootRegistration({ user: deployer, zns, domainName: defaultDomain }); + const parentDomainHash = await getDomainHashFromReceipt(topLevelTx); + const owner = await zns.registry.connect(user).getDomainOwner(parentDomainHash); + expect(owner).to.not.equal(user.address); + + // Try to revoke domain + const tx = zns.rootRegistrar.connect(user).revokeDomain(parentDomainHash); + await expect(tx).to.be.revertedWith(NOT_BOTH_OWNER_RAR_ERR); + }); + + it("No one can revoke if Token and Name have different owners", async () => { + // Register Top level + const topLevelTx = await defaultRootRegistration({ user: deployer, zns, domainName: defaultDomain }); + const parentDomainHash = await getDomainHashFromReceipt(topLevelTx); + const owner = await zns.registry.connect(user).getDomainOwner(parentDomainHash); + expect(owner).to.not.equal(user.address); + + const tokenId = BigNumber.from(parentDomainHash); + + await zns.domainToken.transferFrom(deployer.address, user.address, tokenId); + + // Try to revoke domain as a new owner of the token + const tx = zns.rootRegistrar.connect(user).revokeDomain(parentDomainHash); + await expect(tx).to.be.revertedWith(NOT_BOTH_OWNER_RAR_ERR); + + const tx2 = zns.rootRegistrar.connect(deployer).revokeDomain(parentDomainHash); + await expect(tx2).to.be.revertedWith(NOT_BOTH_OWNER_RAR_ERR); + }); + + it("After domain has been revoked, an old operator can NOT access Registry", async () => { + // Register Top level + const tx = await defaultRootRegistration({ user, zns, domainName: defaultDomain }); + const domainHash = await getDomainHashFromReceipt(tx); + + // assign an operator + await zns.registry.connect(user).setOwnersOperator(operator.address, true); + + // Revoke the domain + await zns.rootRegistrar.connect(user).revokeDomain(domainHash); + + // check operator access to the revoked domain + const tx2 = zns.registry + .connect(operator) + .updateDomainOwner( + domainHash, + operator.address + ); + await expect(tx2).to.be.revertedWith( + ONLY_OWNER_REGISTRAR_REG_ERR + ); + + const tx3 = zns.registry + .connect(operator) + .updateDomainRecord( + domainHash, + user.address, + operator.address + ); + await expect(tx3).to.be.revertedWith( + ONLY_NAME_OWNER_REG_ERR + ); + + const tx4 = zns.registry + .connect(operator) + .updateDomainResolver( + domainHash, + zeroVault.address + ); + await expect(tx4).to.be.revertedWith( + NOT_AUTHORIZED_REG_ERR + ); + }); + }); + + describe("State Setters", () => { + describe("#setAccessController", () => { + it("Should set AccessController and fire AccessControllerSet event", async () => { + const currentAC = await zns.rootRegistrar.getAccessController(); + const tx = await zns.rootRegistrar.connect(deployer).setAccessController(randomUser.address); + const newAC = await zns.rootRegistrar.getAccessController(); + + await expect(tx).to.emit(zns.rootRegistrar, "AccessControllerSet").withArgs(randomUser.address); + + expect(newAC).to.equal(randomUser.address); + expect(currentAC).to.not.equal(newAC); + }); + + it("Should revert if not called by ADMIN", async () => { + const tx = zns.rootRegistrar.connect(user).setAccessController(randomUser.address); + await expect(tx).to.be.revertedWith( + getAccessRevertMsg(user.address, ADMIN_ROLE) + ); + }); + + it("Should revert if new AccessController is address zero", async () => { + const tx = zns.rootRegistrar.connect(deployer).setAccessController(ethers.constants.AddressZero); + await expect(tx).to.be.revertedWith("AC: _accessController is 0x0 address"); + }); + }); + + describe("#setRegistry", () => { + it("Should set ZNSRegistry and fire RegistrySet event", async () => { + const currentRegistry = await zns.rootRegistrar.registry(); + const tx = await zns.rootRegistrar.connect(deployer).setRegistry(randomUser.address); + const newRegistry = await zns.rootRegistrar.registry(); + + await expect(tx).to.emit(zns.rootRegistrar, "RegistrySet").withArgs(randomUser.address); + + expect(newRegistry).to.equal(randomUser.address); + expect(currentRegistry).to.not.equal(newRegistry); + }); + + it("Should revert if not called by ADMIN", async () => { + const tx = zns.rootRegistrar.connect(user).setRegistry(randomUser.address); + await expect(tx).to.be.revertedWith( + getAccessRevertMsg(user.address, ADMIN_ROLE) + ); + }); + + it("Should revert if ZNSRegistry is address zero", async () => { + const tx = zns.rootRegistrar.connect(deployer).setRegistry(ethers.constants.AddressZero); + await expect(tx).to.be.revertedWith("ARegistryWired: _registry can not be 0x0 address"); + }); + }); + + describe("#setTreasury", () => { + it("Should set Treasury and fire TreasurySet event", async () => { + const currentTreasury = await zns.rootRegistrar.treasury(); + const tx = await zns.rootRegistrar.connect(deployer).setTreasury(randomUser.address); + const newTreasury = await zns.rootRegistrar.treasury(); + + await expect(tx).to.emit(zns.rootRegistrar, "TreasurySet").withArgs(randomUser.address); + + expect(newTreasury).to.equal(randomUser.address); + expect(currentTreasury).to.not.equal(newTreasury); + }); + + it("Should revert if not called by ADMIN", async () => { + const tx = zns.rootRegistrar.connect(user).setTreasury(randomUser.address); + await expect(tx).to.be.revertedWith( + getAccessRevertMsg(user.address, ADMIN_ROLE) + ); + }); + + it("Should revert if Treasury is address zero", async () => { + const tx = zns.rootRegistrar.connect(deployer).setTreasury(ethers.constants.AddressZero); + await expect(tx).to.be.revertedWith("ZNSRootRegistrar: treasury_ is 0x0 address"); + }); + }); + + describe("#setDomainToken", () => { + it("Should set DomainToken and fire DomainTokenSet event", async () => { + const currentToken = await zns.rootRegistrar.domainToken(); + const tx = await zns.rootRegistrar.connect(deployer).setDomainToken(randomUser.address); + const newToken = await zns.rootRegistrar.domainToken(); + + await expect(tx).to.emit(zns.rootRegistrar, "DomainTokenSet").withArgs(randomUser.address); + + expect(newToken).to.equal(randomUser.address); + expect(currentToken).to.not.equal(newToken); + }); + + it("Should revert if not called by ADMIN", async () => { + const tx = zns.rootRegistrar.connect(user).setDomainToken(randomUser.address); + await expect(tx).to.be.revertedWith( + getAccessRevertMsg(user.address, ADMIN_ROLE) + ); + }); + + it("Should revert if DomainToken is address zero", async () => { + const tx = zns.rootRegistrar.connect(deployer).setDomainToken(ethers.constants.AddressZero); + await expect(tx).to.be.revertedWith("ZNSRootRegistrar: domainToken_ is 0x0 address"); + }); + }); + }); + + describe("UUPS", () => { + it("Allows an authorized user to upgrade the contract", async () => { + // Confirm deployer has the correct role first + await expect(zns.accessController.checkGovernor(deployer.address)).to.not.be.reverted; + + const registrarFactory = new ZNSRootRegistrar__factory(deployer); + const registrar = await registrarFactory.deploy(); + await registrar.deployed(); + + const upgradeTx = zns.rootRegistrar.connect(deployer).upgradeTo(registrar.address); + await expect(upgradeTx).to.not.be.reverted; + }); + + it("Fails to upgrade when an unauthorized users calls", async () => { + const registrarFactory = new ZNSRootRegistrar__factory(deployer); + const registrar = await registrarFactory.deploy(); + await registrar.deployed(); + + const tx = zns.rootRegistrar.connect(randomUser).upgradeTo(registrar.address); + + await expect(tx).to.be.revertedWith( + getAccessRevertMsg(randomUser.address, GOVERNOR_ROLE) + ); + }); + + it("Verifies that variable values are not changed in the upgrade process", async () => { + // Confirm deployer has the correct role first + await expect(zns.accessController.checkGovernor(deployer.address)).to.not.be.reverted; + + const registrarFactory = new ZNSRootRegistrarUpgradeMock__factory(deployer); + const registrar = await registrarFactory.deploy(); + await registrar.deployed(); + + const domainName = "world"; + const domainHash = hashDomainLabel(domainName); + + await zns.meowToken.connect(randomUser).approve(zns.treasury.address, ethers.constants.MaxUint256); + await zns.meowToken.mint(randomUser.address, priceConfigDefault.maxPrice); + + await zns.rootRegistrar.connect(randomUser).registerRootDomain( + domainName, + randomUser.address, + defaultTokenURI, + distrConfigEmpty + ); + + + const contractCalls = [ + zns.rootRegistrar.getAccessController(), + zns.rootRegistrar.registry(), + zns.rootRegistrar.treasury(), + zns.rootRegistrar.domainToken(), + zns.registry.exists(domainHash), + zns.treasury.stakedForDomain(domainHash), + zns.domainToken.name(), + zns.domainToken.symbol(), + zns.curvePricer.getPrice(ethers.constants.HashZero, domainName, false), + ]; + + await validateUpgrade(deployer, zns.rootRegistrar, registrar, registrarFactory, contractCalls); + }); + }); +}); diff --git a/test/deploy-campaign-smoke.test.ts b/test/deploy-campaign-smoke.test.ts deleted file mode 100644 index 82364c2ed..000000000 --- a/test/deploy-campaign-smoke.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { DeployCampaign } from "../src/deploy/campaign/deploy-campaign"; -import { HardhatDeployer } from "../src/deploy/deployer/hardhat-deployer"; -import * as hre from "hardhat"; -import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; -import { - defaultRoyaltyFraction, - GOVERNOR_ROLE, - priceConfigDefault, registrationFeePercDefault, - ZNS_DOMAIN_TOKEN_NAME, - ZNS_DOMAIN_TOKEN_SYMBOL, -} from "./helpers"; -import { expect } from "chai"; -import { FileStorageAdapter } from "../src/deploy/storage/file-storage"; -import { znsNames } from "../src/deploy/constants"; -import { - MeowTokenMockDM, - ZNSAccessControllerDM, - ZNSAddressResolverDM, - ZNSDomainTokenDM, ZNSCurvePricerDM, ZNSRootRegistrarDM, - ZNSRegistryDM, ZNSTreasuryDM, -} from "../src/deploy/missions/contracts"; - - -// TODO dep: possibly remove this whole test. it was added as a first check when prototyping -describe("Deploy Campaign Smoke Test", () => { - let deployAdmin : SignerWithAddress; - let admin : SignerWithAddress; - // eslint-disable-next-line @typescript-eslint/no-unused-vars - let user : SignerWithAddress; - let zeroVault : SignerWithAddress; - - it("Deploy", async () => { - [deployAdmin, admin, zeroVault, user] = await hre.ethers.getSigners(); - - const deployer = new HardhatDeployer(); - const dbAdapterIn = new FileStorageAdapter(console); - const config = { - deployAdmin, - governorAddresses: [ deployAdmin.address ], - adminAddresses: [ deployAdmin.address, admin.address ], - domainToken: { - name: ZNS_DOMAIN_TOKEN_NAME, - symbol: ZNS_DOMAIN_TOKEN_SYMBOL, - defaultRoyaltyReceiver: deployAdmin.address, - defaultRoyaltyFraction, - }, - rootPriceConfig: priceConfigDefault, - registrationFee: registrationFeePercDefault, - zeroVaultAddress: zeroVault.address, - }; - - const campaign = new DeployCampaign({ - missions: [ - ZNSAccessControllerDM, - ZNSRegistryDM, - ZNSDomainTokenDM, - MeowTokenMockDM, - ZNSAddressResolverDM, - ZNSCurvePricerDM, - ZNSTreasuryDM, - ZNSRootRegistrarDM, - ], - deployer, - dbAdapter: dbAdapterIn, - logger: console, - config, - }); - - await campaign.execute(); - - const { - accessController, - registry, - dbAdapter, - } = campaign; - const isGovernor = await accessController.hasRole(GOVERNOR_ROLE, deployAdmin.address); - expect(isGovernor).to.be.true; - - const acFromRegistry = await registry.getAccessController(); - expect(acFromRegistry).to.equal(accessController.address); - - const contractDbDoc = await dbAdapter.getContract( - znsNames.accessController.contract - ); - const contract = new hre.ethers.Contract( - contractDbDoc!.address, - contractDbDoc!.abi, - deployAdmin - ); - const isGovernor2 = await contract.hasRole(GOVERNOR_ROLE, deployAdmin.address); - console.log("isGovernor2", isGovernor2); - }); -}); diff --git a/test/helpers/types.ts b/test/helpers/types.ts index bfb6210ee..046bc1757 100644 --- a/test/helpers/types.ts +++ b/test/helpers/types.ts @@ -25,7 +25,6 @@ import { ZNSTreasury, ZNSTreasuryUpgradeMock, ZNSTreasuryUpgradeMock__factory, - MeowToken, MeowTokenMock, } from "../../typechain"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; diff --git a/test/mocha-global.ts b/test/mocha-global.ts new file mode 100644 index 000000000..47592e86b --- /dev/null +++ b/test/mocha-global.ts @@ -0,0 +1,13 @@ +import { getMongoAdapter } from "../src/deploy/db/mongo-adapter/get-adapter"; + + +export const mochaGlobalSetup = async () => { + await getMongoAdapter(); +}; + +export const mochaGlobalTeardown = async () => { + const mongoAdapter = await getMongoAdapter(); + // the next line can be commented out to leave the DB after test to manually test + await mongoAdapter.dropDB(); + await mongoAdapter.close(); +}; diff --git a/yarn.lock b/yarn.lock index 97925376d..343c30314 100644 --- a/yarn.lock +++ b/yarn.lock @@ -26,9 +26,9 @@ tslib "^1.11.1" "@aws-sdk/types@^3.1.0": - version "3.433.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.433.0.tgz#0f94eae2a4a3525ca872c9ab04e143c01806d755" - integrity sha512-0jEE2mSrNDd8VGFjTc1otYrwYPIkzZJEIK90ZxisKvQ/EURGBhNzWn7ejWB9XCMFT6XumYLBR0V9qq5UPisWtA== + version "3.449.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.449.0.tgz#0da2f8cdb344fbe9846de371a04c6dde1bcaf83f" + integrity sha512-tSQPAvknheB6XnRoc+AuEgdzn2KhY447hddeVW0Mbg8Yl9es4u4TKVINloKDEyUrCKhB/1f93Hb5uJkPe/e/Ww== dependencies: "@smithy/types" "^2.4.0" tslib "^2.5.0" @@ -950,6 +950,13 @@ tweetnacl "^1.0.3" tweetnacl-util "^0.15.1" +"@mongodb-js/saslprep@^1.1.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@mongodb-js/saslprep/-/saslprep-1.1.1.tgz#9a6c2516bc9188672c4d953ec99760ba49970da7" + integrity sha512-t7c5K033joZZMspnHg/gWPE4kandgc2OxE74aYOtGKfgB9VPuVJPix0H6fhmm2erj5PBJ21mqcx34lpIGtUCsQ== + dependencies: + sparse-bitfield "^3.0.3" + "@noble/curves@1.1.0", "@noble/curves@~1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.1.0.tgz#f13fc667c89184bc04cccb9b11e8e7bae27d8c3d" @@ -1457,11 +1464,11 @@ integrity sha512-8li32fUDUeml/ACRp/njCWTsk5t17cfTM1jp9n08pBrqs5cDFJubtjsSnuz56r5Tad6jdEPJld7LxNp9dNcyjQ== "@octokit/plugin-paginate-rest@^9.0.0": - version "9.1.2" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.1.2.tgz#3d6196db8463bd0d935f4c9bc78de6325c95cbb8" - integrity sha512-euDbNV6fxX6btsCDnZoZM4vw3zO1nj1Z7TskHAulO6mZ9lHoFTpwll6farf+wh31mlBabgU81bBYdflp0GLVAQ== + version "9.1.3" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.1.3.tgz#291ffd15243dbbf18e80feb216cca82b660b6710" + integrity sha512-gm4KmW+pdAfCO5cXJyRZnNfnPE9r6OGpRG8JZpI0eSo1XVk7LXoRcdS7aP4L9azdV0ncHazsLAI0knKjr+snPg== dependencies: - "@octokit/types" "^12.1.1" + "@octokit/types" "^12.2.0" "@octokit/plugin-retry@^6.0.0": version "6.0.1" @@ -1490,9 +1497,9 @@ once "^1.4.0" "@octokit/request@^8.0.1", "@octokit/request@^8.0.2": - version "8.1.4" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-8.1.4.tgz#12dfaebdb2ea375eaabb41d39d45182531ac2857" - integrity sha512-M0aaFfpGPEKrg7XoA/gwgRvc9MSXHRO2Ioki1qrPDbl1e9YhjIwVoHE7HIKmv/m3idzldj//xBujcFNqGX6ENA== + version "8.1.5" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-8.1.5.tgz#902ae9565117a1dc410d10b5dbc44c4d27a89b71" + integrity sha512-zVKbNbX1xUluD9ZR4/tPs1yuYrK9xeh5fGZUXA6u04XGsTvomg0YO8/ZUC0FqAd49hAOEMFPAVUTh+2lBhOhLA== dependencies: "@octokit/endpoint" "^9.0.0" "@octokit/request-error" "^5.0.0" @@ -1500,10 +1507,10 @@ is-plain-object "^5.0.0" universal-user-agent "^6.0.0" -"@octokit/types@^12.0.0", "@octokit/types@^12.1.1": - version "12.1.1" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-12.1.1.tgz#376726d8435625b3a1b6fcd8cd3e1b03ade939dc" - integrity sha512-qnJTldJ1NyGT5MTsCg/Zi+y2IFHZ1Jo5+njNCjJ9FcainV7LjuHgmB697kA0g4MjZeDAJsM3B45iqCVsCLVFZg== +"@octokit/types@^12.0.0", "@octokit/types@^12.2.0": + version "12.2.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-12.2.0.tgz#c21044ec70c5e2222043bcbe7fe2c3448a13df31" + integrity sha512-ZkdHqHJdifVndN7Pha10+qrgAjy3AcG//Vmjr/o5UFuTiYCcMhqDj39Yr9VM9zJ/42KO2xAYhV7cvLnLI9Kvwg== dependencies: "@octokit/openapi-types" "^19.0.2" @@ -1656,9 +1663,9 @@ p-reduce "^2.0.0" "@semantic-release/github@^9.0.0": - version "9.2.1" - resolved "https://registry.yarnpkg.com/@semantic-release/github/-/github-9.2.1.tgz#031a617d55f9a78ef5fc2dab4bb6e84d84b8cd37" - integrity sha512-fEn9uOe6jwWR6ro2Wh6YNBCBuZ5lRi8Myz+1j3KDTSt8OuUGlpVM4lFac/0bDrql2NOKrIEAMGCfWb9WMIdzIg== + version "9.2.3" + resolved "https://registry.yarnpkg.com/@semantic-release/github/-/github-9.2.3.tgz#d8eeba7c00bf3145cee1bb9b60940d475af3e9f9" + integrity sha512-FAjXb1F84CVI6IG8fWi+XS9ErYD+s3MHkP03zBa3+GyUrV4kqwYu/WPppIciHxujGFR51SAWPkOY5rnH6ZlrxA== dependencies: "@octokit/core" "^5.0.0" "@octokit/plugin-paginate-rest" "^9.0.0" @@ -1668,7 +1675,7 @@ aggregate-error "^5.0.0" debug "^4.3.4" dir-glob "^3.0.1" - globby "^13.1.4" + globby "^14.0.0" http-proxy-agent "^7.0.0" https-proxy-agent "^7.0.0" issue-parser "^6.0.0" @@ -1809,6 +1816,16 @@ "@sigstore/protobuf-specs" "^0.2.0" tuf-js "^1.1.7" +"@sindresorhus/is@^5.2.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-5.6.0.tgz#41dd6093d34652cddb5d5bdeee04eafc33826668" + integrity sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g== + +"@sindresorhus/merge-streams@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/merge-streams/-/merge-streams-1.0.0.tgz#9cd84cc15bc865a5ca35fcaae198eb899f7b5c90" + integrity sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw== + "@smithy/types@^2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@smithy/types/-/types-2.4.0.tgz#ed35e429e3ea3d089c68ed1bf951d0ccbdf2692e" @@ -1816,17 +1833,31 @@ dependencies: tslib "^2.5.0" -"@solidity-parser/parser@0.16.0", "@solidity-parser/parser@^0.14.0", "@solidity-parser/parser@^0.16.0": - version "0.16.0" - resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.16.0.tgz#1fb418c816ca1fc3a1e94b08bcfe623ec4e1add4" - integrity sha512-ESipEcHyRHg4Np4SqBCfcXwyxxna1DgFVz69bgpLV8vzl/NP1DtcKsJ4dJZXWQhY/Z4J2LeKBiOkOVZn9ct33Q== +"@solidity-parser/parser@^0.14.0": + version "0.14.5" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.5.tgz#87bc3cc7b068e08195c219c91cd8ddff5ef1a804" + integrity sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg== dependencies: antlr4ts "^0.5.0-alpha.4" +"@solidity-parser/parser@^0.16.0": + version "0.16.2" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.16.2.tgz#42cb1e3d88b3e8029b0c9befff00b634cd92d2fa" + integrity sha512-PI9NfoA3P8XK2VBkK5oIfRgKDsicwDZfkVq9ZTBCQYGOP1N2owgY2dyLGyU5/J/hQs8KRk55kdmvTLjy3Mu3vg== + dependencies: + antlr4ts "^0.5.0-alpha.4" + +"@szmarczak/http-timer@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-5.0.1.tgz#c7c1bf1141cdd4751b0399c8fc7b8b664cd5be3a" + integrity sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw== + dependencies: + defer-to-connect "^2.0.1" + "@tenderly/hardhat-tenderly@^1.7.4": - version "1.7.7" - resolved "https://registry.yarnpkg.com/@tenderly/hardhat-tenderly/-/hardhat-tenderly-1.7.7.tgz#b320ee6bea3779e4781eb0299a3a816cbdc83def" - integrity sha512-p/jLzRPpoD7J0LGvUFEQjgniDzmP5AzfTgy41qqzyjhOsW0voe7wZI8lXjadl5MEr7rAXN1iH3VncT13qG6+Zw== + version "1.8.0" + resolved "https://registry.yarnpkg.com/@tenderly/hardhat-tenderly/-/hardhat-tenderly-1.8.0.tgz#3c77f3a8bba4d801cd3d6a98dfec2abc9359fbc2" + integrity sha512-HzyYsFZEXVALz+vDn1XesvaqrSDr3vYqlMd/A+r6pi6er1dFRlbUPW5mdalPQsbPOsBIO+PXjOlrM7mGgBdYEQ== dependencies: "@ethersproject/bignumber" "^5.7.0" "@nomiclabs/hardhat-ethers" "^2.1.1" @@ -1835,7 +1866,7 @@ fs-extra "^10.1.0" hardhat-deploy "^0.11.14" js-yaml "^4.1.0" - tenderly "^0.5.3" + tenderly "^0.6.0" tslog "^4.3.1" "@tootallnate/once@2": @@ -1893,9 +1924,9 @@ lodash "^4.17.15" "@types/async-eventemitter@^0.2.1": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@types/async-eventemitter/-/async-eventemitter-0.2.3.tgz#a825a14f9e0a95edb078a49d3533ee3441591cde" - integrity sha512-QHcih+LsYHY+ODMMTh1BoBC2f95HqkFzf7aEnre9xxkroCglpix7ZGKZI56AnwAipNoHLbIs4Ft9xTB5LFYslQ== + version "0.2.4" + resolved "https://registry.yarnpkg.com/@types/async-eventemitter/-/async-eventemitter-0.2.4.tgz#2f26d81e515a30ec32aa31a53da0469948f5f00b" + integrity sha512-2Bq61VD01kgLf1XkK2xPtoBcu7fgn/km5JyEX9v0BlG5VQBzA+BlF9umFk+8gR8S4+eK7MgDY2oyVZCu6ar3Jw== dependencies: "@types/events" "*" @@ -1907,23 +1938,23 @@ "@types/node" "*" "@types/bn.js@^5.1.0": - version "5.1.4" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.4.tgz#0853d5f92dfdbc8fe1ae60700411a60845fa7d27" - integrity sha512-ZtBd9L8hVtoBpPMSWfbwjC4dhQtJdlPS+e1A0Rydb7vg7bDcUwiRklPx24sMYtXcmAMST/k0Wze7JLbNU/5SkA== + version "5.1.5" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.5.tgz#2e0dacdcce2c0f16b905d20ff87aedbc6f7b4bf0" + integrity sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A== dependencies: "@types/node" "*" "@types/chai-as-promised@^7.1.3": - version "7.1.7" - resolved "https://registry.yarnpkg.com/@types/chai-as-promised/-/chai-as-promised-7.1.7.tgz#fd16a981ba9542c83d4e1d2f40c7899aae82aa38" - integrity sha512-APucaP5rlmTRYKtRA6FE5QPP87x76ejw5t5guRJ4y5OgMnwtsvigw7HHhKZlx2MGXLeZd6R/GNZR/IqDHcbtQw== + version "7.1.8" + resolved "https://registry.yarnpkg.com/@types/chai-as-promised/-/chai-as-promised-7.1.8.tgz#f2b3d82d53c59626b5d6bbc087667ccb4b677fe9" + integrity sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw== dependencies: "@types/chai" "*" "@types/chai@*": - version "4.3.9" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.9.tgz#144d762491967db8c6dea38e03d2206c2623feec" - integrity sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg== + version "4.3.10" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.10.tgz#2ad2959d1767edee5b0e4efb1a0cd2b500747317" + integrity sha512-of+ICnbqjmFCiixUnqRulbylyXQrPqIGf/B3Jax1wIF3DvSheysQxAWvqHhZiW3IQrycvokcLcFQlveGp+vyNg== "@types/chai@4.2.0": version "4.2.0" @@ -1938,9 +1969,9 @@ "@types/node" "*" "@types/events@*": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.2.tgz#c9b18152fdac34e924260532762255e34ef1d491" - integrity sha512-v4Mr60wJuF069iZZCdY5DKhfj0l6eXNJtbSM/oMDNdRLoBEUsktmKnswkz0X3OAic5W8Qy/YU6owKE4A66Y46A== + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.3.tgz#a8ef894305af28d1fc6d2dfdfc98e899591ea529" + integrity sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g== "@types/form-data@0.0.33": version "0.0.33" @@ -1957,10 +1988,15 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/http-cache-semantics@^4.0.2": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" + integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== + "@types/json-schema@^7.0.9": - version "7.0.14" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.14.tgz#74a97a5573980802f32c8e47b663530ab3b6b7d1" - integrity sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw== + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/json5@^0.0.29": version "0.0.29" @@ -1978,9 +2014,9 @@ integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== "@types/minimist@^1.2.0": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.4.tgz#81f886786411c45bba3f33e781ab48bd56bfca2e" - integrity sha512-Kfe/D3hxHTusnPNRbycJE1N77WHDsdS4AjUYIzlDzhDrS47NrwuL3YW4VITxwR7KCVpzwgy4Rbj829KSSQmwXQ== + version "1.2.5" + resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.5.tgz#ec10755e871497bcd83efe927e43ec46e8c0747e" + integrity sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag== "@types/mocha@9.1.0": version "9.1.0" @@ -1988,9 +2024,9 @@ integrity sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg== "@types/node@*": - version "20.8.10" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.8.10.tgz#a5448b895c753ae929c26ce85cab557c6d4a365e" - integrity sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w== + version "20.9.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.9.0.tgz#bfcdc230583aeb891cf51e73cfdaacdd8deae298" + integrity sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw== dependencies: undici-types "~5.26.4" @@ -2000,9 +2036,9 @@ integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== "@types/node@^18.15.11": - version "18.18.8" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.18.8.tgz#2b285361f2357c8c8578ec86b5d097c7f464cfd6" - integrity sha512-OLGBaaK5V3VRBS1bAkMVP2/W9B+H8meUfl866OrMNQqt7wDgdpWPp5o6gmIc9pB+lIQHSq4ZL8ypeH1vPxcPaQ== + version "18.18.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.18.9.tgz#5527ea1832db3bba8eb8023ce8497b7d3f299592" + integrity sha512-0f5klcuImLnG4Qreu9hPj/rEfFq6YRc5n2mAjSsH+ec/mJL+3voBH0+8T7o8RpFjH7ovc+TRsL/c7OYIQsPTfQ== dependencies: undici-types "~5.26.4" @@ -2012,14 +2048,14 @@ integrity sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw== "@types/normalize-package-data@^2.4.0", "@types/normalize-package-data@^2.4.1": - version "2.4.3" - resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.3.tgz#291c243e4b94dbfbc0c0ee26b7666f1d5c030e2c" - integrity sha512-ehPtgRgaULsFG8x0NeYJvmyH1hmlfsNLujHe9dQEia/7MAJYdzMSi19JtchUHjmBA6XC/75dK55mzZH+RyieSg== + version "2.4.4" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" + integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== "@types/pbkdf2@^3.0.0": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.1.tgz#c290c1f0d3dc364af94c2c5ee92046a13b7f89fd" - integrity sha512-4HCoGwR3221nOc7G0Z/6KgTNGgaaFGkbGrtUJsB+zlKX2LBVjFHHIUkieMBgHHXgBH5Gq6dZHJKdBYdtlhBQvw== + version "3.1.2" + resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.2.tgz#2dc43808e9985a2c69ff02e2d2027bd4fe33e8dc" + integrity sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew== dependencies: "@types/node" "*" @@ -2029,26 +2065,39 @@ integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== "@types/qs@^6.2.31", "@types/qs@^6.9.7": - version "6.9.9" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.9.tgz#66f7b26288f6799d279edf13da7ccd40d2fa9197" - integrity sha512-wYLxw35euwqGvTDx6zfY1vokBFnsK0HNrzc6xNHchxfO2hpuRg74GbkEW7e3sSmPvj0TjCDT1VCa6OtHXnubsg== + version "6.9.10" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.10.tgz#0af26845b5067e1c9a622658a51f60a3934d51e8" + integrity sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw== "@types/secp256k1@^4.0.1": - version "4.0.5" - resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.5.tgz#14b1766b4fbc198b0af5599d9fd21c89056633ce" - integrity sha512-aIonTBMErtE3T9MxDvTZRzcrT/mCqpEZBw3CCY/i+oG9n57N/+7obBkhFgavUAIrX21bU0LHg1XRgtaLdelBhA== + version "4.0.6" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.6.tgz#d60ba2349a51c2cbc5e816dcd831a42029d376bf" + integrity sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ== dependencies: "@types/node" "*" "@types/semver@^7.3.12": - version "7.5.4" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.4.tgz#0a41252ad431c473158b22f9bfb9a63df7541cff" - integrity sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ== + version "7.5.5" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.5.tgz#deed5ab7019756c9c90ea86139106b0346223f35" + integrity sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg== "@types/triple-beam@^1.3.2": - version "1.3.4" - resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.4.tgz#a1d5f480245db86e2f4777000065d4fe7467a012" - integrity sha512-HlJjF3wxV4R2VQkFpKe0YqJLilYNgtRtsqqZtby7RkVsSs+i+vbyzjtUwpFEdUCKcrGzCiEJE7F/0mKjh0sunA== + 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/webidl-conversions@*": + version "7.0.3" + resolved "https://registry.yarnpkg.com/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz#1306dbfa53768bcbcfc95a1c8cde367975581859" + integrity sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA== + +"@types/whatwg-url@^8.2.1": + version "8.2.2" + resolved "https://registry.yarnpkg.com/@types/whatwg-url/-/whatwg-url-8.2.2.tgz#749d5b3873e845897ada99be4448041d4cc39e63" + integrity sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA== + dependencies: + "@types/node" "*" + "@types/webidl-conversions" "*" "@typescript-eslint/eslint-plugin@^5.57.1": version "5.62.0" @@ -2154,7 +2203,7 @@ eslint-plugin-prefer-arrow "^1.2.3" typescript "^5.0.2" -"@zero-tech/ztoken@^2.0.0": +"@zero-tech/ztoken@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@zero-tech/ztoken/-/ztoken-2.0.0.tgz#42e2c2aa28ffbc64ce4340cf0464bcc22a155be5" integrity sha512-t6eLaK3KFsMUZ8cD1OqAiLQgvfYvXSIsgvEwt6FPedT5h64s/5XbVc1MWW22DZ98HqmgcWyEUs3g1MBAG1orCQ== @@ -2306,9 +2355,9 @@ ajv@^8.0.1: uri-js "^4.2.2" amazon-cognito-identity-js@^6.0.1: - version "6.3.6" - resolved "https://registry.yarnpkg.com/amazon-cognito-identity-js/-/amazon-cognito-identity-js-6.3.6.tgz#a5baa3615dc5771d9f9edeedf13d6e6df5e202d6" - integrity sha512-kBq+GE6OkLrxtFj3ZduIOlKBFYeOqZK3EhxbDBkv476UTvy+uwfR0tlriTq2QzNdnvlQAjBIXnXuOM7DwR1UEQ== + version "6.3.7" + resolved "https://registry.yarnpkg.com/amazon-cognito-identity-js/-/amazon-cognito-identity-js-6.3.7.tgz#65c3d7ee4e0c0a1ffea01927248989c5bd1d1868" + integrity sha512-tSjnM7KyAeOZ7UMah+oOZ6cW4Gf64FFcc7BE2l7MTcp7ekAPrXaCbpcW2xEpH1EiDS4cPcAouHzmCuc2tr72vQ== dependencies: "@aws-crypto/sha256-js" "1.2.2" buffer "4.9.2" @@ -2646,9 +2695,9 @@ axios@^0.27.2: form-data "^4.0.0" axios@^1.4.0, axios@^1.5.1: - version "1.6.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.0.tgz#f1e5292f26b2fd5c2e66876adc5b06cdbd7d2102" - integrity sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg== + version "1.6.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.1.tgz#76550d644bf0a2d469a01f9244db6753208397d7" + integrity sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" @@ -2829,6 +2878,11 @@ bs58check@^2.1.2: create-hash "^1.1.0" safe-buffer "^5.1.2" +bson@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/bson/-/bson-6.2.0.tgz#4b6acafc266ba18eeee111373c2699304a9ba0a3" + integrity sha512-ID1cI+7bazPDyL9wYy9GaQ8gEEohWvcUl/Yf0dIdutJxnmInEEyCsb4awy/OiBfall7zBA179Pahi3vCdFze3Q== + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -2918,6 +2972,24 @@ cacache@^17.0.0, cacache@^17.0.4, cacache@^17.1.3: tar "^6.1.11" unique-filename "^3.0.0" +cacheable-lookup@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz#3476a8215d046e5a3202a9209dd13fec1f933a27" + integrity sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w== + +cacheable-request@^10.2.8: + version "10.2.14" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-10.2.14.tgz#eb915b665fda41b79652782df3f553449c406b9d" + integrity sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ== + dependencies: + "@types/http-cache-semantics" "^4.0.2" + get-stream "^6.0.1" + http-cache-semantics "^4.1.1" + keyv "^4.5.3" + mimic-response "^4.0.0" + normalize-url "^8.0.0" + responselike "^3.0.0" + call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" @@ -3008,7 +3080,7 @@ chai@4.2.0: pathval "^1.1.0" type-detect "^4.0.5" -chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.2: +chalk@^2.3.2, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -3586,6 +3658,13 @@ decamelize@^4.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + deep-eql@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" @@ -3628,6 +3707,11 @@ defender-base-client@^1.44.0: lodash "^4.17.19" node-fetch "^2.6.0" +defer-to-connect@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== + define-data-property@^1.0.1, define-data-property@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" @@ -4455,7 +4539,7 @@ fast-diff@^1.2.0: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== -fast-glob@^3.0.3, fast-glob@^3.2.9, fast-glob@^3.3.0: +fast-glob@^3.0.3, fast-glob@^3.2.9, fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -4639,6 +4723,11 @@ foreground-child@^3.1.0: cross-spawn "^7.0.0" signal-exit "^4.0.1" +form-data-encoder@^2.1.2: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.4.tgz#261ea35d2a70d48d30ec7a9603130fa5515e9cd5" + integrity sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw== + form-data@^2.2.0: version "2.5.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" @@ -5036,16 +5125,17 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -globby@^13.1.4: - version "13.2.2" - resolved "https://registry.yarnpkg.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592" - integrity sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w== +globby@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-14.0.0.tgz#ea9c062a3614e33f516804e778590fcf055256b9" + integrity sha512-/1WM/LNHRAOH9lZta77uGbq0dAEQM+XjNesWwhlERDVenqothRbnzTrL3/LrIoEPPjeUHC3vrS6TwoyxeHs7MQ== dependencies: - dir-glob "^3.0.1" - fast-glob "^3.3.0" + "@sindresorhus/merge-streams" "^1.0.0" + fast-glob "^3.3.2" ignore "^5.2.4" - merge2 "^1.4.1" - slash "^4.0.0" + path-type "^5.0.0" + slash "^5.1.0" + unicorn-magic "^0.1.0" gopd@^1.0.1: version "1.0.1" @@ -5054,6 +5144,23 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" +got@^12.1.0: + version "12.6.1" + resolved "https://registry.yarnpkg.com/got/-/got-12.6.1.tgz#8869560d1383353204b5a9435f782df9c091f549" + integrity sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ== + dependencies: + "@sindresorhus/is" "^5.2.0" + "@szmarczak/http-timer" "^5.0.1" + cacheable-lookup "^7.0.0" + cacheable-request "^10.2.8" + decompress-response "^6.0.0" + form-data-encoder "^2.1.2" + get-stream "^6.0.1" + http2-wrapper "^2.1.10" + lowercase-keys "^3.0.0" + p-cancelable "^3.0.0" + responselike "^3.0.0" + graceful-fs@4.2.10: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" @@ -5354,6 +5461,14 @@ http-response-object@^3.0.1: dependencies: "@types/node" "^10.0.3" +http2-wrapper@^2.1.10: + version "2.2.1" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-2.2.1.tgz#310968153dcdedb160d8b72114363ef5fce1f64a" + integrity sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.2.0" + https-proxy-agent@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" @@ -6024,6 +6139,13 @@ kuler@^2.0.0: resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== +latest-version@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-7.0.0.tgz#843201591ea81a4d404932eeb61240fe04e9e5da" + integrity sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg== + dependencies: + package-json "^8.1.0" + lcid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" @@ -6187,9 +6309,9 @@ lines-and-columns@^1.1.6: integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== lines-and-columns@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.3.tgz#b2f0badedb556b747020ab8ea7f0373e22efac1b" - integrity sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w== + version "2.0.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.4.tgz#d00318855905d2660d8c0822e3f5a4715855fc42" + integrity sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A== load-json-file@^1.0.0: version "1.1.0" @@ -6309,13 +6431,6 @@ log-symbols@4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" -logdown@3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/logdown/-/logdown-3.3.1.tgz#836d5a195b5949c6db631ccc9fecce0492e01d10" - integrity sha512-pjX0vlIJsYQlgVzFba2amXI1wZZnhrEorL68MdLI7B0/sN1TNUozBNFaHfcPHMM3A+INZ0OXFDxtnoaEgOmGjQ== - dependencies: - chalk "^2.3.0" - logform@^2.3.2, logform@^2.4.0: version "2.6.0" resolved "https://registry.yarnpkg.com/logform/-/logform-2.6.0.tgz#8c82a983f05d6eaeb2d75e3decae7a768b2bf9b5" @@ -6328,10 +6443,17 @@ logform@^2.3.2, logform@^2.4.0: safe-stable-stringify "^2.3.1" triple-beam "^1.3.0" +lowercase-keys@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" + integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== + lru-cache@^10.0.1, "lru-cache@^9.1.1 || ^10.0.0": - version "10.0.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.1.tgz#0a3be479df549cca0e5d693ac402ff19537a6b7a" - integrity sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g== + version "10.0.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.2.tgz#34504678cc3266b09b8dfd6fab4e1515258271b7" + integrity sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg== + dependencies: + semver "^7.3.5" lru-cache@^5.1.1: version "5.1.1" @@ -6470,6 +6592,11 @@ memory-level@^1.0.0: functional-red-black-tree "^1.0.1" module-error "^1.0.1" +memory-pager@^1.0.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5" + integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg== + memorystream@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" @@ -6562,6 +6689,16 @@ mimic-fn@^4.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +mimic-response@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-4.0.0.tgz#35468b19e7c75d10f5165ea25e75a5ceea7cf70f" + integrity sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg== + min-indent@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" @@ -6758,6 +6895,23 @@ module-error@^1.0.1, module-error@^1.0.2: resolved "https://registry.yarnpkg.com/module-error/-/module-error-1.0.2.tgz#8d1a48897ca883f47a45816d4fb3e3c6ba404d86" integrity sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA== +mongodb-connection-string-url@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz#57901bf352372abdde812c81be47b75c6b2ec5cf" + integrity sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ== + dependencies: + "@types/whatwg-url" "^8.2.1" + whatwg-url "^11.0.0" + +mongodb@^6.1.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-6.2.0.tgz#2c9dcb3eeaf528ed850e94b3df392de6c6b0d7ab" + integrity sha512-d7OSuGjGWDZ5usZPqfvb36laQ9CPhnWkAGHT61x5P95p/8nMVeH8asloMwW6GcYFeB0Vj4CB/1wOTDG2RA9BFA== + dependencies: + "@mongodb-js/saslprep" "^1.1.0" + bson "^6.2.0" + mongodb-connection-string-url "^2.6.0" + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -7081,9 +7235,9 @@ npm-user-validate@^2.0.0: integrity sha512-sSWeqAYJ2dUPStJB+AEj0DyLRltr/f6YNcvCA7phkB8/RMLMnVsQ41GMwHo/ERZLYNDsyB2wPm7pZo1mqPOl7Q== npm@^9.5.0: - version "9.9.0" - resolved "https://registry.yarnpkg.com/npm/-/npm-9.9.0.tgz#ea4ecdbdf85dc4cedf9365f6d201990100430bb5" - integrity sha512-wkd7sjz4KmdmddYQcd0aTP73P1cEuPlekeulz4jTDeMVx/Zo5XZ5KQ1z3eUzV3Q/WZpEO0NJXTrD5FNFe6fhCA== + version "9.9.1" + resolved "https://registry.yarnpkg.com/npm/-/npm-9.9.1.tgz#55fd293a86a877b6aacfca3021ec4e94fcc0b930" + integrity sha512-D3YZ1ZTxPGDHLLiFU9q3sVrPfYnn6BaJ1hogm3vdWi8oOmHGtTlPUPXAM0iG22UT0JRkBnMDOh6oUhpbEYgg2A== dependencies: "@isaacs/string-locale-compare" "^1.1.0" "@npmcli/arborist" "^6.5.0" @@ -7341,6 +7495,11 @@ os-tmpdir@~1.0.2: resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== +p-cancelable@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" + integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== + p-each-series@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-3.0.0.tgz#d1aed5e96ef29864c897367a7d2a628fdc960806" @@ -7448,6 +7607,16 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +package-json@^8.1.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-8.1.1.tgz#3e9948e43df40d1e8e78a85485f1070bf8f03dc8" + integrity sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA== + dependencies: + got "^12.1.0" + registry-auth-token "^5.0.1" + registry-url "^6.0.0" + semver "^7.3.7" + pacote@^15.0.0, pacote@^15.0.8, pacote@^15.2.0: version "15.2.0" resolved "https://registry.yarnpkg.com/pacote/-/pacote-15.2.0.tgz#0f0dfcc3e60c7b39121b2ac612bf8596e95344d3" @@ -7603,6 +7772,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +path-type@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-5.0.0.tgz#14b01ed7aea7ddf9c7c3f46181d4d04f9c785bb8" + integrity sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg== + pathval@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" @@ -7790,7 +7964,7 @@ punycode@2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" integrity sha512-Yxz2kRwT90aPiWEMHVYnEf4+rhwF1tBmmZ4KepCP+Wkium9JxtWnUm1nqGwpiAHr/tnTSeHqr3wb++jgSkXjhA== -punycode@^2.1.0: +punycode@^2.1.0, punycode@^2.1.1: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== @@ -7824,6 +7998,11 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -7856,7 +8035,7 @@ raw-body@^2.4.1: iconv-lite "0.4.24" unpipe "1.0.0" -rc@^1.2.8: +rc@1.2.8, rc@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -8039,13 +8218,20 @@ regexp.prototype.flags@^1.5.1: define-properties "^1.2.0" set-function-name "^2.0.0" -registry-auth-token@^5.0.0: +registry-auth-token@^5.0.0, registry-auth-token@^5.0.1: version "5.0.2" resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-5.0.2.tgz#8b026cc507c8552ebbe06724136267e63302f756" integrity sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ== dependencies: "@pnpm/npm-conf" "^2.1.0" +registry-url@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-6.0.1.tgz#056d9343680f2f64400032b1e199faa692286c58" + integrity sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q== + dependencies: + rc "1.2.8" + req-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/req-cwd/-/req-cwd-2.0.0.tgz#d4082b4d44598036640fb73ddea01ed53db49ebc" @@ -8080,6 +8266,11 @@ require-main-filename@^1.0.1: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" integrity sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug== +resolve-alpn@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== + resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" @@ -8116,6 +8307,13 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.22.4: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +responselike@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-3.0.0.tgz#20decb6c298aff0dbee1c355ca95461d42823626" + integrity sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg== + dependencies: + lowercase-keys "^3.0.0" + retry@0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" @@ -8490,10 +8688,10 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slash@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" - integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== +slash@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce" + integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg== slice-ansi@^4.0.0: version "4.0.0" @@ -8552,10 +8750,10 @@ solc@^0.4.20: semver "^5.3.0" yargs "^4.7.1" -solhint@^3.4.1: - version "3.6.2" - resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.6.2.tgz#2b2acbec8fdc37b2c68206a71ba89c7f519943fe" - integrity sha512-85EeLbmkcPwD+3JR7aEMKsVC9YrRSxd4qkXuMzrlf7+z2Eqdfm1wHWq1ffTuo5aDhoZxp2I9yF3QkxZOxOL7aQ== +solhint@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/solhint/-/solhint-4.0.0.tgz#fbd27ec9c8348b4fea90b5b469a5c95d625d2e59" + integrity sha512-bFViMcFvhqVd/HK3Roo7xZXX5nbujS7Bxeg5vnZc9QvH0yCWCrQ38Yrn1pbAY9tlKROc6wFr+rK1mxYgYrjZgA== dependencies: "@solidity-parser/parser" "^0.16.0" ajv "^6.12.6" @@ -8568,6 +8766,7 @@ solhint@^3.4.1: glob "^8.0.3" ignore "^5.2.4" js-yaml "^4.1.0" + latest-version "^7.0.0" lodash "^4.17.21" pluralize "^8.0.0" semver "^7.5.2" @@ -8638,6 +8837,13 @@ source-map@~0.2.0: dependencies: amdefine ">=0.0.4" +sparse-bitfield@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11" + integrity sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ== + dependencies: + memory-pager "^1.0.2" + spawn-error-forwarder@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz#1afd94738e999b0346d7b9fc373be55e07577029" @@ -8997,10 +9203,10 @@ tempy@^3.0.0: type-fest "^2.12.2" unique-string "^3.0.0" -tenderly@^0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/tenderly/-/tenderly-0.5.3.tgz#115653ff33fc33e3be41ab7dd669fdbe0f28a6fb" - integrity sha512-sR+sbZKZzt3b2+moXJsrkBvbava1/4mGulIfuZw8bwr2OpCH8N00dME1t89JC8RjVnQjy4VewVFHyWANdn5zYQ== +tenderly@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tenderly/-/tenderly-0.6.0.tgz#fb74e672a3b6dcfe66c8fd44c79ab91a9233f3eb" + integrity sha512-uPnR5ujR1j0Aay4nuqymTY2nu3e0yDjl6dHBqkTTIYEDzyzaDLx2+PkVxjT5RWseAbWORsa6GYetletATf1zmQ== dependencies: axios "^0.27.2" cli-table3 "^0.6.2" @@ -9086,6 +9292,13 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== +tr46@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" + integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== + dependencies: + punycode "^2.1.1" + tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -9266,9 +9479,9 @@ type-fest@^3.0.0, type-fest@^3.8.0: integrity sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g== type-fest@^4.2.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.6.0.tgz#9c575f7e20530defef4f9cdc5e2c85d6e4ea0fc9" - integrity sha512-rLjWJzQFOq4xw7MgJrCZ6T1jIOvvYElXT12r+y0CC6u67hegDHaxcPqb2fZHOGlqxugGQPNB1EnTezjBetkwkw== + version "4.7.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.7.1.tgz#dbe462c5f350d708ec6ca6fe8925b5b6541ca9a7" + integrity sha512-iWr8RUmzAJRfhZugX9O7nZE6pCxDU8CZ3QxsLuTnGcBLJpCaP2ll3s4eMTBoFnU/CeXY/5rfQSuAEsTGJO4y8A== type-is@~1.6.18: version "1.6.18" @@ -9385,6 +9598,11 @@ unfetch@^4.2.0: resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be" integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA== +unicorn-magic@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4" + integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== + unique-filename@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" @@ -9533,6 +9751,19 @@ webidl-conversions@^3.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + +whatwg-url@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" + integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ== + dependencies: + tr46 "^3.0.0" + webidl-conversions "^7.0.0" + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"