diff --git a/.env.sample b/.env.sample index d7382364..a7721707 100644 --- a/.env.sample +++ b/.env.sample @@ -10,6 +10,9 @@ MONGO_DB_CLIENT_OPTS="" LOG_LEVEL="info" | "debug" SILENT_LOGGER="false" | "true" +# We use the environment level to validate against certain requirements +ENV_LEVEL="dev" | "test" | "prod" + # Boolean value for if we deploy the mock # true = we deploy the mock # false = we use a hard coded address and pull data from chain diff --git a/src/deploy/campaign/environments.ts b/src/deploy/campaign/environments.ts index 88b573c6..bbf58919 100644 --- a/src/deploy/campaign/environments.ts +++ b/src/deploy/campaign/environments.ts @@ -1,125 +1,183 @@ -import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; - -import { IDeployCampaignConfig } from "./types"; -import { - DEFAULT_REGISTRATION_FEE_PERCENT, - DEFAULT_ROYALTY_FRACTION, - ZNS_DOMAIN_TOKEN_NAME, - ZNS_DOMAIN_TOKEN_SYMBOL, - DEFAULT_DECIMALS, - DECAULT_PRECISION, - DEFAULT_PRICE_CONFIG, -} from "../../../test/helpers"; -import { ethers } from "ethers"; -import { ICurvePriceConfig } from "../missions/types"; - -import { MeowMainnet } from "../missions/contracts/meow-token/mainnet-data"; - -const getCustomAddresses = ( - key : string, - account : SignerWithAddress, - accounts ?: Array -) => { - const addresses = []; - - if (process.env[key]) { - /* eslint-disable @typescript-eslint/no-non-null-assertion */ - const decoded = atob(process.env[key]!); - - // Check if there is more than one custom governor - if (decoded.includes(",")) { - addresses.push(...decoded.split(",")); - } else { - addresses.push(decoded); - } - } - - if (addresses.length === 0) { - if (accounts && accounts.length > 0) { - addresses.push(...accounts); // The user provided custom governors / admins as a param for testing - } else { - addresses.push(account.address); // No custom governors / admins provided, use the deployer as the default - } - } - return addresses; -}; - -// This function builds a config with default values but overrides them with any values that are set -export const getConfig = ( - account : SignerWithAddress, - zeroVault : SignerWithAddress, - governors ?: Array, - admins ?: Array, -) : IDeployCampaignConfig => { - // Price config variables - const maxPrice = - process.env.MAX_PRICE - ? ethers.utils.parseEther(process.env.MAX_PRICE) - : DEFAULT_PRICE_CONFIG.maxPrice; - - const minPrice = - process.env.MIN_PRICE - ? ethers.utils.parseEther(process.env.MIN_PRICE) - : DEFAULT_PRICE_CONFIG.minPrice; - - const maxLength = - process.env.MAX_LENGTH - ? ethers.BigNumber.from(process.env.MAX_LENGTH) - : DEFAULT_PRICE_CONFIG.maxLength; - - const baseLength = - process.env.BASE_LENGTH - ? ethers.BigNumber.from(process.env.BASE_LENGTH) - : DEFAULT_PRICE_CONFIG.baseLength; - - const decimals = process.env.DECIMALS ? ethers.BigNumber.from(process.env.DECIMALS) : DEFAULT_DECIMALS; - const precision = process.env.PRECISION ? ethers.BigNumber.from(process.env.PRECISION) : DECAULT_PRECISION; - const precisionMultiplier = ethers.BigNumber.from(10).pow(decimals.sub(precision)); - - const feePercentage = - process.env.REG_FEE_PERCENT - ? ethers.BigNumber.from(process.env.REG_FEE_PERCENT) - : DEFAULT_REGISTRATION_FEE_PERCENT; - const royaltyReceiver = - process.env.ROYALTY_RECEIVER - ? process.env.ROYALTY_RECEIVER - : account.address; - const royaltyFraction = - process.env.ROYALTY_FRACTION - ? ethers.BigNumber.from(process.env.ROYALTY_FRACTION) - : DEFAULT_ROYALTY_FRACTION; - - const priceConfig : ICurvePriceConfig = { - maxPrice, - minPrice, - maxLength, - baseLength, - precisionMultiplier, - feePercentage, - isSet: true, - }; - - // Get governor addresses set through env, if any - const governorAddresses = getCustomAddresses("GOVERNOR_ADDRESSES", account, governors); - - // Get admin addresses set through env, if any - const adminAddresses = getCustomAddresses("ADMIN_ADDRESSES", account, admins); - - const config : IDeployCampaignConfig = { - deployAdmin: account, - governorAddresses, - adminAddresses, - domainToken: { - name: process.env.TOKEN_NAME ? process.env.TOKEN_NAME : ZNS_DOMAIN_TOKEN_NAME, - symbol: process.env.TOKEN_SYMBOL ? process.env.TOKEN_SYMBOL : ZNS_DOMAIN_TOKEN_SYMBOL, - defaultRoyaltyReceiver: royaltyReceiver, - defaultRoyaltyFraction: royaltyFraction, - }, - rootPriceConfig: priceConfig, - zeroVaultAddress: process.env.ZERO_VAULT_ADDRESS ? process.env.ZERO_VAULT_ADDRESS : zeroVault.address, - mockMeowToken: process.env.MOCK_MEOW_TOKEN ? !!process.env.MOCK_MEOW_TOKEN : true, - stakingTokenAddress: process.env.STAKING_TOKEN_ADDRESS ? process.env.STAKING_TOKEN_ADDRESS : MeowMainnet.address, - }; - - return config; -}; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; + +import { IDeployCampaignConfig } from "./types"; +import { + DEFAULT_REGISTRATION_FEE_PERCENT, + DEFAULT_ROYALTY_FRACTION, + ZNS_DOMAIN_TOKEN_NAME, + ZNS_DOMAIN_TOKEN_SYMBOL, + DEFAULT_DECIMALS, + DECAULT_PRECISION, + DEFAULT_PRICE_CONFIG, + getCurvePrice, + NO_MOCK_PROD_ERR, + STAKING_TOKEN_ERR, + INVALID_CURVE_ERR, + MONGO_URI_ERR, + INVALID_ENV_ERR, +} from "../../../test/helpers"; +import { ethers } from "ethers"; +import { ICurvePriceConfig } from "../missions/types"; +import { MEOW_TOKEN } from "../constants"; +import { DEFAULT_MONGO_URI } from "../db/mongo-adapter/constants"; +import { MeowMainnet } from "../missions/contracts/meow-token/mainnet-data"; + + +const getCustomAddresses = ( + key : string, + account : SignerWithAddress, + accounts ?: Array +) => { + const addresses = []; + + if (process.env[key]) { + /* eslint-disable @typescript-eslint/no-non-null-assertion */ + const decoded = atob(process.env[key]!); + + // Check if there is more than one custom governor + if (decoded.includes(",")) { + addresses.push(...decoded.split(",")); + } else { + addresses.push(decoded); + } + } + + if (addresses.length === 0) { + if (accounts && accounts.length > 0) { + addresses.push(...accounts); // The user provided custom governors / admins as a param for testing + } else { + addresses.push(account.address); // No custom governors / admins provided, use the deployer as the default + } + } + return addresses; +}; + +// This function builds a config with default values but overrides them with any values that are set +export const getConfig = ( + account : SignerWithAddress, + zeroVault : SignerWithAddress, + governors ?: Array, + admins ?: Array, +) : IDeployCampaignConfig => { + // Price config variables + const maxPrice = + process.env.MAX_PRICE + ? ethers.utils.parseEther(process.env.MAX_PRICE) + : DEFAULT_PRICE_CONFIG.maxPrice; + + const minPrice = + process.env.MIN_PRICE + ? ethers.utils.parseEther(process.env.MIN_PRICE) + : DEFAULT_PRICE_CONFIG.minPrice; + + const maxLength = + process.env.MAX_LENGTH + ? ethers.BigNumber.from(process.env.MAX_LENGTH) + : DEFAULT_PRICE_CONFIG.maxLength; + + const baseLength = + process.env.BASE_LENGTH + ? ethers.BigNumber.from(process.env.BASE_LENGTH) + : DEFAULT_PRICE_CONFIG.baseLength; + + const decimals = process.env.DECIMALS ? ethers.BigNumber.from(process.env.DECIMALS) : DEFAULT_DECIMALS; + const precision = process.env.PRECISION ? ethers.BigNumber.from(process.env.PRECISION) : DECAULT_PRECISION; + const precisionMultiplier = ethers.BigNumber.from(10).pow(decimals.sub(precision)); + + const feePercentage = + process.env.REG_FEE_PERCENT + ? ethers.BigNumber.from(process.env.REG_FEE_PERCENT) + : DEFAULT_REGISTRATION_FEE_PERCENT; + const royaltyReceiver = + process.env.ROYALTY_RECEIVER + ? process.env.ROYALTY_RECEIVER + : account.address; + const royaltyFraction = + process.env.ROYALTY_FRACTION + ? ethers.BigNumber.from(process.env.ROYALTY_FRACTION) + : DEFAULT_ROYALTY_FRACTION; + + const priceConfig : ICurvePriceConfig = { + maxPrice, + minPrice, + maxLength, + baseLength, + precisionMultiplier, + feePercentage, + isSet: true, + }; + + // Get governor addresses set through env, if any + const governorAddresses = getCustomAddresses("GOVERNOR_ADDRESSES", account, governors); + + // Get admin addresses set through env, if any + const adminAddresses = getCustomAddresses("ADMIN_ADDRESSES", account, admins); + + const config : IDeployCampaignConfig = { + deployAdmin: account, + governorAddresses, + adminAddresses, + domainToken: { + name: process.env.TOKEN_NAME ? process.env.TOKEN_NAME : ZNS_DOMAIN_TOKEN_NAME, + symbol: process.env.TOKEN_SYMBOL ? process.env.TOKEN_SYMBOL : ZNS_DOMAIN_TOKEN_SYMBOL, + defaultRoyaltyReceiver: royaltyReceiver, + defaultRoyaltyFraction: royaltyFraction, + }, + rootPriceConfig: priceConfig, + zeroVaultAddress: process.env.ZERO_VAULT_ADDRESS ? process.env.ZERO_VAULT_ADDRESS : zeroVault.address, + mockMeowToken: process.env.MOCK_MEOW_TOKEN ? !!process.env.MOCK_MEOW_TOKEN : true, + stakingTokenAddress: process.env.STAKING_TOKEN_ADDRESS ? process.env.STAKING_TOKEN_ADDRESS : MEOW_TOKEN, + }; + + // Will throw an error based on any invalid setup, given the `ENV_LEVEL` set + validate(config); + + return config; +}; + +// For testing the behaviour when we manipulate, we have an optional "env" string param +export const validate = (config : IDeployCampaignConfig, env ?: string, mongoUri ?: string) => { + // Prioritize reading from the env variable first, and only then fallback to the param + let envLevel = process.env.ENV_LEVEL; + + if (env) { + // We only ever specify an `env` param in tests + // So if there is a value we must use that instead + // otherwise only ever use the ENV_LEVEL above + envLevel = env; + } + + if (envLevel === "dev") return; // No validation needed for dev + + if (!mongoUri) mongoUri = process.env.MONGO_URI ? process.env.MONGO_URI : DEFAULT_MONGO_URI; + + // Mainnet or testnet + if (envLevel === "prod" || envLevel === "test") { + requires(!config.mockMeowToken, NO_MOCK_PROD_ERR); + requires(config.stakingTokenAddress === MeowMainnet.address, STAKING_TOKEN_ERR); + requires(validatePrice(config.rootPriceConfig), INVALID_CURVE_ERR); + requires(!mongoUri.includes("localhost"), MONGO_URI_ERR); + } + + // If we reach this code, there is an env variable but it's not valid. + throw new Error(INVALID_ENV_ERR); + +}; + +const requires = (condition : boolean, message : string) => { + if (!condition) { + throw new Error(message); + } +}; + +// No price spike before `minPrice` kicks in at `maxLength` +const validatePrice = (config : ICurvePriceConfig) => { + const strA = "a".repeat(config.maxLength.toNumber()); + const strB = "b".repeat(config.maxLength.add(1).toNumber()); + + const priceA = getCurvePrice(strA, config); + const priceB = getCurvePrice(strB, config); + + // if A < B, then the price spike is invalid + return !priceA.lt(priceB); +}; diff --git a/test/DeployCampaign.test.ts b/test/DeployCampaign.test.ts index 51ae40ab..3c469a1d 100644 --- a/test/DeployCampaign.test.ts +++ b/test/DeployCampaign.test.ts @@ -5,21 +5,30 @@ import { DEFAULT_PRICE_CONFIG, ZNS_DOMAIN_TOKEN_NAME, ZNS_DOMAIN_TOKEN_SYMBOL, + INVALID_ENV_ERR, + NO_MOCK_PROD_ERR, + STAKING_TOKEN_ERR, + INVALID_CURVE_ERR, + MONGO_URI_ERR, } from "./helpers"; import { expect } from "chai"; import { - meowTokenName, meowTokenSymbol, + 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"; +import { getConfig, validate } from "../src/deploy/campaign/environments"; +import { ethers, BigNumber } from "ethers"; describe("Deploy Campaign Test", () => { let deployAdmin : SignerWithAddress; let admin : SignerWithAddress; + let governor : SignerWithAddress; // eslint-disable-next-line @typescript-eslint/no-unused-vars let user : SignerWithAddress; let zeroVault : SignerWithAddress; @@ -28,15 +37,17 @@ describe("Deploy Campaign Test", () => { // TODO dep: move logger to runZNSCampaign() const logger = getLogger(); + before(async () => { + [deployAdmin, admin, governor, zeroVault, user] = await hre.ethers.getSigners(); + }); describe("MEOW Token Ops", () => { before(async () => { - [deployAdmin, admin, zeroVault, user] = await hre.ethers.getSigners(); campaignConfig = { deployAdmin, - governorAddresses: [ deployAdmin.address ], - adminAddresses: [ deployAdmin.address, admin.address ], + governorAddresses: [deployAdmin.address], + adminAddresses: [deployAdmin.address, admin.address], domainToken: { name: ZNS_DOMAIN_TOKEN_NAME, symbol: ZNS_DOMAIN_TOKEN_SYMBOL, @@ -124,4 +135,132 @@ describe("Deploy Campaign Test", () => { await dbAdapter.dropDB(); }); }); + + describe("Configurable Environment & Validation", () => { + // The `validate` function accepts the environment parameter only for the + // purpose of testing here as manipulating actual environment variables + // like `process.env. = "value"` is not possible in a test environment + // because the Hardhat process for running these tests will not respect these + // changes. `getConfig` calls to `validate` on its own, but never passes a value + // for the environment specifically, that is ever only inferred from the `process.env.ENV_LEVEL` + it("Throws if env variable is invalid", async () => { + try { + const config = await getConfig( + deployAdmin, + zeroVault, + [deployAdmin.address, governor.address], + [deployAdmin.address, admin.address], + ); + + validate(config, "other"); + + /* eslint-disable @typescript-eslint/no-explicit-any */ + } catch (e : any) { + expect(e.message).includes(INVALID_ENV_ERR); + } + }); + + it("Fails to validate when mocking MEOW on prod", async () => { + try { + const config = await getConfig( + deployAdmin, + zeroVault, + [deployAdmin.address, governor.address], + [deployAdmin.address, admin.address], + ); + // Modify the config + config.mockMeowToken = true; + validate(config, "prod"); + + /* eslint-disable @typescript-eslint/no-explicit-any */ + } catch (e : any) { + expect(e.message).includes(NO_MOCK_PROD_ERR); + } + }); + + it("Fails to validate if not using the MEOW token on prod", async () => { + try { + const config = await getConfig( + deployAdmin, + zeroVault, + [deployAdmin.address, governor.address], + [deployAdmin.address, admin.address], + ); + + config.mockMeowToken = false; + config.stakingTokenAddress = "0x123"; + + validate(config, "prod"); + /* eslint-disable @typescript-eslint/no-explicit-any */ + } catch (e : any) { + expect(e.message).includes(STAKING_TOKEN_ERR); + } + }); + + it("Fails to validate if invalid curve for pricing", async () => { + try { + const config = await getConfig( + deployAdmin, + zeroVault, + [deployAdmin.address, governor.address], + [deployAdmin.address, admin.address], + ); + + config.mockMeowToken = false; + config.stakingTokenAddress = MeowMainnet.address; + config.rootPriceConfig.baseLength = BigNumber.from(3); + config.rootPriceConfig.maxLength = BigNumber.from(5); + config.rootPriceConfig.maxPrice = ethers.constants.Zero; + config.rootPriceConfig.minPrice = ethers.utils.parseEther("3"); + + validate(config, "prod"); + /* eslint-disable @typescript-eslint/no-explicit-any */ + } catch (e : any) { + expect(e.message).includes(INVALID_CURVE_ERR); + } + }); + + it("Fails to validate if no mongo uri or local URI in prod", async () => { + try { + const config = await getConfig( + deployAdmin, + zeroVault, + [deployAdmin.address, governor.address], + [deployAdmin.address, admin.address], + ); + + config.mockMeowToken = false; + config.stakingTokenAddress = MeowMainnet.address; + + // Normally we would call to an env variable to grab this value + const uri = ""; + + // Falls back onto the default URI which is for localhost and fails in prod + validate(config, "prod", uri); + /* eslint-disable @typescript-eslint/no-explicit-any */ + } catch (e : any) { + expect(e.message).includes(MONGO_URI_ERR); + } + + try { + const config = await getConfig( + deployAdmin, + zeroVault, + [deployAdmin.address, governor.address], + [deployAdmin.address, admin.address], + ); + + config.mockMeowToken = false; + config.stakingTokenAddress = MeowMainnet.address; + + // Normally we would call to an env variable to grab this value + const uri = "mongodb://localhost:27018"; + + validate(config, "prod", uri); + /* eslint-disable @typescript-eslint/no-explicit-any */ + } catch (e : any) { + expect(e.message).includes(MONGO_URI_ERR); + } + }); + }); }); diff --git a/test/ZNSCurvePricer.test.ts b/test/ZNSCurvePricer.test.ts index bcfa64ab..477f3149 100644 --- a/test/ZNSCurvePricer.test.ts +++ b/test/ZNSCurvePricer.test.ts @@ -6,7 +6,7 @@ import { parseEther } from "ethers/lib/utils"; import { IZNSContracts } from "./helpers/types"; import { deployZNS, - calcCurvePrice, + getCurvePrice, DEFAULT_PRECISION_MULTIPLIER, CURVE_PRICE_CONFIG_ERR, validateUpgrade, @@ -170,8 +170,8 @@ describe("ZNSCurvePricer", () => { const domainOneRefValue = BigNumber.from("4545450000000000000000"); const domainTwoRefValue = BigNumber.from("7692300000000000000000"); - const domainOneExpPrice = await calcCurvePrice(domainOne, DEFAULT_PRICE_CONFIG); - const domainTwoExpPrice = await calcCurvePrice(domainTwo, DEFAULT_PRICE_CONFIG); + const domainOneExpPrice = await getCurvePrice(domainOne, DEFAULT_PRICE_CONFIG); + const domainTwoExpPrice = await getCurvePrice(domainTwo, DEFAULT_PRICE_CONFIG); const domainOnePriceSC = await zns.curvePricer.getPrice(domainHash, domainOne, true); const domainTwoPriceSC = await zns.curvePricer.getPrice(domainHash, domainTwo, true); @@ -191,7 +191,7 @@ describe("ZNSCurvePricer", () => { "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstu"; - const expectedPrice = await calcCurvePrice(domain, DEFAULT_PRICE_CONFIG); + const expectedPrice = await getCurvePrice(domain, DEFAULT_PRICE_CONFIG); const domainPrice = await zns.curvePricer.getPrice(domainHash, domain, true); expect(domainPrice).to.eq(expectedPrice); @@ -204,15 +204,15 @@ describe("ZNSCurvePricer", () => { const medium = "wilderworld"; const long = "wilderworldbeastspetsnftscatscalicosteve"; - const expectedShortPrice = await calcCurvePrice(short, DEFAULT_PRICE_CONFIG); + const expectedShortPrice = await getCurvePrice(short, DEFAULT_PRICE_CONFIG); const shortPrice = await zns.curvePricer.getPrice(domainHash, short, true); expect(expectedShortPrice).to.eq(shortPrice); - const expectedMediumPrice = await calcCurvePrice(medium, DEFAULT_PRICE_CONFIG); + const expectedMediumPrice = await getCurvePrice(medium, DEFAULT_PRICE_CONFIG); const mediumPrice = await zns.curvePricer.getPrice(domainHash, medium, true); expect(expectedMediumPrice).to.eq(mediumPrice); - const expectedLongPrice = await calcCurvePrice(long, DEFAULT_PRICE_CONFIG); + const expectedLongPrice = await getCurvePrice(long, DEFAULT_PRICE_CONFIG); const longPrice = await zns.curvePricer.getPrice(domainHash, long, true); expect(expectedLongPrice).to.eq(longPrice); }); @@ -225,7 +225,7 @@ describe("ZNSCurvePricer", () => { "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" + "a"; - const expectedPrice = calcCurvePrice(domain, DEFAULT_PRICE_CONFIG); + const expectedPrice = getCurvePrice(domain, DEFAULT_PRICE_CONFIG); const domainPrice = await zns.curvePricer.getPrice(domainHash, domain, true); expect(domainPrice).to.eq(expectedPrice); }); @@ -476,7 +476,7 @@ describe("ZNSCurvePricer", () => { it("The price of a domain is modified relatively when the basePrice is changed", async () => { const newMaxPrice = DEFAULT_PRICE_CONFIG.maxPrice.add(parseEther("9")); - const expectedPriceBefore = await calcCurvePrice(defaultDomain, DEFAULT_PRICE_CONFIG); + const expectedPriceBefore = await getCurvePrice(defaultDomain, DEFAULT_PRICE_CONFIG); const priceBefore= await zns.curvePricer.getPrice(domainHash, defaultDomain, true); expect(expectedPriceBefore).to.eq(priceBefore); @@ -488,7 +488,7 @@ describe("ZNSCurvePricer", () => { maxPrice: newMaxPrice, }; - const expectedPriceAfter = await calcCurvePrice(defaultDomain, newConfig); + const expectedPriceAfter = await getCurvePrice(defaultDomain, newConfig); const priceAfter = await zns.curvePricer.getPrice(domainHash, defaultDomain, true); expect(expectedPriceAfter).to.eq(priceAfter); @@ -712,7 +712,7 @@ describe("ZNSCurvePricer", () => { const newLength = 8; const paramsBefore = await zns.curvePricer.priceConfigs(domainHash); - const expectedPriceBefore = await calcCurvePrice(defaultDomain, DEFAULT_PRICE_CONFIG); + const expectedPriceBefore = await getCurvePrice(defaultDomain, DEFAULT_PRICE_CONFIG); const priceBefore = await zns.curvePricer.getPrice(domainHash, defaultDomain, true); expect(priceBefore).to.eq(expectedPriceBefore); expect(priceBefore).to.not.eq(paramsBefore.maxPrice); @@ -726,7 +726,7 @@ describe("ZNSCurvePricer", () => { baseLength: BigNumber.from(newLength), }; - const expectedPriceAfter = await calcCurvePrice(defaultDomain, newConfig); + const expectedPriceAfter = await getCurvePrice(defaultDomain, newConfig); const priceAfter = await zns.curvePricer.getPrice(domainHash, defaultDomain, true); expect(priceAfter).to.eq(expectedPriceAfter); expect(priceAfter).to.eq(paramsAfter.maxPrice); @@ -743,7 +743,7 @@ describe("ZNSCurvePricer", () => { const paramsBefore = await zns.curvePricer.priceConfigs(domainHash); - const expectedPriceBefore = await calcCurvePrice(defaultDomain, newConfig1); + const expectedPriceBefore = await getCurvePrice(defaultDomain, newConfig1); const priceBefore = await zns.curvePricer.getPrice(domainHash, defaultDomain, true); expect(priceBefore).to.eq(expectedPriceBefore); expect(priceBefore).to.eq(paramsBefore.maxPrice); @@ -758,7 +758,7 @@ describe("ZNSCurvePricer", () => { const paramsAfter = await zns.curvePricer.priceConfigs(domainHash); - const expectedPriceAfter = await calcCurvePrice(defaultDomain, newConfig2); + const expectedPriceAfter = await getCurvePrice(defaultDomain, newConfig2); const priceAfter = await zns.curvePricer.getPrice(domainHash, defaultDomain, true); expect(priceAfter).to.eq(expectedPriceAfter); expect(priceAfter).to.not.eq(paramsAfter.maxPrice); @@ -793,7 +793,7 @@ describe("ZNSCurvePricer", () => { baseLength: BigNumber.from(newRootLength), }; - const expectedRootPrice = await calcCurvePrice(defaultDomain, newConfig); + const expectedRootPrice = await getCurvePrice(defaultDomain, newConfig); const rootPrice = await zns.curvePricer.getPrice(domainHash, defaultDomain, true); expect(rootPrice).to.eq(expectedRootPrice); diff --git a/test/ZNSRootRegistrar.test.ts b/test/ZNSRootRegistrar.test.ts index 40a5504d..5907ffa4 100644 --- a/test/ZNSRootRegistrar.test.ts +++ b/test/ZNSRootRegistrar.test.ts @@ -2,28 +2,31 @@ import * as hre from "hardhat"; import { expect } from "chai"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { + normalizeName, + validateUpgrade, AccessType, + OwnerOf, + PaymentType, + getAccessRevertMsg, + hashDomainLabel, DEFAULT_ROYALTY_FRACTION, DEFAULT_TOKEN_URI, distrConfigEmpty, - hashDomainLabel, INVALID_LENGTH_ERR, INITIALIZED_ERR, INVALID_TOKENID_ERC_ERR, - normalizeName, + REGISTRAR_ROLE, + ZNS_DOMAIN_TOKEN_NAME, + ZNS_DOMAIN_TOKEN_SYMBOL, + DEFAULT_PRECISION_MULTIPLIER, + DEFAULT_PRICE_CONFIG, + DEFAULT_REGISTRATION_FEE_PERCENT, 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, - DEFAULT_PRECISION_MULTIPLIER, - DEFAULT_PRICE_CONFIG, - DEFAULT_REGISTRATION_FEE_PERCENT, + INVALID_NAME_ERR, } from "./helpers"; import { IDistributionConfig } from "./helpers/types"; import * as ethers from "ethers"; @@ -33,7 +36,6 @@ import { checkBalance } from "./helpers/balances"; 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"; diff --git a/test/helpers/errors.ts b/test/helpers/errors.ts index 8135121c..070dd189 100644 --- a/test/helpers/errors.ts +++ b/test/helpers/errors.ts @@ -38,3 +38,10 @@ export const NO_BENEFICIARY_ERR = "ZNSTreasury: parent domain has no beneficiary // OpenZeppelin export const INVALID_TOKENID_ERC_ERR = "ERC721: invalid token ID"; export const INITIALIZED_ERR = "Initializable: contract is already initialized"; + +// Environment validation +export const INVALID_ENV_ERR = "Invalid environment value. Must set env to one of `dev`, `test`, or `prod`"; +export const NO_MOCK_PROD_ERR = "Cannot mock MEOW token in production"; +export const STAKING_TOKEN_ERR = "Must use MEOW token in production"; +export const INVALID_CURVE_ERR = "Must use a valid price configuration"; +export const MONGO_URI_ERR = "Cannot use local mongo URI in production"; diff --git a/test/helpers/pricing.ts b/test/helpers/pricing.ts index 475aabbd..30ae987b 100644 --- a/test/helpers/pricing.ts +++ b/test/helpers/pricing.ts @@ -12,7 +12,7 @@ import { ICurvePriceConfig } from "../../src/deploy/missions/types"; * @param priceConfig Object with all the pricing props * @returns The expected price for that domain */ -export const calcCurvePrice = ( +export const getCurvePrice = ( name : string, priceConfig = DEFAULT_PRICE_CONFIG, ) : BigNumber => { @@ -66,7 +66,7 @@ export const getPriceObject = ( let expectedPrice; const configLen = Object.keys(priceConfig).length; if (configLen === 7 || configLen === 6) { - expectedPrice = calcCurvePrice(name, priceConfig as ICurvePriceConfig); + expectedPrice = getCurvePrice(name, priceConfig as ICurvePriceConfig); } else if (configLen === 3 || configLen === 2) { ({ price: expectedPrice } = priceConfig as IFixedPriceConfig); } else {