From 1f9cd619ca14e0bd71e8da46e416dc54331b7827 Mon Sep 17 00:00:00 2001 From: wei3erHase Date: Wed, 11 May 2022 13:34:34 +0200 Subject: [PATCH] chore: added test scaffolding (#2) * chore: added hardhat boilerplate * chore: run linter * fix: rm legacy code * feat: added vyper compiler * feat: added github workflows * fix: linter error * feat: adding basic test structure * fix: typings bug * feat: added tests scaffolding * fix: revert linter * fix: run prettier --- hardhat.config.ts | 26 +++-------- package.json | 4 +- test/e2e/fixed-forex.spec.ts | 42 +++++++++++++++++ test/unit/GaugeProxy.spec.ts | 26 +++++++++++ test/utils/bdd.ts | 9 ++++ test/utils/behaviours.ts | 49 ++++++++++++++++++++ test/utils/bn.ts | 28 ++++++++++++ test/utils/constants.ts | 2 + test/utils/contracts.ts | 18 ++++++++ test/utils/erc20.ts | 20 +++++++++ test/utils/event-utils.ts | 36 +++++++++++++++ test/utils/evm.ts | 73 ++++++++++++++++++++++++++++++ test/utils/index.ts | 8 ++++ test/utils/wallet.ts | 30 +++++++++++++ utils/deploy.ts | 4 +- utils/env.ts | 81 +++++++++++++++++++++++++++++++++ yarn.lock | 87 +++--------------------------------- 17 files changed, 440 insertions(+), 103 deletions(-) create mode 100644 test/e2e/fixed-forex.spec.ts create mode 100644 test/unit/GaugeProxy.spec.ts create mode 100644 test/utils/bdd.ts create mode 100644 test/utils/behaviours.ts create mode 100644 test/utils/bn.ts create mode 100644 test/utils/constants.ts create mode 100644 test/utils/contracts.ts create mode 100644 test/utils/erc20.ts create mode 100644 test/utils/event-utils.ts create mode 100644 test/utils/evm.ts create mode 100644 test/utils/index.ts create mode 100644 test/utils/wallet.ts create mode 100644 utils/env.ts diff --git a/hardhat.config.ts b/hardhat.config.ts index 513ae14..166b909 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -10,8 +10,8 @@ import 'hardhat-gas-reporter'; import 'hardhat-deploy'; import 'solidity-coverage'; import { HardhatUserConfig, MultiSolcUserConfig, NetworksUserConfig } from 'hardhat/types'; +import * as env from './utils/env'; import 'tsconfig-paths/register'; -import { getNodeUrl, accounts } from './utils/network'; const networks: NetworksUserConfig = process.env.TEST ? {} @@ -19,28 +19,16 @@ const networks: NetworksUserConfig = process.env.TEST hardhat: { forking: { enabled: process.env.FORK ? true : false, - url: getNodeUrl('mainnet'), + url: env.getNodeUrl('ethereum'), }, }, - localhost: { - url: getNodeUrl('localhost'), - accounts: accounts('localhost'), - }, kovan: { - url: getNodeUrl('kovan'), - accounts: accounts('kovan'), - }, - rinkeby: { - url: getNodeUrl('rinkeby'), - accounts: accounts('rinkeby'), - }, - ropsten: { - url: getNodeUrl('ropsten'), - accounts: accounts('ropsten'), + url: env.getNodeUrl('kovan'), + accounts: env.getAccounts('kovan'), }, - mainnet: { - url: getNodeUrl('mainnet'), - accounts: accounts('mainnet'), + ethereum: { + url: env.getNodeUrl('ethereum'), + accounts: env.getAccounts('ethereum'), }, }; diff --git a/package.json b/package.json index f8411ea..3f147e6 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "eth-gas-reporter/colors": "1.4.0" }, "dependencies": { - "@nomiclabs/hardhat-vyper": "^3.0.0", + "@nomiclabs/hardhat-vyper": "3.0.0", "@openzeppelin/contracts": "4.6.0", "solhint-plugin-wonderland": "0.0.1" }, @@ -81,7 +81,7 @@ "cross-env": "7.0.3", "dotenv": "16.0.0", "ethereum-waffle": "3.4.4", - "ethers": "5.6.4", + "ethers": "5.6.5", "hardhat": "2.9.3", "hardhat-deploy": "0.11.4", "hardhat-gas-reporter": "1.0.8", diff --git a/test/e2e/fixed-forex.spec.ts b/test/e2e/fixed-forex.spec.ts new file mode 100644 index 0000000..1f6d88d --- /dev/null +++ b/test/e2e/fixed-forex.spec.ts @@ -0,0 +1,42 @@ +import { getMainnetSdk } from '@dethcrypto/eth-sdk-client'; +import { Keep3rV1 } from '@eth-sdk-types'; +import { FixedForex, FixedForex__factory } from '@typechained'; +import { ethers } from 'hardhat'; +import { evm } from '@utils'; +import { expect } from 'chai'; +import { getNodeUrl } from 'utils/env'; +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; + +describe('FixedForex @skip-on-coverage', () => { + let deployer: SignerWithAddress; + let keep3rV1: Keep3rV1; + let snapshotId: string; + let fixedForex: FixedForex; + + before(async () => { + [deployer] = await ethers.getSigners(); + + await evm.reset({ + jsonRpcUrl: getNodeUrl('ethereum'), + blockNumber: 14750000, + }); + + const sdk = getMainnetSdk(deployer); + keep3rV1 = sdk.keep3rV1; + + const fixedForexFactory = (await ethers.getContractFactory('FixedForex')) as FixedForex__factory; + fixedForex = await fixedForexFactory.connect(deployer).deploy(keep3rV1.address); + + snapshotId = await evm.snapshot.take(); + }); + + beforeEach(async () => { + await evm.snapshot.revert(snapshotId); + }); + + describe('fixed-forex', () => { + it('should be deployed', async () => { + expect(await fixedForex.deployed()); + }); + }); +}); diff --git a/test/unit/GaugeProxy.spec.ts b/test/unit/GaugeProxy.spec.ts new file mode 100644 index 0000000..6ed01b7 --- /dev/null +++ b/test/unit/GaugeProxy.spec.ts @@ -0,0 +1,26 @@ +import chai, { expect } from 'chai'; +import { MockContract, MockContractFactory, smock } from '@defi-wonderland/smock'; +import { GaugeProxy, GaugeProxy__factory } from '@typechained'; +import { evm } from '@utils'; + +chai.use(smock.matchers); + +describe('GaugeProxy', () => { + let gauge: MockContract; + let gaugeFactory: MockContractFactory; + let snapshotId: string; + + before(async () => { + gaugeFactory = await smock.mock('GaugeProxy'); + gauge = await gaugeFactory.deploy(); + snapshotId = await evm.snapshot.take(); + }); + + beforeEach(async () => { + await evm.snapshot.revert(snapshotId); + }); + + it('should be deployed', async () => { + expect(await gauge.deployed()); + }); +}); diff --git a/test/utils/bdd.ts b/test/utils/bdd.ts new file mode 100644 index 0000000..fbc8f88 --- /dev/null +++ b/test/utils/bdd.ts @@ -0,0 +1,9 @@ +import { Suite, SuiteFunction } from 'mocha'; + +export const then = it; +export const given = beforeEach; +export const when: SuiteFunction = function (title: string, fn: (this: Suite) => void) { + context('when ' + title, fn); +}; +when.only = (title: string, fn?: (this: Suite) => void) => context.only('when ' + title, fn!); +when.skip = (title: string, fn: (this: Suite) => void) => context.skip('when ' + title, fn); diff --git a/test/utils/behaviours.ts b/test/utils/behaviours.ts new file mode 100644 index 0000000..b36cb3d --- /dev/null +++ b/test/utils/behaviours.ts @@ -0,0 +1,49 @@ +import { smock } from '@defi-wonderland/smock'; +import { Provider } from '@ethersproject/providers'; +import chai, { expect } from 'chai'; +import { Signer } from 'ethers'; +import { contracts, wallet } from '.'; +import { toUnit } from './bn'; + +chai.use(smock.matchers); + +export type Impersonator = Signer | Provider | string; + +export const onlyMaker = createOnlyCallableCheck(['maker'], 'OnlyMaker()'); +export const onlyKeeper = createOnlyCallableCheck(['keeper'], 'OnlyKeeper()'); +export const onlyGovernor = createOnlyCallableCheck(['governance'], 'OnlyGovernor()'); +export const onlyPendingGovernor = createOnlyCallableCheck(['pending governance'], 'OnlyPendingGovernor()'); + +export function createOnlyCallableCheck(allowedLabels: string[], error: string) { + return ( + delayedContract: () => any, + fnName: string, + allowedWallet: Impersonator | Impersonator[] | (() => Impersonator | Impersonator[]), + args: unknown[] | (() => unknown[]) + ) => { + allowedLabels.forEach((allowedLabel, index) => { + it(`should be callable by ${allowedLabel}`, async () => { + let impersonator = allowedWallet; + if (typeof allowedWallet === 'function') impersonator = allowedWallet(); + if (Array.isArray(impersonator)) impersonator = impersonator[index]; + + return expect(callFunction(impersonator as Impersonator)).not.to.be.revertedWith(error); + }); + }); + + it('should not be callable by any address', async () => { + const any = await wallet.generateRandom(); + await wallet.setBalance({ account: any.address, balance: toUnit(1) }); + return expect(callFunction(any)).to.be.revertedWith(error); + }); + + function callFunction(impersonator: Impersonator) { + const argsArray: unknown[] = typeof args === 'function' ? args() : args; + const fn = delayedContract().connect(impersonator)[fnName] as (...args: unknown[]) => unknown; + return fn(...argsArray, { + gasLimit: 1e6, + gasPrice: 500e9, + }); + } + }; +} diff --git a/test/utils/bn.ts b/test/utils/bn.ts new file mode 100644 index 0000000..47f41f3 --- /dev/null +++ b/test/utils/bn.ts @@ -0,0 +1,28 @@ +import { BigNumber, utils } from 'ethers'; +import { expect } from 'chai'; + +export const expectToEqualWithThreshold = ({ + value, + to, + threshold, +}: { + value: BigNumber | number | string; + to: BigNumber | number | string; + threshold: BigNumber | number | string; +}): void => { + value = toBN(value); + to = toBN(to); + threshold = toBN(threshold); + expect( + to.sub(threshold).lte(value) && to.add(threshold).gte(value), + `Expected ${value.toString()} to be between ${to.sub(threshold).toString()} and ${to.add(threshold).toString()}` + ).to.be.true; +}; + +export const toBN = (value: string | number | BigNumber): BigNumber => { + return BigNumber.isBigNumber(value) ? value : BigNumber.from(value); +}; + +export const toUnit = (value: number): BigNumber => { + return utils.parseUnits(value.toString()); +}; diff --git a/test/utils/constants.ts b/test/utils/constants.ts new file mode 100644 index 0000000..87596d7 --- /dev/null +++ b/test/utils/constants.ts @@ -0,0 +1,2 @@ +export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; +export const ETH_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; diff --git a/test/utils/contracts.ts b/test/utils/contracts.ts new file mode 100644 index 0000000..ee78d37 --- /dev/null +++ b/test/utils/contracts.ts @@ -0,0 +1,18 @@ +import { Contract, ContractFactory } from '@ethersproject/contracts'; +import { TransactionResponse } from '@ethersproject/abstract-provider'; +import { ContractInterface, Signer } from 'ethers'; +import { getStatic } from 'ethers/lib/utils'; + +export const deploy = async (contract: ContractFactory, args: any[]): Promise<{ tx: TransactionResponse; contract: Contract }> => { + const deploymentTransactionRequest = await contract.getDeployTransaction(...args); + const deploymentTx = await contract.signer.sendTransaction(deploymentTransactionRequest); + const contractAddress = getStatic<(deploymentTx: TransactionResponse) => string>(contract.constructor, 'getContractAddress')(deploymentTx); + const deployedContract = getStatic<(contractAddress: string, contractInterface: ContractInterface, signer?: Signer) => Contract>( + contract.constructor, + 'getContract' + )(contractAddress, contract.interface, contract.signer); + return { + tx: deploymentTx, + contract: deployedContract, + }; +}; diff --git a/test/utils/erc20.ts b/test/utils/erc20.ts new file mode 100644 index 0000000..1a9aecd --- /dev/null +++ b/test/utils/erc20.ts @@ -0,0 +1,20 @@ +import { BigNumber, Contract } from 'ethers'; +import { ethers } from 'hardhat'; + +export const deploy = async ({ + name, + symbol, + decimals, + initialAccount, + initialAmount, +}: { + name: string; + symbol: string; + decimals?: BigNumber | number; + initialAccount: string; + initialAmount: BigNumber; +}): Promise => { + const erc20MockContract = await ethers.getContractFactory('contracts/mocks/ERC20Mock.sol:ERC20Mock'); + const deployedContract = await erc20MockContract.deploy(name, symbol, decimals || 18, initialAccount, initialAmount); + return deployedContract; +}; diff --git a/test/utils/event-utils.ts b/test/utils/event-utils.ts new file mode 100644 index 0000000..6edbb7d --- /dev/null +++ b/test/utils/event-utils.ts @@ -0,0 +1,36 @@ +import { TransactionResponse, TransactionReceipt } from '@ethersproject/abstract-provider'; +import { expect } from 'chai'; + +export async function expectNoEventWithName(response: TransactionResponse, eventName: string) { + const receipt = await response.wait(); + for (const event of getEvents(receipt)) { + expect(event.event).not.to.equal(eventName); + } +} + +export async function readArgFromEvent(response: TransactionResponse, eventName: string, paramName: string): Promise { + const receipt = await response.wait(); + for (const event of getEvents(receipt)) { + if (event.event === eventName) { + return event.args[paramName]; + } + } +} + +export async function readArgFromEventOrFail(response: TransactionResponse, eventName: string, paramName: string): Promise { + const result = await readArgFromEvent(response, eventName, paramName); + if (result) { + return result; + } + throw new Error(`Failed to find event with name ${eventName}`); +} + +function getEvents(receipt: TransactionReceipt): Event[] { + // @ts-ignore + return receipt.events; +} + +type Event = { + event: string; // Event name + args: any; +}; diff --git a/test/utils/evm.ts b/test/utils/evm.ts new file mode 100644 index 0000000..a13d8e4 --- /dev/null +++ b/test/utils/evm.ts @@ -0,0 +1,73 @@ +import { BigNumber, BigNumberish } from 'ethers'; +import { network } from 'hardhat'; + +export const advanceTimeAndBlock = async (time: number): Promise => { + await advanceTime(time); + await advanceBlocks(1); +}; + +export const advanceToTimeAndBlock = async (time: number): Promise => { + await advanceToTime(time); + await advanceBlocks(1); +}; + +export const advanceTime = async (time: number): Promise => { + await network.provider.request({ + method: 'evm_increaseTime', + params: [time], + }); +}; + +export const advanceToTime = async (time: number): Promise => { + await network.provider.request({ + method: 'evm_setNextBlockTimestamp', + params: [time], + }); +}; + +export const advanceBlocks = async (blocks: BigNumberish) => { + blocks = !BigNumber.isBigNumber(blocks) ? BigNumber.from(`${blocks}`) : blocks; + await network.provider.request({ + method: 'hardhat_mine', + params: [blocks.toHexString().replace('0x0', '0x')], + }); +}; + +export const reset = async (forking?: { [key: string]: any }) => { + const params = forking ? [{ forking }] : []; + await network.provider.request({ + method: 'hardhat_reset', + params, + }); +}; + +class SnapshotManager { + snapshots: { [id: string]: string } = {}; + + async take(): Promise { + const id = await this.takeSnapshot(); + this.snapshots[id] = id; + return id; + } + + async revert(id: string): Promise { + await this.revertSnapshot(this.snapshots[id]); + this.snapshots[id] = await this.takeSnapshot(); + } + + private async takeSnapshot(): Promise { + return (await network.provider.request({ + method: 'evm_snapshot', + params: [], + })) as string; + } + + private async revertSnapshot(id: string) { + await network.provider.request({ + method: 'evm_revert', + params: [id], + }); + } +} + +export const snapshot = new SnapshotManager(); diff --git a/test/utils/index.ts b/test/utils/index.ts new file mode 100644 index 0000000..25abac1 --- /dev/null +++ b/test/utils/index.ts @@ -0,0 +1,8 @@ +import * as behaviours from './behaviours'; +import * as contracts from './contracts'; +import * as erc20 from './erc20'; +import * as evm from './evm'; +import * as bn from './bn'; +import * as wallet from './wallet'; + +export { contracts, behaviours, bn, erc20, evm, wallet }; diff --git a/test/utils/wallet.ts b/test/utils/wallet.ts new file mode 100644 index 0000000..2ce69a5 --- /dev/null +++ b/test/utils/wallet.ts @@ -0,0 +1,30 @@ +import { BigNumber, constants, Wallet } from 'ethers'; +import { ethers, network } from 'hardhat'; +import { JsonRpcSigner } from '@ethersproject/providers'; +import { getAddress } from 'ethers/lib/utils'; +import { randomHex } from 'web3-utils'; + +export const impersonate = async (address: string): Promise => { + await network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [address], + }); + return ethers.provider.getSigner(address); +}; + +export const generateRandom = async () => { + const wallet = (await Wallet.createRandom()).connect(ethers.provider); + await setBalance({ + account: wallet.address, + balance: constants.MaxUint256, + }); + return wallet; +}; + +export const setBalance = async ({ account, balance }: { account: string; balance: BigNumber }): Promise => { + await ethers.provider.send('hardhat_setBalance', [account, balance.toHexString().replace('0x0', '0x')]); +}; + +export const generateRandomAddress = () => { + return getAddress(randomHex(20)); +}; diff --git a/utils/deploy.ts b/utils/deploy.ts index d90f614..66158c3 100644 --- a/utils/deploy.ts +++ b/utils/deploy.ts @@ -14,12 +14,12 @@ export const getChainId = async (hre: HardhatRuntimeEnvironment): Promise { const config = hre.network.config as HardhatNetworkUserConfig; - if (config.forking?.url.includes('mainnet')) return 1; + if (config.forking?.url.includes('eth')) return 1; if (config.forking?.url.includes('ftm') || config.forking?.url.includes('fantom')) return 250; if (config.forking?.url.includes('polygon')) return 137; throw new Error('Should specify chain id of fork'); diff --git a/utils/env.ts b/utils/env.ts new file mode 100644 index 0000000..4476051 --- /dev/null +++ b/utils/env.ts @@ -0,0 +1,81 @@ +import 'dotenv/config'; + +const MAX_ACCOUNTS = 10; + +export function getNodeUrl(network: string): string { + if (network) { + const uri = process.env[`NODE_URI_${network.toUpperCase()}`]; + if (uri && uri !== '') { + return uri; + } + } + console.warn(`No node uri for network ${network}`); + return ''; +} + +export function getMnemonic(network: string): string { + const mnemonic = process.env[`${network.toUpperCase()}_MNEMONIC`] as string; + if (!mnemonic) { + console.warn(`No mnemonic for network ${network}`); + return 'test test test test test test test test test test test junk'; + } + return mnemonic; +} + +export function getPrivateKeys(network: string): string[] { + const privateKeys = []; + for (let i = 1; i <= MAX_ACCOUNTS; i++) { + const privateKey = process.env[`${network.toUpperCase()}_${i}_PRIVATE_KEY`]; + if (!!privateKey) privateKeys.push(privateKey); + } + if (privateKeys.length === 0) { + console.warn(`No private keys for network ${network}`); + } + return privateKeys; +} + +type ACCOUNTS_TYPE = 'MNEMONIC' | 'PRIVATE_KEYS'; + +export function getAccountsType(network: string): ACCOUNTS_TYPE { + const accountsType = process.env[`${network.toUpperCase()}_ACCOUNTS_TYPE`]; + if (!accountsType || accountsType === 'PRIVATE_KEYS') return 'PRIVATE_KEYS'; + if (accountsType != 'MNEMONIC' && accountsType != 'PRIVATE_KEYS') { + console.warn(`Accounts type incorrect for network ${network} using fallback`); + return 'PRIVATE_KEYS'; + } + return 'MNEMONIC'; +} + +export function getAccounts(network: string): { mnemonic: string } | string[] { + if (getAccountsType(network) == 'PRIVATE_KEYS') { + return getPrivateKeys(network); + } + return { + mnemonic: getMnemonic(network), + }; +} + +export function getEtherscanAPIKeys(networks: string[]): { [network: string]: string } { + const apiKeys: { [network: string]: string } = {}; + networks.forEach((network) => { + const networkApiKey = process.env[`${network.toUpperCase()}_ETHERSCAN_API_KEY`]; + if (!networkApiKey) { + console.warn(`No etherscan api key for ${network}`); + } else { + apiKeys[network == 'ethereum' ? 'mainnet' : network] = networkApiKey; + } + }); + return apiKeys; +} + +export function isTesting(): boolean { + return !!process.env.TEST; +} + +export function isHardhatCompile(): boolean { + return process.argv[process.argv.length - 1] == 'compile'; +} + +export function isHardhatClean(): boolean { + return process.argv[process.argv.length - 1] == 'clean'; +} diff --git a/yarn.lock b/yarn.lock index cea93fd..147f923 100644 --- a/yarn.lock +++ b/yarn.lock @@ -607,31 +607,6 @@ dependencies: "@ethersproject/logger" "^5.6.0" -"@ethersproject/providers@5.6.4": - version "5.6.4" - resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.6.4.tgz#1a49c211b57b0b2703c320819abbbfa35c83dff7" - integrity sha512-WAdknnaZ52hpHV3qPiJmKx401BLpup47h36Axxgre9zT+doa/4GC/Ne48ICPxTm0BqndpToHjpLP1ZnaxyE+vw== - dependencies: - "@ethersproject/abstract-provider" "^5.6.0" - "@ethersproject/abstract-signer" "^5.6.0" - "@ethersproject/address" "^5.6.0" - "@ethersproject/basex" "^5.6.0" - "@ethersproject/bignumber" "^5.6.0" - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/constants" "^5.6.0" - "@ethersproject/hash" "^5.6.0" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/networks" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/random" "^5.6.0" - "@ethersproject/rlp" "^5.6.0" - "@ethersproject/sha2" "^5.6.0" - "@ethersproject/strings" "^5.6.0" - "@ethersproject/transactions" "^5.6.0" - "@ethersproject/web" "^5.6.0" - bech32 "1.1.4" - ws "7.4.6" - "@ethersproject/providers@5.6.5", "@ethersproject/providers@^5.4.4": version "5.6.5" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.6.5.tgz#aefecf78459817a323452e05a16d56afcf807e27" @@ -682,18 +657,6 @@ "@ethersproject/logger" "^5.6.0" hash.js "1.1.7" -"@ethersproject/signing-key@5.6.0": - version "5.6.0" - resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.6.0.tgz#4f02e3fb09e22b71e2e1d6dc4bcb5dafa69ce042" - integrity sha512-S+njkhowmLeUu/r7ir8n78OUKx63kBdMCPssePS89So1TH4hZqnWFsThEd/GiXYp9qMxVrydf7KdM9MTGPFukA== - dependencies: - "@ethersproject/bytes" "^5.6.0" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - bn.js "^4.11.9" - elliptic "6.5.4" - hash.js "1.1.7" - "@ethersproject/signing-key@5.6.1", "@ethersproject/signing-key@^5.6.0": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.6.1.tgz#31b0a531520616254eb0465b9443e49515c4d457" @@ -880,7 +843,7 @@ semver "^6.3.0" undici "^4.14.1" -"@nomiclabs/hardhat-vyper@^3.0.0": +"@nomiclabs/hardhat-vyper@3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-vyper/-/hardhat-vyper-3.0.0.tgz#36a9add9e2e465a26e53acd564f538215705c646" integrity sha512-IxKPX5t2DKtqlwchKIfgAbaySwKrtzc4SgoGEVlbaLptwFBVAMY9yii9IFWX0MNTTsqbMmavCe5CcUsK/2lSKw== @@ -4782,10 +4745,10 @@ ethereumjs-wallet@0.6.5: utf8 "^3.0.0" uuid "^3.3.2" -ethers@5.6.4: - version "5.6.4" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.6.4.tgz#23629e9a7d4bc5802dfb53d4da420d738744b53c" - integrity sha512-62UIfxAQXdf67TeeOaoOoPctm5hUlYgfd0iW3wxfj7qRYKDcvvy0f+sJ3W2/Pyx77R8dblvejA8jokj+lS+ATQ== +ethers@5.6.5, ethers@^5.0.1, ethers@^5.0.2, ethers@^5.5.2: + version "5.6.5" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.6.5.tgz#3185ac7815dc640993408adf6f133ffabfbcbb63" + integrity sha512-9CTmplO9bv0s/aPw3HB3txGzKz3tUSI2EfO4dJo0W2WvaEq1ArgsEX6obV+bj5X3yY+Zgb1kAux8TDtJKe1FaA== dependencies: "@ethersproject/abi" "5.6.1" "@ethersproject/abstract-provider" "5.6.0" @@ -4805,11 +4768,11 @@ ethers@5.6.4: "@ethersproject/networks" "5.6.2" "@ethersproject/pbkdf2" "5.6.0" "@ethersproject/properties" "5.6.0" - "@ethersproject/providers" "5.6.4" + "@ethersproject/providers" "5.6.5" "@ethersproject/random" "5.6.0" "@ethersproject/rlp" "5.6.0" "@ethersproject/sha2" "5.6.0" - "@ethersproject/signing-key" "5.6.0" + "@ethersproject/signing-key" "5.6.1" "@ethersproject/solidity" "5.6.0" "@ethersproject/strings" "5.6.0" "@ethersproject/transactions" "5.6.0" @@ -4833,42 +4796,6 @@ ethers@^4.0.32, ethers@^4.0.40: uuid "2.0.1" xmlhttprequest "1.8.0" -ethers@^5.0.1, ethers@^5.0.2, ethers@^5.5.2: - version "5.6.5" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.6.5.tgz#3185ac7815dc640993408adf6f133ffabfbcbb63" - integrity sha512-9CTmplO9bv0s/aPw3HB3txGzKz3tUSI2EfO4dJo0W2WvaEq1ArgsEX6obV+bj5X3yY+Zgb1kAux8TDtJKe1FaA== - dependencies: - "@ethersproject/abi" "5.6.1" - "@ethersproject/abstract-provider" "5.6.0" - "@ethersproject/abstract-signer" "5.6.0" - "@ethersproject/address" "5.6.0" - "@ethersproject/base64" "5.6.0" - "@ethersproject/basex" "5.6.0" - "@ethersproject/bignumber" "5.6.0" - "@ethersproject/bytes" "5.6.1" - "@ethersproject/constants" "5.6.0" - "@ethersproject/contracts" "5.6.0" - "@ethersproject/hash" "5.6.0" - "@ethersproject/hdnode" "5.6.0" - "@ethersproject/json-wallets" "5.6.0" - "@ethersproject/keccak256" "5.6.0" - "@ethersproject/logger" "5.6.0" - "@ethersproject/networks" "5.6.2" - "@ethersproject/pbkdf2" "5.6.0" - "@ethersproject/properties" "5.6.0" - "@ethersproject/providers" "5.6.5" - "@ethersproject/random" "5.6.0" - "@ethersproject/rlp" "5.6.0" - "@ethersproject/sha2" "5.6.0" - "@ethersproject/signing-key" "5.6.1" - "@ethersproject/solidity" "5.6.0" - "@ethersproject/strings" "5.6.0" - "@ethersproject/transactions" "5.6.0" - "@ethersproject/units" "5.6.0" - "@ethersproject/wallet" "5.6.0" - "@ethersproject/web" "5.6.0" - "@ethersproject/wordlists" "5.6.0" - ethjs-unit@0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699"