From b72b7dd22ca16b9c2864f23a67f260a56519a699 Mon Sep 17 00:00:00 2001 From: ivaylonikolov7 Date: Mon, 17 Apr 2023 23:23:46 +0300 Subject: [PATCH] chore: change eslint, prettier and solhint configs --- .eslintignore | 1 + .eslintrc.js | 16 ++----- .prettierignore | 1 + .solhint.json | 3 +- hardhat.config.ts | 9 +++- test/e2e/main.test.ts | 37 +++++++++------ test/unit/DssCronKeeper.test.ts | 24 +++++----- test/unit/DssVestTopUp.test.ts | 53 +++++++++++++--------- test/utils/chainlink-automation.ts | 73 ++++++++++++++++++------------ test/utils/events.ts | 22 +++++++-- test/utils/types.ts | 29 ++++++++++++ test/utils/uniswap.ts | 10 ++-- 12 files changed, 180 insertions(+), 98 deletions(-) create mode 100644 test/utils/types.ts diff --git a/.eslintignore b/.eslintignore index 85f5562..d464e5b 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,5 @@ node_modules artifacts cache +typechain coverage diff --git a/.eslintrc.js b/.eslintrc.js index 98ce193..6fc04a9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,24 +1,14 @@ module.exports = { env: { - browser: false, - es2021: true, - mocha: true, + es6: true, node: true, + mocha: true, }, plugins: ["@typescript-eslint"], extends: [ "standard", + "plugin:@typescript-eslint/recommended", "plugin:prettier/recommended", - "plugin:node/recommended", ], parser: "@typescript-eslint/parser", - parserOptions: { - ecmaVersion: 12, - }, - rules: { - "node/no-unsupported-features/es-syntax": [ - "error", - { ignores: ["modules"] }, - ], - }, }; diff --git a/.prettierignore b/.prettierignore index f268596..db04f3c 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,5 +1,6 @@ node_modules artifacts cache +typechain coverage* gasReporterOutput.json diff --git a/.solhint.json b/.solhint.json index f3e31e8..68a4292 100644 --- a/.solhint.json +++ b/.solhint.json @@ -2,6 +2,7 @@ "extends": "solhint:recommended", "rules": { "compiler-version": ["error", "^0.8.0"], - "func-visibility": ["warn", { "ignoreConstructors": true }] + "func-visibility": ["warn", { "ignoreConstructors": true }], + "var-name-mixedcase": "off" } } diff --git a/hardhat.config.ts b/hardhat.config.ts index f06bafa..966af65 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -77,10 +77,17 @@ const config: HardhatUserConfig = { etherscan: { apiKey: process.env.ETHERSCAN_API_KEY, }, + typechain: { + outDir: "typechain", + target: "ethers-v4", + }, }; +if (!config.networks) { + throw new Error(`No config networks!`); +} if (process.env.FORK_ENABLED === "true") { - config.networks!.hardhat = { + config.networks.hardhat = { forking: { url: process.env.GOERLI_URL || "", blockNumber: parseInt(process.env.BLOCK_NUMBER || ""), diff --git a/test/e2e/main.test.ts b/test/e2e/main.test.ts index 87a6491..8605234 100644 --- a/test/e2e/main.test.ts +++ b/test/e2e/main.test.ts @@ -3,15 +3,15 @@ import { ethers, network } from "hardhat"; import * as chainlink from "../utils/chainlink-automation"; import { setupPool } from "../utils/uniswap"; import { parseEventFromABI } from "../utils/events"; -import { Wallet } from "ethers"; +import { Wallet, Event, Contract } from "ethers"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { DssVestTopUp } from "../../typechain/DssVestTopUp"; import { NetworkPaymentAdapter } from "../../typechain/NetworkPaymentAdapter"; import { SampleJob } from "../../typechain/SampleJob"; import { ERC20PresetMinterPauser } from "../../typechain/ERC20PresetMinterPauser"; import { LinkTokenMock } from "../../typechain/LinkTokenMock"; -import { KeeperRegistry20 } from "../../typechain/KeeperRegistry20"; -import { KeeperRegistrar20 } from "../../typechain/KeeperRegistrar20"; +import { KeeperRegistry2_0 as KeeperRegistry20 } from "../../typechain/KeeperRegistry2_0"; +import { KeeperRegistrar2_0 as KeeperRegistrar20 } from "../../typechain/KeeperRegistrar2_0"; const { parseEther, parseBytes32String, formatBytes32String } = ethers.utils; @@ -54,13 +54,18 @@ describe("E2E", function () { before(async function () { [owner] = await ethers.getSigners(); - linkToken = await ethers.getContractAt("LinkTokenMock", linkTokenAddress); + linkToken = (await ethers.getContractAt( + "LinkTokenMock", + linkTokenAddress + )) as unknown as LinkTokenMock; registrySigners = chainlink.getRegistrySigners(); }); beforeEach(async function () { // setup dai token - const ERC20 = await ethers.getContractFactory("ERC20PresetMinterPauser"); + const ERC20 = (await ethers.getContractFactory( + "ERC20PresetMinterPauser" + )) as unknown as ERC20PresetMinterPauser; daiToken = await ERC20.deploy("Test DAI", "DAI"); await daiToken.mint(owner.address, parseEther("100")); @@ -68,7 +73,10 @@ describe("E2E", function () { const Sequencer = await ethers.getContractFactory("Sequencer"); const sequencer = await Sequencer.deploy(); const SampleJob = await ethers.getContractFactory("SampleJob"); - job = await SampleJob.deploy(sequencer.address, 100); + job = (await SampleJob.deploy( + sequencer.address, + 100 + )) as unknown as SampleJob; await sequencer.addJob(job.address); // setup vest @@ -124,11 +132,11 @@ describe("E2E", function () { const vestingPlanId = 1; const bufferMax = parseEther("1"); const minPayment = parseEther("1"); - paymentAdapter = await NetworkPaymentAdapter.deploy( + paymentAdapter = (await NetworkPaymentAdapter.deploy( dssVest.address, daiJoin.address, vowAddress - ); + )) as unknown as NetworkPaymentAdapter; await paymentAdapter["file(bytes32,uint256)"]( formatBytes32String("vestId"), vestingPlanId @@ -150,7 +158,7 @@ describe("E2E", function () { [daiToken.address, uniswapPoolFee, linkToken.address] ); const DssVestTopUp = await ethers.getContractFactory("DssVestTopUp"); - topUp = await DssVestTopUp.deploy( + topUp = (await DssVestTopUp.deploy( upkeepId, registry.address, daiToken.address, @@ -161,7 +169,7 @@ describe("E2E", function () { swapRouterAddress, slippageToleranceBps, uniswapPath - ); + )) as unknown as DssVestTopUp; // set topup contract as treasury in payment adapter await paymentAdapter["file(bytes32,address)"]( @@ -224,9 +232,12 @@ describe("E2E", function () { beforeEach(async function () { const poolLinkTokenLiquidity = parseEther("100"); const poolDaiTokenLiquidity = parseEther("100"); + const linkTokenContract = linkToken as unknown as Contract; + const daiTokenContract = daiToken as unknown as Contract; + await setupPool( - linkToken, - daiToken, + linkTokenContract, + daiTokenContract, owner, poolLinkTokenLiquidity, poolDaiTokenLiquidity, @@ -276,7 +287,7 @@ describe("E2E", function () { // get total link spent by upkeep const performUpkeepLinkSpent = tx.events?.find( - (e) => e.event === "UpkeepPerformed" + (e: Event) => e.event === "UpkeepPerformed" )?.args?.totalPayment; // check if balance is equal to initial balance + amount of link swapped - link spent for upkeep diff --git a/test/unit/DssCronKeeper.test.ts b/test/unit/DssCronKeeper.test.ts index 8a34498..5e09eea 100644 --- a/test/unit/DssCronKeeper.test.ts +++ b/test/unit/DssCronKeeper.test.ts @@ -1,12 +1,10 @@ import { expect } from "chai"; import { ethers } from "hardhat"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; -import { - DssCronKeeper, - DssVestTopUpMock, - SampleJob, - Sequencer, -} from "../../typechain"; +import { DssCronKeeper } from "../../typechain/DssCronKeeper"; +import { Sequencer } from "../../typechain/Sequencer"; +import { SampleJob } from "../../typechain/SampleJob"; +import { DssVestTopUpMock } from "../../typechain/DssVestTopUpMock"; const { HashZero } = ethers.constants; const { formatBytes32String } = ethers.utils; @@ -27,24 +25,28 @@ describe("DssCronKeeper", function () { beforeEach(async function () { const Sequencer = await ethers.getContractFactory("Sequencer"); - sequencer = await Sequencer.deploy(); + sequencer = (await Sequencer.deploy()) as unknown as Sequencer; await sequencer.addNetwork(formatBytes32String("test"), 1); const SampleJob = await ethers.getContractFactory("SampleJob"); - job = await SampleJob.deploy(sequencer.address, 100); + job = (await SampleJob.deploy( + sequencer.address, + 100 + )) as unknown as SampleJob; await sequencer.addJob(job.address); runJobEncoded = iface.encodeFunctionData("runJob", [job.address, "0x"]); const DssCronKeeper = await ethers.getContractFactory("DssCronKeeper"); - keeper = await DssCronKeeper.deploy( + keeper = (await DssCronKeeper.deploy( sequencer.address, formatBytes32String("test") - ); + )) as unknown as DssCronKeeper; const DssVestTopUpMock = await ethers.getContractFactory( "DssVestTopUpMock" ); - topUpMock = await DssVestTopUpMock.deploy(); + topUpMock = + (await DssVestTopUpMock.deploy()) as unknown as DssVestTopUpMock; await keeper.setUpkeepRefunder(topUpMock.address); }); diff --git a/test/unit/DssVestTopUp.test.ts b/test/unit/DssVestTopUp.test.ts index e5015ef..3ef4622 100644 --- a/test/unit/DssVestTopUp.test.ts +++ b/test/unit/DssVestTopUp.test.ts @@ -3,16 +3,14 @@ import { ethers } from "hardhat"; import { AbiCoder } from "@ethersproject/abi"; import { utils, Wallet } from "ethers"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; -import { - DssVestTopUp, - DssVestTopUp__factory as DssVestTopUpFactory, - ERC20PresetMinterPauser, - KeeperRegistryMock, - MockV3Aggregator, - MockV3Aggregator__factory as MockV3AggregatorFactory, - NetworkPaymentAdapterMock, - SwapRouterMock, -} from "../../typechain"; +import { DssVestTopUp } from "../../typechain/DssVestTopUp"; +import { NetworkPaymentAdapterMock } from "../../typechain/NetworkPaymentAdapterMock"; +import { ERC20PresetMinterPauser } from "../../typechain/ERC20PresetMinterPauser"; +import { KeeperRegistryMock } from "../../typechain/KeeperRegistryMock"; +import { SwapRouterMock } from "../../typechain/SwapRouterMock"; +import { DssVestTopUp__factory as DssVestTopUpFactory } from "../../typechain/factories/DssVestTopUp__factory"; +import { MockV3Aggregator__factory as MockV3AggregatorFactory } from "../../typechain/factories/MockV3Aggregator__factory"; +import { MockV3Aggregator } from "../../typechain/MockV3Aggregator"; const { parseEther, parseUnits, toUtf8Bytes, keccak256 } = ethers.utils; @@ -40,8 +38,12 @@ describe("DssVestTopUp", function () { let owner: SignerWithAddress; before(async function () { - DssVestTopUp = await ethers.getContractFactory("DssVestTopUp"); - MockV3Aggregator = await ethers.getContractFactory("MockV3Aggregator"); + DssVestTopUp = (await ethers.getContractFactory( + "DssVestTopUp" + )) as unknown as DssVestTopUpFactory; + MockV3Aggregator = (await ethers.getContractFactory( + "MockV3Aggregator" + )) as unknown as MockV3AggregatorFactory; }); beforeEach(async function () { @@ -50,24 +52,32 @@ describe("DssVestTopUp", function () { const ERC20PresetMinterPauser = await ethers.getContractFactory( "ERC20PresetMinterPauser" ); - daiToken = await ERC20PresetMinterPauser.deploy("Test DAI", "DAI"); + daiToken = (await ERC20PresetMinterPauser.deploy( + "Test DAI", + "DAI" + )) as unknown as ERC20PresetMinterPauser; // setup link token - linkToken = await ERC20PresetMinterPauser.deploy("Test Chainlink", "LINK"); + linkToken = (await ERC20PresetMinterPauser.deploy( + "Test Chainlink", + "LINK" + )) as unknown as ERC20PresetMinterPauser; // setup chainlink keeper registry mock const KeeperRegistryMock = await ethers.getContractFactory( "KeeperRegistryMock" ); - keeperRegistryMock = await KeeperRegistryMock.deploy(linkToken.address); + keeperRegistryMock = (await KeeperRegistryMock.deploy( + linkToken.address + )) as unknown as KeeperRegistryMock; await keeperRegistryMock.setUpkeepBalance(initialUpkeepBalance); // setup uniswap router mock const SwapRouterMock = await ethers.getContractFactory("SwapRouterMock"); - swapRouterMock = await SwapRouterMock.deploy( + swapRouterMock = (await SwapRouterMock.deploy( daiToken.address, linkToken.address - ); + )) as unknown as SwapRouterMock; // setup price feed mocks daiUsdPriceFeedMock = await MockV3Aggregator.deploy( @@ -83,13 +93,14 @@ describe("DssVestTopUp", function () { const NetworkPaymentAdapterMock = await ethers.getContractFactory( "NetworkPaymentAdapterMock" ); - paymentAdapterMock = await NetworkPaymentAdapterMock.deploy( + paymentAdapterMock = (await NetworkPaymentAdapterMock.deploy( daiToken.address, topUpAmount - ); + )) as unknown as NetworkPaymentAdapterMock; // setup topup contract - topUp = await DssVestTopUp.deploy( + const DssVestTopUp = await ethers.getContractFactory("DssVestTopUp"); + topUp = (await DssVestTopUp.deploy( fakeUpkeepId, keeperRegistryMock.address, daiToken.address, @@ -100,7 +111,7 @@ describe("DssVestTopUp", function () { swapRouterMock.address, slippageToleranceBps, uniswapPath - ); + )) as unknown as DssVestTopUp; // set topup contract as treasury in payment adapter paymentAdapterMock.setTreasury(topUp.address); diff --git a/test/utils/chainlink-automation.ts b/test/utils/chainlink-automation.ts index bf06988..58d71b6 100644 --- a/test/utils/chainlink-automation.ts +++ b/test/utils/chainlink-automation.ts @@ -1,11 +1,17 @@ -import { BigNumber, Wallet, constants } from "ethers"; +import { + BigNumber, + Wallet, + constants, + Event, + type ContractReceipt, +} from "ethers"; +import { JsonRpcProvider } from "@ethersproject/providers"; import { ethers } from "hardhat"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; -import { - KeeperRegistry20, - KeeperRegistrar20, - LinkTokenMock, -} from "../../typechain"; +import { LinkTokenMock } from "../../typechain/LinkTokenMock"; +import { KeeperRegistry2_0 as KeeperRegistry20 } from "../../typechain/KeeperRegistry2_0"; +import { KeeperRegistrar2_0 as KeeperRegistrar20 } from "../../typechain/KeeperRegistrar2_0"; +import { EncodeConfig, ExtraParams, Upkeep } from "./types"; const { HashZero } = ethers.constants; const { formatBytes32String, Interface } = ethers.utils; @@ -33,7 +39,10 @@ export async function setupChainlinkAutomation( owner: SignerWithAddress, linkToken: LinkTokenMock, keeperRegistryLogicAddress: string -) { +): Promise<{ + registrar: KeeperRegistrar20; + registry: KeeperRegistry20; +}> { const registry = await deployRegistry(keeperRegistryLogicAddress); const registrar = await deployRegistrar(owner, linkToken, registry); @@ -55,14 +64,14 @@ export async function setupChainlinkAutomation( to: registrySigner.address, value: ethers.utils.parseEther("1.0"), }); - return { registry, registrar }; + return { registrar, registry }; } async function deployRegistry(keeperRegistryLogicAddress: string) { const Registry = await ethers.getContractFactory("KeeperRegistry2_0"); const registry = (await Registry.deploy( keeperRegistryLogicAddress - )) as KeeperRegistry20; + )) as unknown as KeeperRegistry20; return registry; } @@ -80,17 +89,17 @@ async function deployRegistrar( KeeperRegistrarParams.AUTO_APPROVE_MAX_ALLOWED, registry.address, KeeperRegistrarParams.MIN_UPKEEP_SPEND - )) as KeeperRegistrar20; + )) as unknown as KeeperRegistrar20; return registrar; } function encodeReport( - upkeeps: any, + upkeeps: Upkeep[], gasWeiReport = gasWei, linkEthReport = linkEth ) { - const upkeepIds = upkeeps.map((u: any) => u.Id); - const performDataTuples = upkeeps.map((u: any) => [ + const upkeepIds = upkeeps.map((u) => u.Id); + const performDataTuples = upkeeps.map((u) => [ u.checkBlockNum, u.checkBlockHash, u.performData, @@ -101,7 +110,11 @@ function encodeReport( ); } -function signReport(reportContext: string[], report: any, signers: Wallet[]) { +function signReport( + reportContext: string[], + report: string, + signers: Wallet[] +) { const reportDigest = ethers.utils.keccak256(report); const packedArgs = ethers.utils.solidityPack( ["bytes32", "bytes32[3]"], @@ -121,7 +134,7 @@ function signReport(reportContext: string[], report: any, signers: Wallet[]) { }; } -function encodeConfig(config: any) { +function encodeConfig(config: EncodeConfig) { return ethers.utils.defaultAbiCoder.encode( [ // eslint-disable-next-line no-multi-str @@ -182,13 +195,13 @@ async function configRegistry( async function transmit( registry: KeeperRegistry20, transmitter: Wallet, - upkeepIds: any, + upkeepIds: string[], signers: Wallet[], numSigners: number, - extraParams?: any, - performData?: any, - checkBlockNum?: any, - checkBlockHash?: any + extraParams?: ExtraParams, + performData?: string, + checkBlockNum?: number, + checkBlockHash?: string ) { const latestBlock = await ethers.provider.getBlock("latest"); const configDigest = (await registry.getState()).state.latestConfigDigest; @@ -228,7 +241,7 @@ export async function keeperRegistryPerformUpkeep( registrySigners: Wallet[], upkeepId: string, f: number -) { +): Promise { const registryCheckUpkeep = await registry .connect(constants.AddressZero) .callStatic.checkUpkeep(upkeepId); @@ -244,29 +257,31 @@ export async function keeperRegistryPerformUpkeep( [upkeepId], registrySigners, f + 1, - null, + undefined, performData ); return await tx.wait(); } -export function getRegistrySigners() { +export function getRegistrySigners(): Wallet[] { const signers: Wallet[] = []; + const provider = ethers.provider as JsonRpcProvider; + const signer1 = new ethers.Wallet( "0x7777777000000000000000000000000000000000000000000000000000000001" - ).connect(ethers.provider); + ).connect(provider); const signer2 = new ethers.Wallet( "0x7777777000000000000000000000000000000000000000000000000000000002" - ).connect(ethers.provider); + ).connect(provider); const signer3 = new ethers.Wallet( "0x7777777000000000000000000000000000000000000000000000000000000003" - ).connect(ethers.provider); + ).connect(provider); const signer4 = new ethers.Wallet( "0x7777777000000000000000000000000000000000000000000000000000000004" - ).connect(ethers.provider); + ).connect(provider); const signer5 = new ethers.Wallet( "0x7777777000000000000000000000000000000000000000000000000000000005" - ).connect(ethers.provider); + ).connect(provider); signers.push(signer1, signer2, signer3, signer4, signer5); @@ -310,7 +325,7 @@ export async function registerUpkeep( // get upkeep id const registerRc = await registerTx.wait(); const registrarEvents = registerRc.events?.filter( - (e) => e.address === keeperRegistrarAddress + (e: Event) => e.address === keeperRegistrarAddress ); const registrationApprovedEvent = registrarEvents && registrarEvents[1]; const upkeepIdHex = registrationApprovedEvent?.topics[2]; diff --git a/test/utils/events.ts b/test/utils/events.ts index 805fa78..d238241 100644 --- a/test/utils/events.ts +++ b/test/utils/events.ts @@ -1,8 +1,15 @@ import { ContractReceipt, ethers, Event } from "ethers"; import { Interface } from "ethers/lib/utils"; -export function decodeEvent(tx: ContractReceipt, iface: Interface) { - const event: Event = tx.events?.find((e: Event) => { +export function decodeEvent( + tx: ContractReceipt, + iface: Interface +): ethers.utils.LogDescription { + if (!tx.events) { + throw new Error("No events in this tx"); + } + + const event = tx.events.find((e: Event) => { let result = true; try { iface.parseLog(e); @@ -10,12 +17,19 @@ export function decodeEvent(tx: ContractReceipt, iface: Interface) { result = false; } return result; - })!; + }); + + if (!event) { + throw new Error("No such event"); + } return iface.parseLog(event); } -export function parseEventFromABI(tx: ContractReceipt, abi: string[]) { +export function parseEventFromABI( + tx: ContractReceipt, + abi: string[] +): ethers.utils.Result { const iface = new ethers.utils.Interface(abi); const event = decodeEvent(tx, iface); return event.args; diff --git a/test/utils/types.ts b/test/utils/types.ts new file mode 100644 index 0000000..9da8edf --- /dev/null +++ b/test/utils/types.ts @@ -0,0 +1,29 @@ +import { type BigNumber } from "ethers"; + +export interface Upkeep { + Id: string; + checkBlockNum: number; + checkBlockHash: string; + performData: string; +} + +export interface ExtraParams { + gasLimit: BigNumber; + gasPrice: BigNumber; +} + +export interface EncodeConfig { + paymentPremiumPPB: BigNumber; + flatFeeMicroLink: BigNumber; + checkGasLimit: BigNumber; + stalenessSeconds: BigNumber; + gasCeilingMultiplier: BigNumber; + minUpkeepSpend: BigNumber; + maxCheckDataSize: BigNumber; + maxPerformDataSize: BigNumber; + maxPerformGas: BigNumber; + fallbackGasPrice: BigNumber; + fallbackLinkPrice: BigNumber; + transcoder: string; + registrar: string; +} diff --git a/test/utils/uniswap.ts b/test/utils/uniswap.ts index 1336d9a..ceeba48 100644 --- a/test/utils/uniswap.ts +++ b/test/utils/uniswap.ts @@ -23,11 +23,11 @@ export const TICK_SPACINGS = { [FeeAmount.HIGH]: 200, }; -export const getMinTick = (tickSpacing: number) => +export const getMinTick = (tickSpacing: number): number => Math.ceil(-887272 / tickSpacing) * tickSpacing; -export const getMaxTick = (tickSpacing: number) => +export const getMaxTick = (tickSpacing: number): number => Math.floor(887272 / tickSpacing) * tickSpacing; -export const getMaxLiquidityPerTick = (tickSpacing: number) => +export const getMaxLiquidityPerTick = (tickSpacing: number): BigNumber => BigNumber.from(2) .pow(128) .sub(1) @@ -54,7 +54,7 @@ export async function setupPool( token1Liquidity: BigNumber, uniswapV3FactoryAddress: string, positionManagerAddress: string -) { +): Promise { const oneToOneRatio = encodePriceSqrt(BigNumber.from(1), BigNumber.from(1)); const pool = await findOrCreatePool( @@ -112,7 +112,7 @@ export async function findOrCreatePool( poolAddress, IUniswapV3PoolABI, provider - ) as IUniswapV3Pool; + ) as unknown as IUniswapV3Pool; } else { pool = await createPool(token0, token1, ratio, provider, uniswapFactory); }