diff --git a/.github/workflows/CI.yaml b/.github/workflows/CI.yaml index 9c1ab13b..04de2c3d 100644 --- a/.github/workflows/CI.yaml +++ b/.github/workflows/CI.yaml @@ -51,7 +51,9 @@ jobs: - run: npm run compile env: { SKIP_LOAD: true } - run: npm run test:gas - env: { SKIP_LOAD: true } + env: + SKIP_LOAD: true + MAINNET_FORK_RPC_URL: ${{ secrets.MAINNET_FORK_RPC_URL }} - run: npm run coverage env: { SKIP_LOAD: true } - uses: codecov/codecov-action@v3 diff --git a/contracts/interfaces/IEthPrivErc20Vault.sol b/contracts/interfaces/IEthPrivErc20Vault.sol index 89a84eb1..0fbab335 100644 --- a/contracts/interfaces/IEthPrivErc20Vault.sol +++ b/contracts/interfaces/IEthPrivErc20Vault.sol @@ -10,6 +10,4 @@ import {IEthErc20Vault} from './IEthErc20Vault.sol'; * @author StakeWise * @notice Defines the interface for the EthPrivErc20Vault contract */ -interface IEthPrivErc20Vault is IEthErc20Vault, IVaultWhitelist { - -} +interface IEthPrivErc20Vault is IEthErc20Vault, IVaultWhitelist {} diff --git a/contracts/interfaces/IEthPrivVault.sol b/contracts/interfaces/IEthPrivVault.sol index 3f694827..75ef0117 100644 --- a/contracts/interfaces/IEthPrivVault.sol +++ b/contracts/interfaces/IEthPrivVault.sol @@ -10,6 +10,4 @@ import {IEthVault} from './IEthVault.sol'; * @author StakeWise * @notice Defines the interface for the EthPrivVault contract */ -interface IEthPrivVault is IEthVault, IVaultWhitelist { - -} +interface IEthPrivVault is IEthVault, IVaultWhitelist {} diff --git a/contracts/interfaces/IVaultToken.sol b/contracts/interfaces/IVaultToken.sol index 599886a2..c7b55f24 100644 --- a/contracts/interfaces/IVaultToken.sol +++ b/contracts/interfaces/IVaultToken.sol @@ -13,6 +13,4 @@ import {IVaultEnterExit} from './IVaultEnterExit.sol'; * @author StakeWise * @notice Defines the interface for the VaultToken contract */ -interface IVaultToken is IERC20Permit, IERC20, IERC20Metadata, IVaultState, IVaultEnterExit { - -} +interface IVaultToken is IERC20Permit, IERC20, IERC20Metadata, IVaultState, IVaultEnterExit {} diff --git a/hardhat.config.ts b/hardhat.config.ts index fed50385..d615f202 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -12,7 +12,7 @@ import '@openzeppelin/hardhat-upgrades' dotenv.config({ path: '.env' }) import { Networks } from './helpers/types' -import { NETWORKS } from './helpers/constants' +import { MAINNET_FORK, NETWORKS } from './helpers/constants' if (!process.env.SKIP_LOAD) { glob.sync('./tasks/*.ts').forEach((file) => { @@ -28,6 +28,17 @@ const IS_COVERAGE = process.env.COVERAGE === 'true' const BLOCK_EXPLORER_KEY = process.env.BLOCK_EXPLORER_KEY || '' const HARDHATEVM_CHAINID = 31337 +// fork +const mainnetFork = process.env.MAINNET_FORK_RPC_URL + ? { + blockNumber: MAINNET_FORK.blockNumber, + url: process.env.MAINNET_FORK_RPC_URL, + } + : undefined +if (mainnetFork) { + console.log(`Using mainnet fork at block ${mainnetFork.blockNumber}`) +} + const getCommonNetworkConfig = (networkName) => { return { url: NETWORKS[networkName].url, @@ -67,13 +78,13 @@ const config: HardhatUserConfig = { hardhat: { blockGasLimit: DEFAULT_BLOCK_GAS_LIMIT, gas: DEFAULT_BLOCK_GAS_LIMIT, - gasPrice: 8000000000, chainId: HARDHATEVM_CHAINID, throwOnTransactionFailures: true, throwOnCallFailures: true, accounts: { accountsBalance: '1000000000000000000000000', }, + forking: mainnetFork, }, local: { url: 'http://127.0.0.1:8545/', diff --git a/helpers/constants.ts b/helpers/constants.ts index e14b7de7..12c8e28a 100644 --- a/helpers/constants.ts +++ b/helpers/constants.ts @@ -41,8 +41,8 @@ export const NETWORKS: { osTokenCapacity: parseEther('1000000'), osTokenName: 'SW Staked ETH', osTokenSymbol: 'osETH', - redeemFromLtvPercent: 9150n, // 91.5% - redeemToLtvPercent: 9000n, // 90% + redeemFromLtvPercent: 65535n, // disabled + redeemToLtvPercent: 65535n, // disabled liqThresholdPercent: 9200, // 92% liqBonusPercent: 10100, // 101% ltvPercent: 9000, // 90% @@ -227,3 +227,34 @@ export const ethValidatorsRegistry = { bytecode: '0x608060405234801561001057600080fd5b5060005b601f8110156101025760026021826020811061002c57fe5b01546021836020811061003b57fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b602083106100925780518252601f199092019160209182019101610073565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa1580156100d1573d6000803e3d6000fd5b5050506040513d60208110156100e657600080fd5b5051602160018301602081106100f857fe5b0155600101610014565b506118d680620001136000396000f3fe60806040526004361061003f5760003560e01c806301ffc9a71461004457806322895118146100a4578063621fd130146101ba578063c5f2892f14610244575b600080fd5b34801561005057600080fd5b506100906004803603602081101561006757600080fd5b50357fffffffff000000000000000000000000000000000000000000000000000000001661026b565b604080519115158252519081900360200190f35b6101b8600480360360808110156100ba57600080fd5b8101906020810181356401000000008111156100d557600080fd5b8201836020820111156100e757600080fd5b8035906020019184600183028401116401000000008311171561010957600080fd5b91939092909160208101903564010000000081111561012757600080fd5b82018360208201111561013957600080fd5b8035906020019184600183028401116401000000008311171561015b57600080fd5b91939092909160208101903564010000000081111561017957600080fd5b82018360208201111561018b57600080fd5b803590602001918460018302840111640100000000831117156101ad57600080fd5b919350915035610304565b005b3480156101c657600080fd5b506101cf6110b5565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102095781810151838201526020016101f1565b50505050905090810190601f1680156102365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561025057600080fd5b506102596110c7565b60408051918252519081900360200190f35b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a70000000000000000000000000000000000000000000000000000000014806102fe57507fffffffff0000000000000000000000000000000000000000000000000000000082167f8564090700000000000000000000000000000000000000000000000000000000145b92915050565b6030861461035d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118056026913960400191505060405180910390fd5b602084146103b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061179c6036913960400191505060405180910390fd5b6060821461040f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806118786029913960400191505060405180910390fd5b670de0b6b3a7640000341015610470576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118526026913960400191505060405180910390fd5b633b9aca003406156104cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260338152602001806117d26033913960400191505060405180910390fd5b633b9aca00340467ffffffffffffffff811115610535576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061182b6027913960400191505060405180910390fd5b6060610540826114ba565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a6105756020546114ba565b6040805160a0808252810189905290819060208201908201606083016080840160c085018e8e80828437600083820152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690910187810386528c815260200190508c8c808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690920188810386528c5181528c51602091820193918e019250908190849084905b83811015610648578181015183820152602001610630565b50505050905090810190601f1680156106755780820380516001836020036101000a031916815260200191505b5086810383528881526020018989808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169092018881038452895181528951602091820193918b019250908190849084905b838110156106ef5781810151838201526020016106d7565b50505050905090810190601f16801561071c5780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b604051602001808484808284377fffffffffffffffffffffffffffffffff0000000000000000000000000000000090941691909301908152604080517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0818403018152601090920190819052815191955093508392506020850191508083835b602083106107fc57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107bf565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610859573d6000803e3d6000fd5b5050506040513d602081101561086e57600080fd5b5051905060006002806108846040848a8c6116fe565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106108f857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016108bb565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610955573d6000803e3d6000fd5b5050506040513d602081101561096a57600080fd5b5051600261097b896040818d6116fe565b60405160009060200180848480828437919091019283525050604080518083038152602092830191829052805190945090925082918401908083835b602083106109f457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016109b7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610a51573d6000803e3d6000fd5b5050506040513d6020811015610a6657600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610ada57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610a9d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610b37573d6000803e3d6000fd5b5050506040513d6020811015610b4c57600080fd5b50516040805160208101858152929350600092600292839287928f928f92018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b60208310610bd957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610b9c565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610c36573d6000803e3d6000fd5b5050506040513d6020811015610c4b57600080fd5b50516040518651600291889160009188916020918201918291908601908083835b60208310610ca957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610c6c565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610d4e57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610d11565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610dab573d6000803e3d6000fd5b5050506040513d6020811015610dc057600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610e3457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610df7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610e91573d6000803e3d6000fd5b5050506040513d6020811015610ea657600080fd5b50519050858114610f02576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260548152602001806117486054913960600191505060405180910390fd5b60205463ffffffff11610f60576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806117276021913960400191505060405180910390fd5b602080546001019081905560005b60208110156110a9578160011660011415610fa0578260008260208110610f9157fe5b0155506110ac95505050505050565b600260008260208110610faf57fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061102557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610fe8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015611082573d6000803e3d6000fd5b5050506040513d602081101561109757600080fd5b50519250600282049150600101610f6e565b50fe5b50505050505050565b60606110c26020546114ba565b905090565b6020546000908190815b60208110156112f05781600116600114156111e6576002600082602081106110f557fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061116b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161112e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156111c8573d6000803e3d6000fd5b5050506040513d60208110156111dd57600080fd5b505192506112e2565b600283602183602081106111f657fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061126b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161122e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156112c8573d6000803e3d6000fd5b5050506040513d60208110156112dd57600080fd5b505192505b6002820491506001016110d1565b506002826112ff6020546114ba565b600060401b6040516020018084815260200183805190602001908083835b6020831061135a57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161131d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790527fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000095909516920191825250604080518083037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8018152601890920190819052815191955093508392850191508083835b6020831061143f57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101611402565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa15801561149c573d6000803e3d6000fd5b5050506040513d60208110156114b157600080fd5b50519250505090565b60408051600880825281830190925260609160208201818036833701905050905060c082901b8060071a60f81b826000815181106114f457fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060061a60f81b8260018151811061153757fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060051a60f81b8260028151811061157a57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060041a60f81b826003815181106115bd57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060031a60f81b8260048151811061160057fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060021a60f81b8260058151811061164357fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060011a60f81b8260068151811061168657fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060001a60f81b826007815181106116c957fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535050919050565b6000808585111561170d578182fd5b83861115611719578182fd5b505082019391909203915056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a2646970667358221220dceca8706b29e917dacf25fceef95acac8d90d765ac926663ce4096195952b6164736f6c634300060b0033', } + +export const MAINNET_FORK = { + enabled: Boolean(process.env.MAINNET_FORK_RPC_URL), + blockNumber: 18870000, + rpcUrl: process.env.MAINNET_FORK_RPC_URL, + genesisVaultHarvestParams: { + proofReward: 8222516177498228168659n, + proofUnlockedMevReward: 57054334659094598004n, + proof: [ + '0xc2c1334f12ed58092112b785bc6000b469dbeac453d3accd29b3317ba4edc3fa', + '0x23c4f7627f6a2eaad242a9a0d499e2b9a70e2e2e05535204bc9898156e45279f', + '0x5ece033fbed4c67d6e9e24b779af765960cecc47021ddb2816288011f6f84fe2', + '0x584ed8ee053fa2afd3c0b512066bf73c3275a466bd0b866c04ea96c5aa49f42b', + '0xcf299fc681f982707bed35ae96898bb1b4fa13e4d0558c78e1d34e24e861e6b0', + ], + }, + oracles: [ + '0x6D403394848EaD12356C9Bb667ED27bCe1945914', + '0xED5a1c366984215A28a95bE95A9a49d59a065e91', + '0x20B04EcB2bc5E44Ac5AaAd9c8DD3cd04d9Fb87c8', + '0x4E81bfde2eb1574bf0839aDEFb65cEA0D8B07EFC', + '0x49F436341dbB3ffFce92C59fBcfcAEdaD22D0b0e', + '0x624EC1141Eb0C3bE58b382737718852665c35Cf0', + '0x671D846eCd7D945011912a6fa42E6F3E39eD0569', + '0x3F77cC37b5F49561E84e36D87FAe1F032E1f771e', + '0xa9Ccb8ba942C45F6Fa786F936679812591dA012a', + '0xb5dBd61DAb7138aF20A61614e0A4587566C2A15A', + '0x8Ce4f2800dE6476F42a070C79AfA58E0E209173e', + ], + v2PoolHolder: '0x56556075Ab3e2Bb83984E90C52850AFd38F20883', +} diff --git a/package-lock.json b/package-lock.json index 3a491a31..b2129d98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,17 +14,17 @@ }, "devDependencies": { "@chainsafe/ssz": "0.14.0", - "@metamask/eth-sig-util": "7.0.0", - "@nomicfoundation/hardhat-toolbox": "3.0.0", - "@openzeppelin/hardhat-upgrades": "2.3.3", + "@metamask/eth-sig-util": "7.0.1", + "@nomicfoundation/hardhat-toolbox": "4.0.0", + "@openzeppelin/hardhat-upgrades": "3.0.1", "@openzeppelin/merkle-tree": "1.0.5", - "@typescript-eslint/eslint-plugin": "6.9.0", - "@typescript-eslint/parser": "6.9.0", - "bls-eth-wasm": "1.1.0", + "@typescript-eslint/eslint-plugin": "6.15.0", + "@typescript-eslint/parser": "6.15.0", + "bls-eth-wasm": "1.1.1", "dotenv": "16.3.1", - "eslint": "8.52.0", - "eslint-config-prettier": "9.0.0", - "eslint-plugin-prettier": "5.0.1", + "eslint": "8.56.0", + "eslint-config-prettier": "9.1.0", + "eslint-plugin-prettier": "5.1.2", "ethereumjs-wallet": "1.0.2", "hardhat-abi-exporter": "2.10.1", "hardhat-contract-sizer": "2.10.0", @@ -32,10 +32,10 @@ "hardhat-spdx-license-identifier": "2.2.0", "husky": "8.0.3", "keccak256": "1.0.6", - "lint-staged": "15.0.2", + "lint-staged": "15.2.0", "mocha-chai-jest-snapshot": "1.1.4", - "prettier": "3.0.3", - "prettier-plugin-solidity": "1.1.3" + "prettier": "3.1.1", + "prettier-plugin-solidity": "1.3.1" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -90,12 +90,12 @@ } }, "node_modules/@aws-sdk/types": { - "version": "3.433.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.433.0.tgz", - "integrity": "sha512-0jEE2mSrNDd8VGFjTc1otYrwYPIkzZJEIK90ZxisKvQ/EURGBhNzWn7ejWB9XCMFT6XumYLBR0V9qq5UPisWtA==", + "version": "3.468.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.468.0.tgz", + "integrity": "sha512-rx/9uHI4inRbp2tw3Y4Ih4PNZkVj32h7WneSg3MVgVjAoVD5Zti9KhS5hkvsBxfgmQmg0AQbE+b1sy5WGAgntA==", "dev": true, "dependencies": { - "@smithy/types": "^2.4.0", + "@smithy/types": "^2.7.0", "tslib": "^2.5.0" }, "engines": { @@ -694,9 +694,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", - "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -717,9 +717,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz", - "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2044,16 +2044,15 @@ } }, "node_modules/@metamask/eth-sig-util": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-7.0.0.tgz", - "integrity": "sha512-8KeXZB4SKx3EfNS5ahbjUMegyGvDQYk6Nk3hmM658sXpfAQR5ZlIXBgj+9RF+ZROqsU6EuNVgKt7Fr10re60PQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-7.0.1.tgz", + "integrity": "sha512-59GSrMyFH2fPfu7nKeIQdZ150zxXNNhAQIUaFRUW+MGtVA4w/ONbiQobcRBLi+jQProfIyss51G8pfLPcQ0ylg==", "dev": true, "dependencies": { "@ethereumjs/util": "^8.1.0", "@metamask/abi-utils": "^2.0.2", "@metamask/utils": "^8.1.0", "ethereum-cryptography": "^2.1.2", - "ethjs-util": "^0.1.6", "tweetnacl": "^1.0.3", "tweetnacl-util": "^0.15.1" }, @@ -2778,34 +2777,34 @@ } }, "node_modules/@nomicfoundation/hardhat-toolbox": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-toolbox/-/hardhat-toolbox-3.0.0.tgz", - "integrity": "sha512-MsteDXd0UagMksqm9KvcFG6gNKYNa3GGNCy73iQ6bEasEgg2v8Qjl6XA5hjs8o5UD5A3153B6W2BIVJ8SxYUtA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-toolbox/-/hardhat-toolbox-4.0.0.tgz", + "integrity": "sha512-jhcWHp0aHaL0aDYj8IJl80v4SZXWMS1A2XxXa1CA6pBiFfJKuZinCkO6wb+POAt0LIfXB3gA3AgdcOccrcwBwA==", "dev": true, "peerDependencies": { "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", "@nomicfoundation/hardhat-ethers": "^3.0.0", "@nomicfoundation/hardhat-network-helpers": "^1.0.0", - "@nomicfoundation/hardhat-verify": "^1.0.0", - "@typechain/ethers-v6": "^0.4.0", - "@typechain/hardhat": "^8.0.0", + "@nomicfoundation/hardhat-verify": "^2.0.0", + "@typechain/ethers-v6": "^0.5.0", + "@typechain/hardhat": "^9.0.0", "@types/chai": "^4.2.0", "@types/mocha": ">=9.1.0", - "@types/node": ">=12.0.0", + "@types/node": ">=16.0.0", "chai": "^4.2.0", "ethers": "^6.4.0", "hardhat": "^2.11.0", "hardhat-gas-reporter": "^1.0.8", "solidity-coverage": "^0.8.1", "ts-node": ">=8.0.0", - "typechain": "^8.2.0", + "typechain": "^8.3.0", "typescript": ">=4.5.0" } }, "node_modules/@nomicfoundation/hardhat-verify": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-verify/-/hardhat-verify-1.1.1.tgz", - "integrity": "sha512-9QsTYD7pcZaQFEA3tBb/D/oCStYDiEVDN7Dxeo/4SCyHRSm86APypxxdOMEPlGmXsAvd+p1j/dTODcpxb8aztA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-verify/-/hardhat-verify-2.0.3.tgz", + "integrity": "sha512-ESbRu9by53wu6VvgwtMtm108RSmuNsVqXtzg061D+/4R7jaWh/Wl/8ve+p6SdDX7vA1Z3L02hDO1Q3BY4luLXQ==", "dev": true, "peer": true, "dependencies": { @@ -3039,12 +3038,12 @@ } }, "node_modules/@openzeppelin/defender-admin-client": { - "version": "1.50.0", - "resolved": "https://registry.npmjs.org/@openzeppelin/defender-admin-client/-/defender-admin-client-1.50.0.tgz", - "integrity": "sha512-JxeA111ifCIzXho2gFymhepufB0ElI1UMvFIMEfJLvRL7g7V69wSiN8v+OqZyqZTahiY32Rb+TwdhVjKF5Zu+A==", + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/@openzeppelin/defender-admin-client/-/defender-admin-client-1.54.1.tgz", + "integrity": "sha512-kRpSUdTsnSqntp4FOXIm95t+6VKHc8CUY2Si71VDuxs0q7HSPZkdpRPSntcolwEzWy9L4a8NS/QMwDF5NJ4X1g==", "dev": true, "dependencies": { - "@openzeppelin/defender-base-client": "1.50.0", + "@openzeppelin/defender-base-client": "1.54.1", "axios": "^1.4.0", "ethers": "^5.7.2", "lodash": "^4.17.19", @@ -3100,9 +3099,9 @@ } }, "node_modules/@openzeppelin/defender-base-client": { - "version": "1.50.0", - "resolved": "https://registry.npmjs.org/@openzeppelin/defender-base-client/-/defender-base-client-1.50.0.tgz", - "integrity": "sha512-V5uJ4t3kr9ex1RrqGH2DwsHuyW7/hl3VK0sSkq3VVbAewtcsW3cdg/UkXd5ITu6mtz76RoYkvUBHtkYUm0nb+w==", + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/@openzeppelin/defender-base-client/-/defender-base-client-1.54.1.tgz", + "integrity": "sha512-DRGz/7KN3ZQwu28YWMOaojrC7jjPkz/uCwkC8/C8B11qwZhA5qIVvyhYHhhFOCl0J84+E3TNdvkPD2q3p2WaJw==", "dev": true, "dependencies": { "amazon-cognito-identity-js": "^6.0.1", @@ -3113,50 +3112,50 @@ } }, "node_modules/@openzeppelin/defender-sdk-base-client": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@openzeppelin/defender-sdk-base-client/-/defender-sdk-base-client-1.3.0.tgz", - "integrity": "sha512-OMMt7NaAL8C95ralF9nMeKZpg96COLZT9FPpGpPsI7aB8fVZfCM8+6k99gTF44hMS6IsRdN2WthS3m7VzQeeoA==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/defender-sdk-base-client/-/defender-sdk-base-client-1.8.0.tgz", + "integrity": "sha512-XIJat6BW2CTM74AwG5IL0Q/aE6RXj8x7smnVKmBql4wMvmirVW+njfwzZCLhUTiBXg9AlHxIInEF14SabfIisg==", "dev": true, "dependencies": { - "amazon-cognito-identity-js": "^6.0.1", + "amazon-cognito-identity-js": "^6.3.6", "async-retry": "^1.3.3" } }, "node_modules/@openzeppelin/defender-sdk-deploy-client": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@openzeppelin/defender-sdk-deploy-client/-/defender-sdk-deploy-client-1.3.0.tgz", - "integrity": "sha512-RTYM3HnVvD2d5NoYfTug8UwT41e0Jjwb13lk9v0Jl8z7mcclUVvAnKD4DHJ4b8RhKpg4B15oLQK/Igzjg1HHRA==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/defender-sdk-deploy-client/-/defender-sdk-deploy-client-1.8.0.tgz", + "integrity": "sha512-/tNS2EnHuA5l095wzMbIkGMDNHZLcZQ2eLUP8z+AeKaAUeR2z4qzZ1ul21kR3EJURAyoy8aULFZanLggoBWHrA==", "dev": true, "dependencies": { - "@ethersproject/abi": "^5.6.3", - "@openzeppelin/defender-sdk-base-client": "^1.3.0", + "@ethersproject/abi": "^5.7.0", + "@openzeppelin/defender-sdk-base-client": "^1.8.0", "axios": "^1.4.0", "lodash": "^4.17.21" } }, "node_modules/@openzeppelin/hardhat-upgrades": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@openzeppelin/hardhat-upgrades/-/hardhat-upgrades-2.3.3.tgz", - "integrity": "sha512-rF87xYSz6Q2WFq5zcUF1T1tx3Kiq83hmke0xOBn5Qgrl++osseiDwS5oXfDK3NSWvj06oYGLERRGHcXnpQ31FQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@openzeppelin/hardhat-upgrades/-/hardhat-upgrades-3.0.1.tgz", + "integrity": "sha512-NtD2/n2PKNqHBafQy3AM6KCvsDZD0w97po7fFa4wctl0fg/8QwGAg3fB8InkBFEhGn17+IgshRI8G94hUrFPcQ==", "dev": true, "dependencies": { - "@openzeppelin/defender-admin-client": "^1.48.0", - "@openzeppelin/defender-base-client": "^1.48.0", - "@openzeppelin/defender-sdk-base-client": "^1.2.0", - "@openzeppelin/defender-sdk-deploy-client": "^1.2.0", - "@openzeppelin/upgrades-core": "^1.30.1", + "@openzeppelin/defender-admin-client": "^1.52.0", + "@openzeppelin/defender-base-client": "^1.52.0", + "@openzeppelin/defender-sdk-base-client": "^1.8.0", + "@openzeppelin/defender-sdk-deploy-client": "^1.8.0", + "@openzeppelin/upgrades-core": "^1.32.0", "chalk": "^4.1.0", "debug": "^4.1.1", "ethereumjs-util": "^7.1.5", "proper-lockfile": "^4.1.1", - "undici": "^5.14.0" + "undici": "^5.28.2" }, "bin": { "migrate-oz-cli-project": "dist/scripts/migrate-oz-cli-project.js" }, "peerDependencies": { "@nomicfoundation/hardhat-ethers": "^3.0.0", - "@nomicfoundation/hardhat-verify": "^1.1.0", + "@nomicfoundation/hardhat-verify": "^2.0.0", "ethers": "^6.6.0", "hardhat": "^2.0.2" }, @@ -3304,9 +3303,9 @@ } }, "node_modules/@openzeppelin/upgrades-core": { - "version": "1.31.0", - "resolved": "https://registry.npmjs.org/@openzeppelin/upgrades-core/-/upgrades-core-1.31.0.tgz", - "integrity": "sha512-E1Cz8lVpo2mnBeFWxiGDLWtuTYMFNTEWwbnhle4dZ+5UHX6xTRO+Q/CaWBHm33HHhuuiUbRwgGNnAR9zOu+fyQ==", + "version": "1.32.2", + "resolved": "https://registry.npmjs.org/@openzeppelin/upgrades-core/-/upgrades-core-1.32.2.tgz", + "integrity": "sha512-EkXriOHZfn6u00Tbq0zUuhHDeTQB9WyAZKZo3UeYdqULb7E3vqxZgxgXmWJwEzAb6E77DvprzQ4gwCAjMV/S4Q==", "dev": true, "dependencies": { "cbor": "^9.0.0", @@ -3582,9 +3581,9 @@ "dev": true }, "node_modules/@smithy/types": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.4.0.tgz", - "integrity": "sha512-iH1Xz68FWlmBJ9vvYeHifVMWJf82ONx+OybPW8ZGf5wnEv2S0UXcU4zwlwJkRXuLKpcSLHrraHbn2ucdVXLb4g==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.7.0.tgz", + "integrity": "sha512-1OIFyhK+vOkMbu4aN2HZz/MomREkrAC/HqY5mlJMUJfGrPRwijJDTeiN8Rnj9zUaB8ogXAfIOtZrrgqZ4w7Wnw==", "dev": true, "dependencies": { "tslib": "^2.5.0" @@ -3638,9 +3637,9 @@ "peer": true }, "node_modules/@typechain/ethers-v6": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@typechain/ethers-v6/-/ethers-v6-0.4.3.tgz", - "integrity": "sha512-TrxBsyb4ryhaY9keP6RzhFCviWYApcLCIRMPyWaKp2cZZrfaM3QBoxXTnw/eO4+DAY3l+8O0brNW0WgeQeOiDA==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@typechain/ethers-v6/-/ethers-v6-0.5.1.tgz", + "integrity": "sha512-F+GklO8jBWlsaVV+9oHaPh5NJdd6rAKN4tklGfInX1Q7h0xPgVLP39Jl3eCulPB5qexI71ZFHwbljx4ZXNfouA==", "dev": true, "peer": true, "dependencies": { @@ -3649,24 +3648,24 @@ }, "peerDependencies": { "ethers": "6.x", - "typechain": "^8.3.1", + "typechain": "^8.3.2", "typescript": ">=4.7.0" } }, "node_modules/@typechain/hardhat": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/@typechain/hardhat/-/hardhat-8.0.3.tgz", - "integrity": "sha512-MytSmJJn+gs7Mqrpt/gWkTCOpOQ6ZDfRrRT2gtZL0rfGe4QrU4x9ZdW15fFbVM/XTa+5EsKiOMYXhRABibNeng==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@typechain/hardhat/-/hardhat-9.1.0.tgz", + "integrity": "sha512-mtaUlzLlkqTlfPwB3FORdejqBskSnh+Jl8AIJGjXNAQfRQ4ofHADPl1+oU7Z3pAJzmZbUXII8MhOLQltcHgKnA==", "dev": true, "peer": true, "dependencies": { "fs-extra": "^9.1.0" }, "peerDependencies": { - "@typechain/ethers-v6": "^0.4.3", + "@typechain/ethers-v6": "^0.5.1", "ethers": "^6.1.0", "hardhat": "^2.9.9", - "typechain": "^8.3.1" + "typechain": "^8.3.2" } }, "node_modules/@types/babel__traverse": { @@ -3778,9 +3777,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.14", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", - "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "node_modules/@types/lru-cache": { @@ -3869,9 +3868,9 @@ } }, "node_modules/@types/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", "dev": true }, "node_modules/@types/stack-utils": { @@ -3896,16 +3895,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.9.0.tgz", - "integrity": "sha512-lgX7F0azQwRPB7t7WAyeHWVfW1YJ9NIgd9mvGhfQpRY56X6AVf8mwM8Wol+0z4liE7XX3QOt8MN1rUKCfSjRIA==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.15.0.tgz", + "integrity": "sha512-j5qoikQqPccq9QoBAupOP+CBu8BaJ8BLjaXSioDISeTZkVO3ig7oSIKh3H+rEpee7xCXtWwSB4KIL5l6hWZzpg==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.9.0", - "@typescript-eslint/type-utils": "6.9.0", - "@typescript-eslint/utils": "6.9.0", - "@typescript-eslint/visitor-keys": "6.9.0", + "@typescript-eslint/scope-manager": "6.15.0", + "@typescript-eslint/type-utils": "6.15.0", + "@typescript-eslint/utils": "6.15.0", + "@typescript-eslint/visitor-keys": "6.15.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -3931,15 +3930,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.9.0.tgz", - "integrity": "sha512-GZmjMh4AJ/5gaH4XF2eXA8tMnHWP+Pm1mjQR2QN4Iz+j/zO04b9TOvJYOX2sCNIQHtRStKTxRY1FX7LhpJT4Gw==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.15.0.tgz", + "integrity": "sha512-MkgKNnsjC6QwcMdlNAel24jjkEO/0hQaMDLqP4S9zq5HBAUJNQB6y+3DwLjX7b3l2b37eNAxMPLwb3/kh8VKdA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.9.0", - "@typescript-eslint/types": "6.9.0", - "@typescript-eslint/typescript-estree": "6.9.0", - "@typescript-eslint/visitor-keys": "6.9.0", + "@typescript-eslint/scope-manager": "6.15.0", + "@typescript-eslint/types": "6.15.0", + "@typescript-eslint/typescript-estree": "6.15.0", + "@typescript-eslint/visitor-keys": "6.15.0", "debug": "^4.3.4" }, "engines": { @@ -3959,13 +3958,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.0.tgz", - "integrity": "sha512-1R8A9Mc39n4pCCz9o79qRO31HGNDvC7UhPhv26TovDsWPBDx+Sg3rOZdCELIA3ZmNoWAuxaMOT7aWtGRSYkQxw==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.15.0.tgz", + "integrity": "sha512-+BdvxYBltqrmgCNu4Li+fGDIkW9n//NrruzG9X1vBzaNK+ExVXPoGB71kneaVw/Jp+4rH/vaMAGC6JfMbHstVg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.9.0", - "@typescript-eslint/visitor-keys": "6.9.0" + "@typescript-eslint/types": "6.15.0", + "@typescript-eslint/visitor-keys": "6.15.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -3976,13 +3975,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.9.0.tgz", - "integrity": "sha512-XXeahmfbpuhVbhSOROIzJ+b13krFmgtc4GlEuu1WBT+RpyGPIA4Y/eGnXzjbDj5gZLzpAXO/sj+IF/x2GtTMjQ==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.15.0.tgz", + "integrity": "sha512-CnmHKTfX6450Bo49hPg2OkIm/D/TVYV7jO1MCfPYGwf6x3GO0VU8YMO5AYMn+u3X05lRRxA4fWCz87GFQV6yVQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.9.0", - "@typescript-eslint/utils": "6.9.0", + "@typescript-eslint/typescript-estree": "6.15.0", + "@typescript-eslint/utils": "6.15.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -4003,9 +4002,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.0.tgz", - "integrity": "sha512-+KB0lbkpxBkBSiVCuQvduqMJy+I1FyDbdwSpM3IoBS7APl4Bu15lStPjgBIdykdRqQNYqYNMa8Kuidax6phaEw==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.15.0.tgz", + "integrity": "sha512-yXjbt//E4T/ee8Ia1b5mGlbNj9fB9lJP4jqLbZualwpP2BCQ5is6BcWwxpIsY4XKAhmdv3hrW92GdtJbatC6dQ==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -4016,13 +4015,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.0.tgz", - "integrity": "sha512-NJM2BnJFZBEAbCfBP00zONKXvMqihZCrmwCaik0UhLr0vAgb6oguXxLX1k00oQyD+vZZ+CJn3kocvv2yxm4awQ==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.15.0.tgz", + "integrity": "sha512-7mVZJN7Hd15OmGuWrp2T9UvqR2Ecg+1j/Bp1jXUEY2GZKV6FXlOIoqVDmLpBiEiq3katvj/2n2mR0SDwtloCew==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.9.0", - "@typescript-eslint/visitor-keys": "6.9.0", + "@typescript-eslint/types": "6.15.0", + "@typescript-eslint/visitor-keys": "6.15.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -4043,17 +4042,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.9.0.tgz", - "integrity": "sha512-5Wf+Jsqya7WcCO8me504FBigeQKVLAMPmUzYgDbWchINNh1KJbxCgVya3EQ2MjvJMVeXl3pofRmprqX6mfQkjQ==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.15.0.tgz", + "integrity": "sha512-eF82p0Wrrlt8fQSRL0bGXzK5nWPRV2dYQZdajcfzOD9+cQz9O7ugifrJxclB+xVOvWvagXfqS4Es7vpLP4augw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.9.0", - "@typescript-eslint/types": "6.9.0", - "@typescript-eslint/typescript-estree": "6.9.0", + "@typescript-eslint/scope-manager": "6.15.0", + "@typescript-eslint/types": "6.15.0", + "@typescript-eslint/typescript-estree": "6.15.0", "semver": "^7.5.4" }, "engines": { @@ -4068,12 +4067,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.0.tgz", - "integrity": "sha512-dGtAfqjV6RFOtIP8I0B4ZTBRrlTT8NHHlZZSchQx3qReaoDeXhYM++M4So2AgFK9ZB0emRPA6JI1HkafzA2Ibg==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.15.0.tgz", + "integrity": "sha512-1zvtdC1a9h5Tb5jU9x3ADNXO9yjP8rXlaoChu0DQX40vf5ACVpYIVIZhIMZ6d5sDXH7vq4dsZBT1fEGj8D2n2w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/types": "6.15.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -4242,9 +4241,9 @@ } }, "node_modules/amazon-cognito-identity-js": { - "version": "6.3.6", - "resolved": "https://registry.npmjs.org/amazon-cognito-identity-js/-/amazon-cognito-identity-js-6.3.6.tgz", - "integrity": "sha512-kBq+GE6OkLrxtFj3ZduIOlKBFYeOqZK3EhxbDBkv476UTvy+uwfR0tlriTq2QzNdnvlQAjBIXnXuOM7DwR1UEQ==", + "version": "6.3.7", + "resolved": "https://registry.npmjs.org/amazon-cognito-identity-js/-/amazon-cognito-identity-js-6.3.7.tgz", + "integrity": "sha512-tSjnM7KyAeOZ7UMah+oOZ6cW4Gf64FFcc7BE2l7MTcp7ekAPrXaCbpcW2xEpH1EiDS4cPcAouHzmCuc2tr72vQ==", "dev": true, "dependencies": { "@aws-crypto/sha256-js": "1.2.2", @@ -4328,7 +4327,8 @@ "version": "0.5.0-alpha.4", "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/anymatch": { "version": "3.1.3", @@ -4601,9 +4601,9 @@ "dev": true }, "node_modules/big-integer": { - "version": "1.6.51", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", "dev": true, "engines": { "node": ">=0.6" @@ -4636,9 +4636,9 @@ "dev": true }, "node_modules/bls-eth-wasm": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/bls-eth-wasm/-/bls-eth-wasm-1.1.0.tgz", - "integrity": "sha512-5kNu85tXD0dHEDABo05tYgCcRToBDwjn+3cAWBiJivzh2EUotdfRmgIxNrsZWnqtQjoMK56hqNmM69cxFH/Wog==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bls-eth-wasm/-/bls-eth-wasm-1.1.1.tgz", + "integrity": "sha512-yaoOHCgjICZc2qTpYCvIkJ6L4N2MXBNmdhIpTZGmKZOUc3mmU941EV95qMK02apyYRSKuCydPuCN3fK4o24C1g==", "dev": true }, "node_modules/bn.js": { @@ -5109,16 +5109,16 @@ } }, "node_modules/cli-truncate": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", - "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", "dev": true, "dependencies": { "slice-ansi": "^5.0.0", - "string-width": "^5.0.0" + "string-width": "^7.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5137,23 +5137,23 @@ } }, "node_modules/cli-truncate/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", "dev": true }, "node_modules/cli-truncate/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.0.0.tgz", + "integrity": "sha512-GPQHj7row82Hjo9hKZieKcHIhaAIKOJvFSIZXuCU9OASVZrMNUaZuz++SPVrBjnLsnk4k+z9f2EIypgxf2vNFw==", "dev": true, "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5836,12 +5836,6 @@ "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, "node_modules/electron-to-chromium": { "version": "1.4.569", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.569.tgz", @@ -6099,15 +6093,15 @@ } }, "node_modules/eslint": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz", - "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.52.0", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -6154,9 +6148,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", - "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -6166,23 +6160,24 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", - "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.2.tgz", + "integrity": "sha512-dhlpWc9vOwohcWmClFcA+HjlvUpuyynYs0Rf+L/P6/0iQE6vlHW9l5bkfzN62/Stm9fbq8ku46qzde76T1xlSg==", "dev": true, "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.5" + "synckit": "^0.8.6" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/prettier" + "url": "https://opencollective.com/eslint-plugin-prettier" }, "peerDependencies": { "@types/eslint": ">=8.0.0", "eslint": ">=8.0.0", + "eslint-config-prettier": "*", "prettier": ">=3.0.0" }, "peerDependenciesMeta": { @@ -6851,6 +6846,7 @@ "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", "dev": true, + "peer": true, "dependencies": { "is-hex-prefixed": "1.0.0", "strip-hex-prefix": "1.0.0" @@ -7234,6 +7230,18 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-east-asian-width": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", + "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-func-name": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", @@ -7394,9 +7402,9 @@ } }, "node_modules/globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -8500,6 +8508,7 @@ "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==", "dev": true, + "peer": true, "engines": { "node": ">=6.5.0", "npm": ">=3" @@ -9584,30 +9593,30 @@ } }, "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", + "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", "dev": true, "engines": { - "node": ">=10" + "node": ">=14" } }, "node_modules/lint-staged": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.0.2.tgz", - "integrity": "sha512-vnEy7pFTHyVuDmCAIFKR5QDO8XLVlPFQQyujQ/STOxe40ICWqJ6knS2wSJ/ffX/Lw0rz83luRDh+ET7toN+rOw==", + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.0.tgz", + "integrity": "sha512-TFZzUEV00f+2YLaVPWBWGAMq7So6yQx+GG8YRMDeOEIf95Zn5RyiLMsEiX4KTNl9vq/w+NqRJkLA1kPIo15ufQ==", "dev": true, "dependencies": { "chalk": "5.3.0", "commander": "11.1.0", "debug": "4.3.4", "execa": "8.0.1", - "lilconfig": "2.1.0", - "listr2": "7.0.2", + "lilconfig": "3.0.0", + "listr2": "8.0.0", "micromatch": "4.0.5", "pidtree": "0.6.0", "string-argv": "0.3.2", - "yaml": "2.3.3" + "yaml": "2.3.4" }, "bin": { "lint-staged": "bin/lint-staged.js" @@ -9632,20 +9641,20 @@ } }, "node_modules/listr2": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-7.0.2.tgz", - "integrity": "sha512-rJysbR9GKIalhTbVL2tYbF2hVyDnrf7pFUZBwjPaMIdadYHmeT+EVi/Bu3qd7ETQPahTotg2WRCatXwRBW554g==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.0.0.tgz", + "integrity": "sha512-u8cusxAcyqAiQ2RhYvV7kRKNLgUvtObIbhOX2NCXqvp1UU32xIg5CT22ykS2TPKJXZWJwtK3IKLiqAGlGNE+Zg==", "dev": true, "dependencies": { - "cli-truncate": "^3.1.0", + "cli-truncate": "^4.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", - "log-update": "^5.0.1", + "log-update": "^6.0.0", "rfdc": "^1.3.0", - "wrap-ansi": "^8.1.0" + "wrap-ansi": "^9.0.0" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/locate-path": { @@ -9797,34 +9806,34 @@ } }, "node_modules/log-update": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz", - "integrity": "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.0.0.tgz", + "integrity": "sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==", "dev": true, "dependencies": { - "ansi-escapes": "^5.0.0", + "ansi-escapes": "^6.2.0", "cli-cursor": "^4.0.0", - "slice-ansi": "^5.0.0", - "strip-ansi": "^7.0.1", - "wrap-ansi": "^8.0.1" + "slice-ansi": "^7.0.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/log-update/node_modules/ansi-escapes": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", - "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz", + "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", "dev": true, "dependencies": { - "type-fest": "^1.0.2" + "type-fest": "^3.0.0" }, "engines": { - "node": ">=12" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -9842,6 +9851,49 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dev": true, + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, "node_modules/log-update/node_modules/strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", @@ -9858,12 +9910,12 @@ } }, "node_modules/log-update/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", "dev": true, "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -10570,13 +10622,13 @@ } }, "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, @@ -10890,9 +10942,9 @@ } }, "node_modules/prettier": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", - "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz", + "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -10917,30 +10969,27 @@ } }, "node_modules/prettier-plugin-solidity": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.1.3.tgz", - "integrity": "sha512-fQ9yucPi2sBbA2U2Xjh6m4isUTJ7S7QLc/XDDsktqqxYfTwdYKJ0EnnywXHwCGAaYbQNK+HIYPL1OemxuMsgeg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.3.1.tgz", + "integrity": "sha512-MN4OP5I2gHAzHZG1wcuJl0FsLS3c4Cc5494bbg+6oQWBPuEamjwDvmGfFMZ6NFzsh3Efd9UUxeT7ImgjNH4ozA==", "dev": true, "dependencies": { - "@solidity-parser/parser": "^0.16.0", - "semver": "^7.3.8", - "solidity-comments-extractor": "^0.0.7" + "@solidity-parser/parser": "^0.17.0", + "semver": "^7.5.4", + "solidity-comments-extractor": "^0.0.8" }, "engines": { - "node": ">=12" + "node": ">=16" }, "peerDependencies": { - "prettier": ">=2.3.0 || >=3.0.0-alpha.0" + "prettier": ">=2.3.0" } }, "node_modules/prettier-plugin-solidity/node_modules/@solidity-parser/parser": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.16.1.tgz", - "integrity": "sha512-PdhRFNhbTtu3x8Axm0uYpqOy/lODYQK+MlYSgqIsq2L8SFYEHJPHNUiOTAJbDGzNjjr1/n9AcIayxafR/fWmYw==", - "dev": true, - "dependencies": { - "antlr4ts": "^0.5.0-alpha.4" - } + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.17.0.tgz", + "integrity": "sha512-Nko8R0/kUo391jsEHHxrGM07QFdnPGvlmox4rmH0kNiNAashItAilhy4Mv4pK5gQmW5f4sXAF58fwJbmlkGcVw==", + "dev": true }, "node_modules/pretty-format": { "version": "28.1.3", @@ -12038,18 +12087,18 @@ } }, "node_modules/solidity-ast": { - "version": "0.4.52", - "resolved": "https://registry.npmjs.org/solidity-ast/-/solidity-ast-0.4.52.tgz", - "integrity": "sha512-iOya9BSiB9jhM8Vf40n8lGELGzwrUc57rl5BhfNtJ5cvAaMvRcNlHeAMNvqJJyjoUnczqRbHqdivEqK89du3Cw==", + "version": "0.4.55", + "resolved": "https://registry.npmjs.org/solidity-ast/-/solidity-ast-0.4.55.tgz", + "integrity": "sha512-qeEU/r/K+V5lrAw8iswf2/yfWAnSGs3WKPHI+zAFKFjX0dIBVXEU/swQ8eJQYHf6PJWUZFO2uWV4V1wEOkeQbA==", "dev": true, "dependencies": { "array.prototype.findlast": "^1.2.2" } }, "node_modules/solidity-comments-extractor": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz", - "integrity": "sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==", + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.8.tgz", + "integrity": "sha512-htM7Vn6LhHreR+EglVMd2s+sZhcXAirB1Zlyrv5zBuTxieCvjfnRpd7iZk75m/u6NOlEyQ94C6TWbBn2cY7w8g==", "dev": true }, "node_modules/solidity-coverage": { @@ -12369,6 +12418,7 @@ "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", "dev": true, + "peer": true, "dependencies": { "is-hex-prefixed": "1.0.0" }, @@ -12436,13 +12486,13 @@ } }, "node_modules/synckit": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", - "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.6.tgz", + "integrity": "sha512-laHF2savN6sMeHCjLRkheIU4wo3Zg9Ln5YOjOo7sZ5dVQW8yF5pPE5SIw1dsPhq3TRp1jisKRCdPhfs/1WMqDA==", "dev": true, "dependencies": { - "@pkgr/utils": "^2.3.1", - "tslib": "^2.5.0" + "@pkgr/utils": "^2.4.2", + "tslib": "^2.6.2" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -13194,9 +13244,9 @@ } }, "node_modules/undici": { - "version": "5.27.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.27.0.tgz", - "integrity": "sha512-l3ydWhlhOJzMVOYkymLykcRRXqbUaQriERtR70B9LzNkZ4bX52Fc8wbTDneMiwo8T+AemZXvXaTx+9o5ROxrXg==", + "version": "5.28.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.2.tgz", + "integrity": "sha512-wh1pHJHnUeQV5Xa8/kyQhO7WFa8M34l026L5P/+2TYiakvGy5Rdc8jWZVyG7ieht/0WgJLEd3kcU5gKx+6GC8w==", "dev": true, "dependencies": { "@fastify/busboy": "^2.0.0" @@ -13218,9 +13268,9 @@ "dev": true }, "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "peer": true, "engines": { @@ -13457,17 +13507,17 @@ "peer": true }, "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", "dev": true, "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" @@ -13498,23 +13548,23 @@ } }, "node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", "dev": true }, "node_modules/wrap-ansi/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.0.0.tgz", + "integrity": "sha512-GPQHj7row82Hjo9hKZieKcHIhaAIKOJvFSIZXuCU9OASVZrMNUaZuz++SPVrBjnLsnk4k+z9f2EIypgxf2vNFw==", "dev": true, "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -13598,9 +13648,9 @@ "dev": true }, "node_modules/yaml": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.3.tgz", - "integrity": "sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", "dev": true, "engines": { "node": ">= 14" diff --git a/package.json b/package.json index 859f57c9..4f0011b0 100644 --- a/package.json +++ b/package.json @@ -36,17 +36,17 @@ "homepage": "https://github.com/stakewise/v3-core#readme", "devDependencies": { "@chainsafe/ssz": "0.14.0", - "@metamask/eth-sig-util": "7.0.0", - "@nomicfoundation/hardhat-toolbox": "3.0.0", - "@openzeppelin/hardhat-upgrades": "2.3.3", + "@metamask/eth-sig-util": "7.0.1", + "@nomicfoundation/hardhat-toolbox": "4.0.0", + "@openzeppelin/hardhat-upgrades": "3.0.1", "@openzeppelin/merkle-tree": "1.0.5", - "@typescript-eslint/eslint-plugin": "6.9.0", - "@typescript-eslint/parser": "6.9.0", - "bls-eth-wasm": "1.1.0", + "@typescript-eslint/eslint-plugin": "6.15.0", + "@typescript-eslint/parser": "6.15.0", + "bls-eth-wasm": "1.1.1", "dotenv": "16.3.1", - "eslint": "8.52.0", - "eslint-config-prettier": "9.0.0", - "eslint-plugin-prettier": "5.0.1", + "eslint": "8.56.0", + "eslint-config-prettier": "9.1.0", + "eslint-plugin-prettier": "5.1.2", "ethereumjs-wallet": "1.0.2", "hardhat-abi-exporter": "2.10.1", "hardhat-contract-sizer": "2.10.0", @@ -54,10 +54,10 @@ "hardhat-spdx-license-identifier": "2.2.0", "husky": "8.0.3", "keccak256": "1.0.6", - "lint-staged": "15.0.2", + "lint-staged": "15.2.0", "mocha-chai-jest-snapshot": "1.1.4", - "prettier": "3.0.3", - "prettier-plugin-solidity": "1.1.3" + "prettier": "3.1.1", + "prettier-plugin-solidity": "1.3.1" }, "lint-staged": { "test/**/*.ts": [ diff --git a/test/EthGenesisVault.spec.ts b/test/EthGenesisVault.spec.ts index ad6e070b..f6a9f9ad 100644 --- a/test/EthGenesisVault.spec.ts +++ b/test/EthGenesisVault.spec.ts @@ -1,11 +1,13 @@ import hre, { ethers } from 'hardhat' -import { Contract, Wallet } from 'ethers' +import { Contract, Signer, Wallet } from 'ethers' import { EthGenesisVault, Keeper, PoolEscrowMock, RewardEthTokenMock, RewardEthTokenMock__factory, + EthGenesisVault__factory, + PoolEscrowMock__factory, } from '../typechain-types' import { createDepositorMock, @@ -41,23 +43,47 @@ import { } from './shared/utils' import { loadFixture } from '@nomicfoundation/hardhat-toolbox/network-helpers' import { simulateDeployImpl } from '@openzeppelin/hardhat-upgrades/dist/utils' +import mainnetDeployment from '../deployments/mainnet.json' +import { MAINNET_FORK, NETWORKS } from '../helpers/constants' describe('EthGenesisVault', () => { - const capacity = ethers.parseEther('1000') - const feePercent = 1000 + const capacity = ethers.parseEther('1000000') + const feePercent = 500 const metadataIpfsHash = 'bafkreidivzimqfqtoqxkrpge6bjyhlvxqs3rhe73owtmdulaxr5do5in7u' const deadline = VALIDATORS_DEADLINE - let dao: Wallet, admin: Wallet, other: Wallet + let dao: Wallet, admin: Signer, other: Wallet let vault: EthGenesisVault, keeper: Keeper, validatorsRegistry: Contract let poolEscrow: PoolEscrowMock let rewardEthToken: RewardEthTokenMock + async function acceptPoolEscrowOwnership() { + if (MAINNET_FORK.enabled) return + await vault.connect(admin).acceptPoolEscrowOwnership() + } + + async function collatEthVault() { + if (MAINNET_FORK.enabled) return + await collateralizeEthVault(vault, keeper, validatorsRegistry, admin) + } + beforeEach('deploy fixtures', async () => { ;[dao, admin, other] = await (ethers as any).getSigners() const fixture = await loadFixture(ethVaultFixture) keeper = fixture.keeper validatorsRegistry = fixture.validatorsRegistry + if (MAINNET_FORK.enabled) { + admin = await ethers.getImpersonatedSigner(NETWORKS.mainnet.genesisVault.admin) + await setBalance(NETWORKS.mainnet.genesisVault.admin, ethers.parseEther('1')) + vault = EthGenesisVault__factory.connect(mainnetDeployment.EthGenesisVault, admin) + poolEscrow = PoolEscrowMock__factory.connect(NETWORKS.mainnet.genesisVault.poolEscrow, admin) + rewardEthToken = RewardEthTokenMock__factory.connect( + NETWORKS.mainnet.genesisVault.rewardEthToken, + dao + ) + return + } + let factory = await ethers.getContractFactory('RewardEthTokenMock') rewardEthToken = RewardEthTokenMock__factory.connect( await (await factory.deploy()).getAddress(), @@ -88,18 +114,19 @@ describe('EthGenesisVault', () => { vault = factory.attach(proxyAddress) as EthGenesisVault await rewardEthToken.setVault(await vault.getAddress()) await poolEscrow.connect(dao).commitOwnershipTransfer(await vault.getAddress()) + const adminAddr = await admin.getAddress() const tx = await vault.initialize( ethers.AbiCoder.defaultAbiCoder().encode( ['address', 'tuple(uint256 capacity, uint16 feePercent, string metadataIpfsHash)'], - [admin.address, [capacity, feePercent, metadataIpfsHash]] + [adminAddr, [capacity, feePercent, metadataIpfsHash]] ), { value: SECURITY_DEPOSIT } ) await expect(tx).to.emit(vault, 'MetadataUpdated').withArgs(dao.address, metadataIpfsHash) - await expect(tx).to.emit(vault, 'FeeRecipientUpdated').withArgs(dao.address, admin.address) + await expect(tx).to.emit(vault, 'FeeRecipientUpdated').withArgs(dao.address, adminAddr) await expect(tx) .to.emit(vault, 'GenesisVaultCreated') - .withArgs(admin.address, capacity, feePercent, metadataIpfsHash) + .withArgs(adminAddr, capacity, feePercent, metadataIpfsHash) expect(await vault.mevEscrow()).to.be.eq(await fixture.sharedMevEscrow.getAddress()) await fixture.vaultsRegistry.connect(dao).addVault(await vault.getAddress()) @@ -113,14 +140,17 @@ describe('EthGenesisVault', () => { expect(await vault.capacity()).to.be.eq(capacity) // VaultAdmin - expect(await vault.admin()).to.be.eq(admin.address) + const adminAddr = await admin.getAddress() + expect(await vault.admin()).to.be.eq(adminAddr) // VaultVersion expect(await vault.version()).to.be.eq(1) expect(await vault.vaultId()).to.be.eq(`0x${keccak256('EthGenesisVault').toString('hex')}`) // VaultFee - expect(await vault.feeRecipient()).to.be.eq(admin.address) + if (!MAINNET_FORK.enabled) { + expect(await vault.feeRecipient()).to.be.eq(adminAddr) + } expect(await vault.feePercent()).to.be.eq(feePercent) }) @@ -129,12 +159,12 @@ describe('EthGenesisVault', () => { }) it('applies ownership transfer', async () => { - await vault.connect(admin).acceptPoolEscrowOwnership() + await acceptPoolEscrowOwnership() expect(await poolEscrow.owner()).to.eq(await vault.getAddress()) }) it('apply ownership cannot be called second time', async () => { - await vault.connect(admin).acceptPoolEscrowOwnership() + await acceptPoolEscrowOwnership() await expect(vault.connect(other).acceptPoolEscrowOwnership()).to.be.revertedWithCustomError( vault, 'AccessDenied' @@ -147,11 +177,12 @@ describe('EthGenesisVault', () => { describe('migrate', () => { it('fails from not rewardEthToken', async () => { await expect( - vault.connect(admin).migrate(admin.address, ethers.parseEther('1')) + vault.connect(admin).migrate(await admin.getAddress(), ethers.parseEther('1')) ).to.be.revertedWithCustomError(vault, 'AccessDenied') }) it('fails when pool escrow ownership is not accepted', async () => { + if (MAINNET_FORK.enabled) return const assets = ethers.parseEther('10') await expect( rewardEthToken.connect(other).migrate(other.address, assets, 0) @@ -159,24 +190,37 @@ describe('EthGenesisVault', () => { }) it('fails with zero receiver', async () => { - await vault.connect(admin).acceptPoolEscrowOwnership() - await collateralizeEthVault(vault, keeper, validatorsRegistry, admin) + await acceptPoolEscrowOwnership() + await collatEthVault() const assets = ethers.parseEther('1') - await expect( - rewardEthToken.connect(other).migrate(ZERO_ADDRESS, assets, assets) - ).to.be.revertedWithCustomError(vault, 'ZeroAddress') + if (MAINNET_FORK.enabled) { + await expect( + rewardEthToken.connect(other).migrate(ZERO_ADDRESS, assets, assets) + ).to.be.revertedWith('RewardEthToken: invalid receiver') + } else { + await expect( + rewardEthToken.connect(other).migrate(ZERO_ADDRESS, assets, assets) + ).to.be.revertedWithCustomError(vault, 'ZeroAddress') + } }) it('fails with zero assets', async () => { - await vault.connect(admin).acceptPoolEscrowOwnership() - await collateralizeEthVault(vault, keeper, validatorsRegistry, admin) - await expect( - rewardEthToken.connect(other).migrate(other.address, 0, 0) - ).to.be.revertedWithCustomError(vault, 'InvalidAssets') + await acceptPoolEscrowOwnership() + await collatEthVault() + if (MAINNET_FORK.enabled) { + await expect(rewardEthToken.connect(other).migrate(other.address, 0, 0)).to.be.revertedWith( + 'RewardEthToken: zero assets' + ) + } else { + await expect( + rewardEthToken.connect(other).migrate(other.address, 0, 0) + ).to.be.revertedWithCustomError(vault, 'InvalidAssets') + } }) it('fails when not collateralized', async () => { - await vault.connect(admin).acceptPoolEscrowOwnership() + if (MAINNET_FORK.enabled) return + await acceptPoolEscrowOwnership() const assets = ethers.parseEther('1') await expect( rewardEthToken.connect(other).migrate(other.address, assets, assets) @@ -184,96 +228,132 @@ describe('EthGenesisVault', () => { }) it('fails when not harvested', async () => { - await vault.connect(admin).acceptPoolEscrowOwnership() - await collateralizeEthVault(vault, keeper, validatorsRegistry, admin) + await acceptPoolEscrowOwnership() + await collatEthVault() + let reward = ethers.parseEther('5') + let unlockedMevReward = 0n + if (MAINNET_FORK.enabled) { + reward += MAINNET_FORK.genesisVaultHarvestParams.proofReward + unlockedMevReward += MAINNET_FORK.genesisVaultHarvestParams.proofUnlockedMevReward + } + await updateRewards(keeper, [ { - reward: ethers.parseEther('5'), - unlockedMevReward: 0n, + reward, + unlockedMevReward, vault: await vault.getAddress(), }, ]) + reward += ethers.parseEther('5') await updateRewards(keeper, [ { - reward: ethers.parseEther('10'), - unlockedMevReward: 0n, + reward, + unlockedMevReward, vault: await vault.getAddress(), }, ]) + + let holder: Signer + if (MAINNET_FORK.enabled) { + holder = await ethers.getImpersonatedSigner(MAINNET_FORK.v2PoolHolder) + } else { + holder = other + } + const assets = ethers.parseEther('1') await expect( - rewardEthToken.connect(other).migrate(other.address, assets, assets) + rewardEthToken.connect(holder).migrate(await holder.getAddress(), assets, assets) ).to.be.revertedWithCustomError(vault, 'NotHarvested') }) it('migrates from rewardEthToken', async () => { - await vault.connect(admin).acceptPoolEscrowOwnership() - await collateralizeEthVault(vault, keeper, validatorsRegistry, admin) + await acceptPoolEscrowOwnership() + await collatEthVault() const assets = ethers.parseEther('10') - const expectedShares = ethers.parseEther('10') - expect(await vault.convertToShares(assets)).to.eq(expectedShares) + const expectedShares = await vault.convertToShares(assets) + + let holder: Signer + if (MAINNET_FORK.enabled) { + holder = await ethers.getImpersonatedSigner(MAINNET_FORK.v2PoolHolder) + } else { + holder = other + } + const holderAddr = await holder.getAddress() - const receipt = await rewardEthToken.connect(other).migrate(other.address, assets, 0) - expect(await vault.getShares(other.address)).to.eq(expectedShares) + const receipt = await rewardEthToken.connect(holder).migrate(holderAddr, assets, 0) + expect(await vault.getShares(holderAddr)).to.eq(expectedShares) - await expect(receipt) - .to.emit(vault, 'Migrated') - .withArgs(other.address, assets, expectedShares) + await expect(receipt).to.emit(vault, 'Migrated').withArgs(holderAddr, assets, expectedShares) await snapshotGasCost(receipt) }) }) it('pulls assets on claim exited assets', async () => { - await vault.connect(admin).acceptPoolEscrowOwnership() - await collateralizeEthVault(vault, keeper, validatorsRegistry, admin) + await acceptPoolEscrowOwnership() + await collatEthVault() const shares = ethers.parseEther('10') - await vault.connect(other).deposit(other.address, ZERO_ADDRESS, { value: shares }) + let assets = await vault.convertToAssets(shares) + await vault.connect(other).deposit(other.address, ZERO_ADDRESS, { value: assets }) - await setBalance(await vault.getAddress(), 0n) + const vaultAddr = await vault.getAddress() + const vaultBalance = await ethers.provider.getBalance(vaultAddr) + const poolEscrowAddr = await poolEscrow.getAddress() + const poolEscrowBalance = await ethers.provider.getBalance(poolEscrowAddr) + + await setBalance(vaultAddr, 0n) const response = await vault.connect(other).enterExitQueue(shares, other.address) const positionTicket = await extractExitPositionTicket(response) const timestamp = await getBlockTimestamp(response) - await setBalance(await poolEscrow.getAddress(), shares) - expect(await vault.withdrawableAssets()).to.eq(0) + await setBalance(poolEscrowAddr, poolEscrowBalance + vaultBalance) await increaseTime(ONE_DAY) + let reward = 0n + let unlockedMevReward = 0n + if (MAINNET_FORK.enabled) { + reward += MAINNET_FORK.genesisVaultHarvestParams.proofReward + unlockedMevReward += MAINNET_FORK.genesisVaultHarvestParams.proofUnlockedMevReward + } const tree = await updateRewards(keeper, [ { - reward: 0n, - unlockedMevReward: 0n, - vault: await vault.getAddress(), + reward, + unlockedMevReward, + vault: vaultAddr, }, ]) const proof = getRewardsRootProof(tree, { - vault: await vault.getAddress(), - unlockedMevReward: 0n, - reward: 0n, + vault: vaultAddr, + unlockedMevReward, + reward, }) await vault.updateState({ rewardsRoot: tree.root, - reward: 0n, - unlockedMevReward: 0n, + reward, + unlockedMevReward, proof, }) const exitQueueIndex = await vault.getExitQueueIndex(positionTicket) + if (MAINNET_FORK.enabled) { + assets -= 1n + } const tx = await vault .connect(other) .claimExitedAssets(positionTicket, timestamp, exitQueueIndex) await expect(tx) .to.emit(poolEscrow, 'Withdrawn') - .withArgs(await vault.getAddress(), await vault.getAddress(), shares) + .withArgs(vaultAddr, vaultAddr, poolEscrowBalance + vaultBalance) await expect(tx) .to.emit(vault, 'ExitedAssetsClaimed') - .withArgs(other.address, positionTicket, 0, shares) + .withArgs(other.address, positionTicket, 0, assets) expect(await ethers.provider.getBalance(await poolEscrow.getAddress())).to.eq(0) await snapshotGasCost(tx) }) it('pulls assets on redeem', async () => { - await vault.connect(admin).acceptPoolEscrowOwnership() + if (MAINNET_FORK.enabled) return + await acceptPoolEscrowOwnership() const shares = ethers.parseEther('10') await vault.connect(other).deposit(other.address, ZERO_ADDRESS, { value: shares }) @@ -294,23 +374,27 @@ describe('EthGenesisVault', () => { }) it('pulls assets on single validator registration', async () => { - await vault.connect(admin).acceptPoolEscrowOwnership() - await collateralizeEthVault(vault, keeper, validatorsRegistry, admin) + await acceptPoolEscrowOwnership() + await collatEthVault() const validatorDeposit = ethers.parseEther('32') - await rewardEthToken.connect(other).migrate(other.address, validatorDeposit, validatorDeposit) - await setBalance(await vault.getAddress(), 0n) - await setBalance(await poolEscrow.getAddress(), validatorDeposit) - expect(await vault.withdrawableAssets()).to.eq(validatorDeposit) + const vaultAddr = await vault.getAddress() + const vaultBalance = await ethers.provider.getBalance(vaultAddr) + const poolEscrowAddr = await poolEscrow.getAddress() + const poolEscrowBalance = await ethers.provider.getBalance(poolEscrowAddr) + + await setBalance(vaultAddr, 0n) + await setBalance(poolEscrowAddr, validatorDeposit + vaultBalance + poolEscrowBalance) + expect(await vault.withdrawableAssets()).to.be.greaterThanOrEqual(validatorDeposit) const tx = await registerEthValidator(vault, keeper, validatorsRegistry, admin) await expect(tx) .to.emit(poolEscrow, 'Withdrawn') - .withArgs(await vault.getAddress(), await vault.getAddress(), validatorDeposit) + .withArgs(vaultAddr, vaultAddr, validatorDeposit + vaultBalance + poolEscrowBalance) await snapshotGasCost(tx) }) it('pulls assets on multiple validators registration', async () => { - await vault.connect(admin).acceptPoolEscrowOwnership() - await collateralizeEthVault(vault, keeper, validatorsRegistry, admin) + await acceptPoolEscrowOwnership() + await collatEthVault() const validatorsData = await createEthValidatorsData(vault) const validatorsRegistryRoot = await validatorsRegistry.get_deposit_root() await vault.connect(admin).setValidatorsRoot(validatorsData.root) @@ -319,7 +403,6 @@ describe('EthGenesisVault', () => { ]) const validators = validatorsData.validators const assets = ethers.parseEther('32') * BigInt(validators.length) - await rewardEthToken.connect(other).migrate(other.address, assets, assets) const sortedVals = proof.leaves.map((v) => v[0]) const indexes = validators.map((v) => sortedVals.indexOf(v)) @@ -342,59 +425,72 @@ describe('EthGenesisVault', () => { deadline, } - await setBalance(await vault.getAddress(), 0n) - await setBalance(await poolEscrow.getAddress(), assets) - expect(await vault.withdrawableAssets()).to.eq(assets) + const vaultAddr = await vault.getAddress() + const vaultBalance = await ethers.provider.getBalance(vaultAddr) + const poolEscrowAddr = await poolEscrow.getAddress() + const poolEscrowBalance = await ethers.provider.getBalance(poolEscrowAddr) + + await setBalance(vaultAddr, 0n) + await setBalance(poolEscrowAddr, assets + vaultBalance + poolEscrowBalance) const tx = await vault.registerValidators(approveParams, indexes, proof.proofFlags, proof.proof) await expect(tx) .to.emit(poolEscrow, 'Withdrawn') - .withArgs(await vault.getAddress(), await vault.getAddress(), assets) + .withArgs(vaultAddr, vaultAddr, assets + vaultBalance + poolEscrowBalance) await snapshotGasCost(tx) }) it('can deposit through receive fallback function', async () => { const depositorMock = await createDepositorMock(vault) + const depositorMockAddr = await depositorMock.getAddress() const amount = ethers.parseEther('100') - const expectedShares = ethers.parseEther('100') - expect(await vault.convertToShares(amount)).to.eq(expectedShares) + let expectedShares = await vault.convertToShares(amount) + + if (MAINNET_FORK.enabled) { + expectedShares += 1n + } const receipt = await depositorMock.connect(other).depositToVault({ value: amount }) expect(await vault.getShares(await depositorMock.getAddress())).to.eq(expectedShares) await expect(receipt) .to.emit(vault, 'Deposited') - .withArgs( - await depositorMock.getAddress(), - await depositorMock.getAddress(), - amount, - expectedShares, - ZERO_ADDRESS - ) + .withArgs(depositorMockAddr, depositorMockAddr, amount, expectedShares, ZERO_ADDRESS) await snapshotGasCost(receipt) }) describe('update state', () => { - const totalVaultAssets: bigint = ethers.parseEther('10') - const totalLegacyAssets: bigint = ethers.parseEther('5') + let totalVaultAssets: bigint + let totalLegacyAssets: bigint beforeEach(async () => { - await vault.deposit(other.address, ZERO_ADDRESS, { - value: totalVaultAssets - SECURITY_DEPOSIT, - }) - - await rewardEthToken.connect(other).setTotalStaked(totalLegacyAssets) + if (MAINNET_FORK.enabled) { + totalVaultAssets = await vault.totalAssets() + totalLegacyAssets = await rewardEthToken.totalAssets() + } else { + totalVaultAssets = ethers.parseEther('10') + totalLegacyAssets = ethers.parseEther('5') + await vault.deposit(other.address, ZERO_ADDRESS, { + value: totalVaultAssets - SECURITY_DEPOSIT, + }) + await rewardEthToken.connect(other).setTotalStaked(totalLegacyAssets) + } }) it('splits reward between rewardEthToken and vault', async () => { - await vault.connect(admin).acceptPoolEscrowOwnership() - const totalRewards = ethers.parseEther('30') + await acceptPoolEscrowOwnership() + let reward = ethers.parseEther('30') + let unlockedMevReward = 0n const expectedVaultDelta = - (totalRewards * totalVaultAssets) / (totalLegacyAssets + totalVaultAssets) - const expectedLegacyDelta = totalRewards - expectedVaultDelta + (reward * totalVaultAssets) / (totalLegacyAssets + totalVaultAssets) + const expectedLegacyDelta = reward - expectedVaultDelta + if (MAINNET_FORK.enabled) { + reward += MAINNET_FORK.genesisVaultHarvestParams.proofReward + unlockedMevReward += MAINNET_FORK.genesisVaultHarvestParams.proofUnlockedMevReward + } const vaultReward = { - reward: totalRewards, - unlockedMevReward: 0n, + reward, + unlockedMevReward, vault: await vault.getAddress(), } const rewardsTree = await updateRewards(keeper, [vaultReward]) @@ -406,12 +502,19 @@ describe('EthGenesisVault', () => { proof, }) + if (MAINNET_FORK.enabled) { + // rounding error + totalLegacyAssets -= 1n + totalVaultAssets += 1n + } + expect(await rewardEthToken.totalAssets()).to.eq(totalLegacyAssets + expectedLegacyDelta) expect(await vault.totalAssets()).to.eq(totalVaultAssets + expectedVaultDelta) await snapshotGasCost(receipt) }) it('fails when pool escrow ownership not accepted', async () => { + if (MAINNET_FORK.enabled) return const totalRewards = ethers.parseEther('30') const vaultReward = { reward: totalRewards, @@ -431,7 +534,8 @@ describe('EthGenesisVault', () => { }) it('fails with negative first update', async () => { - await vault.connect(admin).acceptPoolEscrowOwnership() + if (MAINNET_FORK.enabled) return + await acceptPoolEscrowOwnership() const totalPenalty = ethers.parseEther('-5') const vaultReward = { reward: totalPenalty, @@ -451,15 +555,20 @@ describe('EthGenesisVault', () => { }) it('splits penalty between rewardEthToken and vault', async () => { - await vault.connect(admin).acceptPoolEscrowOwnership() - await collateralizeEthVault(vault, keeper, validatorsRegistry, admin) - const totalPenalty = ethers.parseEther('-5') + await acceptPoolEscrowOwnership() + await collatEthVault() + let reward = ethers.parseEther('-5') + let unlockedMevReward = 0n const expectedVaultDelta = - (totalPenalty * totalVaultAssets) / (totalLegacyAssets + totalVaultAssets) - const expectedLegacyDelta = totalPenalty - expectedVaultDelta + (reward * totalVaultAssets) / (totalLegacyAssets + totalVaultAssets) + const expectedLegacyDelta = reward - expectedVaultDelta + if (MAINNET_FORK.enabled) { + reward += MAINNET_FORK.genesisVaultHarvestParams.proofReward + unlockedMevReward += MAINNET_FORK.genesisVaultHarvestParams.proofUnlockedMevReward + } const vaultReward = { - reward: totalPenalty, - unlockedMevReward: 0n, + reward, + unlockedMevReward, vault: await vault.getAddress(), } const rewardsTree = await updateRewards(keeper, [vaultReward]) @@ -479,7 +588,8 @@ describe('EthGenesisVault', () => { }) it('deducts rewards on first state update', async () => { - await vault.connect(admin).acceptPoolEscrowOwnership() + if (MAINNET_FORK.enabled) return + await acceptPoolEscrowOwnership() const totalRewards = ethers.parseEther('25') const legacyRewards = ethers.parseEther('5') await rewardEthToken.connect(other).setTotalRewards(legacyRewards) diff --git a/test/EthVault.burn.spec.ts b/test/EthVault.burn.spec.ts index e370dd56..479fa3be 100644 --- a/test/EthVault.burn.spec.ts +++ b/test/EthVault.burn.spec.ts @@ -13,7 +13,7 @@ import { increaseTime } from './shared/utils' describe('EthVault - burn', () => { const assets = ethers.parseEther('2') const osTokenAssets = ethers.parseEther('1') - const osTokenShares = ethers.parseEther('1') + let osTokenShares: bigint const vaultParams = { capacity: ethers.parseEther('1000'), feePercent: 1000, @@ -42,6 +42,7 @@ describe('EthVault - burn', () => { // collateralize vault await collateralizeEthVault(vault, keeper, validatorsRegistry, admin) await vault.connect(sender).deposit(sender.address, ZERO_ADDRESS, { value: assets }) + osTokenShares = await osTokenVaultController.convertToShares(osTokenAssets) await vault.connect(sender).mintOsToken(sender.address, osTokenShares, ZERO_ADDRESS) }) @@ -72,37 +73,33 @@ describe('EthVault - burn', () => { it('updates position accumulated fee', async () => { const treasury = await osTokenVaultController.treasury() - let totalShares = osTokenShares - let totalAssets = osTokenAssets - let cumulativeFeePerShare = ethers.parseEther('1') - let treasuryShares = 0n - let positionShares = osTokenShares + const currTotalShares = await osTokenVaultController.totalShares() + const currTotalAssets = await osTokenVaultController.totalAssets() + const currCumulativeFeePerShare = await osTokenVaultController.cumulativeFeePerShare() + const currTreasuryShares = await osToken.balanceOf(treasury) + const currPositionShares = osTokenShares - expect(await osTokenVaultController.cumulativeFeePerShare()).to.eq(cumulativeFeePerShare) - expect(await vault.osTokenPositions(sender.address)).to.eq(positionShares) + expect(await vault.osTokenPositions(sender.address)).to.eq(currPositionShares) await increaseTime(ONE_DAY) - expect(await osTokenVaultController.cumulativeFeePerShare()).to.be.above(cumulativeFeePerShare) - expect(await osTokenVaultController.totalAssets()).to.be.above(totalAssets) - expect(await vault.osTokenPositions(sender.address)).to.be.above(positionShares) + const newTotalShares = await osTokenVaultController.totalShares() + const newTotalAssets = await osTokenVaultController.totalAssets() + const newCumulativeFeePerShare = await osTokenVaultController.cumulativeFeePerShare() + const newTreasuryShares = await osToken.balanceOf(treasury) + const newPositionShares = await vault.osTokenPositions(sender.address) + expect(newTotalShares).to.be.eq(currTotalShares) + expect(newTotalAssets).to.be.above(currTotalAssets) + expect(newCumulativeFeePerShare).to.be.above(currCumulativeFeePerShare) + expect(newTreasuryShares).to.be.eq(currTreasuryShares) + expect(newPositionShares).to.be.above(currPositionShares) const receipt = await vault.connect(sender).burnOsToken(osTokenShares) - expect(await osToken.balanceOf(treasury)).to.be.above(0) - expect(await osTokenVaultController.cumulativeFeePerShare()).to.be.above(cumulativeFeePerShare) + expect(await vault.osTokenPositions(sender.address)).to.be.above( + newPositionShares - currPositionShares + ) + expect await snapshotGasCost(receipt) - - cumulativeFeePerShare = await osTokenVaultController.cumulativeFeePerShare() - treasuryShares = await osToken.balanceOf(treasury) - positionShares = treasuryShares - totalShares = treasuryShares - totalAssets = await osTokenVaultController.convertToAssets(treasuryShares) - expect(await osTokenVaultController.totalShares()).to.eq(totalShares) - expect(await osTokenVaultController.totalAssets()).to.eq(totalAssets) - expect(await osTokenVaultController.cumulativeFeePerShare()).to.eq(cumulativeFeePerShare) - expect(await osToken.balanceOf(treasury)).to.eq(treasuryShares) - expect(await osToken.balanceOf(sender.address)).to.eq(0) - expect(await vault.osTokenPositions(sender.address)).to.eq(positionShares) }) it('burns osTokens', async () => { @@ -113,9 +110,7 @@ describe('EthVault - burn', () => { const osTokenAssets = receipt.logs?.[receipt.logs.length - 1]?.args?.assets expect(await osToken.balanceOf(sender.address)).to.eq(0) - expect(await vault.osTokenPositions(sender.address)).to.eq( - await osToken.balanceOf(await osTokenVaultController.treasury()) - ) + expect(await vault.osTokenPositions(sender.address)).to.be.above(0) await expect(tx) .to.emit(vault, 'OsTokenBurned') .withArgs(sender.address, osTokenAssets, osTokenShares) @@ -125,7 +120,6 @@ describe('EthVault - burn', () => { await expect(tx) .to.emit(osTokenVaultController, 'Burn') .withArgs(await vault.getAddress(), sender.address, osTokenAssets, osTokenShares) - await snapshotGasCost(tx) }) }) diff --git a/test/EthVault.liquidate.spec.ts b/test/EthVault.liquidate.spec.ts index 5da18bfb..ae832de7 100644 --- a/test/EthVault.liquidate.spec.ts +++ b/test/EthVault.liquidate.spec.ts @@ -7,11 +7,12 @@ import { Keeper, OsToken, OsTokenVaultController, + OsTokenConfig, } from '../typechain-types' import { ThenArg } from '../helpers/types' import { ethVaultFixture } from './shared/fixtures' import { expect } from './shared/expect' -import { ONE_DAY, OSTOKEN_LIQ_BONUS, ZERO_ADDRESS } from './shared/constants' +import { ONE_DAY, ZERO_ADDRESS } from './shared/constants' import { collateralizeEthVault, getRewardsRootProof, @@ -20,10 +21,12 @@ import { } from './shared/rewards' import { increaseTime, setBalance } from './shared/utils' import snapshotGasCost from './shared/snapshotGasCost' +import { MAINNET_FORK } from '../helpers/constants' describe('EthVault - liquidate', () => { const shares = ethers.parseEther('32') - const osTokenShares = ethers.parseEther('28.8') + const osTokenAssets = ethers.parseEther('28.8') + let osTokenShares: bigint const penalty = ethers.parseEther('-2.6') const unlockedMevReward = ethers.parseEther('0') const vaultParams = { @@ -36,6 +39,7 @@ describe('EthVault - liquidate', () => { keeper: Keeper, osTokenVaultController: OsTokenVaultController, osToken: OsToken, + osTokenConfig: OsTokenConfig, validatorsRegistry: Contract let createVault: ThenArg>['createEthVault'] @@ -48,6 +52,7 @@ describe('EthVault - liquidate', () => { validatorsRegistry, osTokenVaultController, osToken, + osTokenConfig, } = await loadFixture(ethVaultFixture)) vault = await createVault(admin, vaultParams) await osTokenVaultController.connect(dao).setFeePercent(0) @@ -60,6 +65,7 @@ describe('EthVault - liquidate', () => { await setAvgRewardPerSecond(dao, vault, keeper, 0) // mint osTokens + osTokenShares = await osTokenVaultController.convertToShares(osTokenAssets) await vault.connect(owner).mintOsToken(owner.address, osTokenShares, ZERO_ADDRESS) // slashing received @@ -163,8 +169,12 @@ describe('EthVault - liquidate', () => { expect(await vault.getShares(owner.address)).to.be.eq(shares) const balanceBefore = await ethers.provider.getBalance(receiver.address) - const osTokenAssets = (osTokenShares * BigInt(OSTOKEN_LIQ_BONUS)) / 10000n - const burnedShares = await vault.convertToShares(osTokenAssets) + const liqBonus = await osTokenConfig.liqBonusPercent() + let liquidatorAssets = (osTokenAssets * BigInt(liqBonus)) / 10000n + if (MAINNET_FORK.enabled) { + liquidatorAssets -= 2n // rounding error + } + const burnedShares = await vault.convertToShares(liquidatorAssets) const receipt = await vault .connect(liquidator) @@ -173,7 +183,9 @@ describe('EthVault - liquidate', () => { expect(await osToken.balanceOf(liquidator.address)).to.eq(0) expect(await vault.osTokenPositions(owner.address)).to.be.eq(0) expect(await vault.getShares(owner.address)).to.be.eq(shares - burnedShares) - expect(await ethers.provider.getBalance(receiver.address)).to.eq(balanceBefore + osTokenAssets) + expect(await ethers.provider.getBalance(receiver.address)).to.eq( + balanceBefore + liquidatorAssets + ) await expect(receipt) .to.emit(vault, 'OsTokenLiquidated') @@ -183,14 +195,20 @@ describe('EthVault - liquidate', () => { receiver.address, osTokenShares, burnedShares, - osTokenAssets + liquidatorAssets ) await expect(receipt) .to.emit(osToken, 'Transfer') .withArgs(liquidator.address, ZERO_ADDRESS, osTokenShares) + + let burnedAssets = osTokenAssets + if (MAINNET_FORK.enabled) { + burnedAssets -= 1n // rounding error + } + await expect(receipt) .to.emit(osTokenVaultController, 'Burn') - .withArgs(await vault.getAddress(), liquidator.address, osTokenShares, osTokenShares) + .withArgs(await vault.getAddress(), liquidator.address, burnedAssets, osTokenShares) // rounding error await snapshotGasCost(receipt) }) diff --git a/test/EthVault.mint.spec.ts b/test/EthVault.mint.spec.ts index 8cfb9d18..8829a1d5 100644 --- a/test/EthVault.mint.spec.ts +++ b/test/EthVault.mint.spec.ts @@ -132,50 +132,47 @@ describe('EthVault - mint', () => { it('updates position accumulated fee', async () => { const treasury = await osTokenVaultController.treasury() - let totalShares = osTokenShares - let totalAssets = await vault.convertToAssets(osTokenShares) - let cumulativeFeePerShare = ethers.parseEther('1') - let treasuryShares = 0n - let positionShares = osTokenShares - let receiverShares = osTokenShares - - expect(await osTokenVaultController.cumulativeFeePerShare()).to.eq(cumulativeFeePerShare) - expect(await vault.osTokenPositions(sender.address)).to.eq(0) - - const verify = async () => { - expect(await osTokenVaultController.totalShares()).to.eq(totalShares) - expect(await osTokenVaultController.totalAssets()).to.eq(totalAssets) - expect(await osTokenVaultController.cumulativeFeePerShare()).to.eq(cumulativeFeePerShare) - expect(await osToken.balanceOf(treasury)).to.eq(treasuryShares) - expect(await osToken.balanceOf(sender.address)).to.eq(0) - expect(await vault.osTokenPositions(sender.address)).to.eq(positionShares) - expect(await osToken.balanceOf(receiver.address)).to.eq(receiverShares) - } - let receipt = await vault - .connect(sender) - .mintOsToken(receiver.address, osTokenShares, ZERO_ADDRESS) - await verify() + const currTotalShares = await osTokenVaultController.totalShares() + const currCumulativeFeePerShare = await osTokenVaultController.cumulativeFeePerShare() + const currTreasuryShares = await osToken.balanceOf(treasury) + const currPositionShares = osTokenShares + + expect(await vault.osTokenPositions(sender.address)).to.eq(0n) - await snapshotGasCost(receipt) await increaseTime(ONE_DAY) - expect(await osTokenVaultController.cumulativeFeePerShare()).to.be.above(cumulativeFeePerShare) - expect(await osTokenVaultController.totalAssets()).to.be.above(totalAssets) - expect(await osTokenVaultController.convertToAssets(receiverShares)).to.be.above(osTokenShares) - expect(await vault.osTokenPositions(sender.address)).to.be.above(positionShares) + await vault.connect(sender).mintOsToken(receiver.address, osTokenShares, ZERO_ADDRESS) + const newTreasuryShares = await osToken.balanceOf(treasury) + const newTotalShares = await osTokenVaultController.totalShares() + const newTotalAssets = await osTokenVaultController.totalAssets() + expect(newTotalShares).to.be.eq( + currTotalShares + currPositionShares - currTreasuryShares + newTreasuryShares + ) + expect(newTotalAssets).to.be.eq(await osTokenVaultController.convertToAssets(newTotalShares)) + if (currTotalShares > 0n) { + expect(await osTokenVaultController.cumulativeFeePerShare()).to.be.above( + currCumulativeFeePerShare + ) + } else { + expect(await osTokenVaultController.cumulativeFeePerShare()).to.be.eq( + currCumulativeFeePerShare + ) + } - receipt = await vault.connect(sender).mintOsToken(receiver.address, 100, ZERO_ADDRESS) - receiverShares = receiverShares + 100n - expect(await osToken.balanceOf(treasury)).to.be.above(0) - expect(await osTokenVaultController.cumulativeFeePerShare()).to.be.above(cumulativeFeePerShare) + expect(await vault.osTokenPositions(sender.address)).to.be.eq(currPositionShares) - cumulativeFeePerShare = await osTokenVaultController.cumulativeFeePerShare() - treasuryShares = await osToken.balanceOf(treasury) - positionShares = treasuryShares + receiverShares - totalShares = positionShares - totalAssets = await osTokenVaultController.convertToAssets(positionShares) - await verify() + const newShares = 10n + const newAssets = await osTokenVaultController.convertToAssets(newShares) + const receipt = await vault + .connect(sender) + .mintOsToken(receiver.address, newShares, ZERO_ADDRESS) + expect(await osTokenVaultController.totalShares()).to.be.above(newTotalShares + 10n) + expect(await osTokenVaultController.totalAssets()).to.be.above(newTotalAssets + newAssets) + expect(await osTokenVaultController.cumulativeFeePerShare()).to.be.above( + currCumulativeFeePerShare + ) + expect(await vault.osTokenPositions(sender.address)).to.be.above(currPositionShares + newShares) await snapshotGasCost(receipt) }) @@ -184,9 +181,8 @@ describe('EthVault - mint', () => { const receipt = await vault .connect(sender) .mintOsToken(receiver.address, osTokenShares, ZERO_ADDRESS) - const osTokenAssets = await vault.convertToAssets(osTokenShares) + const osTokenAssets = await osTokenVaultController.convertToAssets(osTokenShares) - expect(await osTokenVaultController.convertToShares(osTokenAssets)).to.eq(osTokenShares) expect(await osToken.balanceOf(receiver.address)).to.eq(osTokenShares) expect(await vault.osTokenPositions(sender.address)).to.eq(osTokenShares) await expect(receipt) diff --git a/test/EthVault.redeem.spec.ts b/test/EthVault.redeem.spec.ts index 536d2537..d3c67455 100644 --- a/test/EthVault.redeem.spec.ts +++ b/test/EthVault.redeem.spec.ts @@ -18,6 +18,8 @@ import { OSTOKEN_LIQ_BONUS, OSTOKEN_LIQ_THRESHOLD, OSTOKEN_LTV, + OSTOKEN_REDEEM_FROM_LTV, + OSTOKEN_REDEEM_TO_LTV, ZERO_ADDRESS, } from './shared/constants' import { @@ -28,13 +30,16 @@ import { } from './shared/rewards' import { increaseTime, setBalance } from './shared/utils' import snapshotGasCost from './shared/snapshotGasCost' +import { MAINNET_FORK } from '../helpers/constants' describe('EthVault - redeem osToken', () => { const shares = ethers.parseEther('32') - const osTokenShares = ethers.parseEther('28.8') + const osTokenAssets = ethers.parseEther('28.8') + let osTokenShares: bigint const penalty = ethers.parseEther('-0.53') const unlockedMevReward = ethers.parseEther('0') - const redeemedShares = ethers.parseEther('4.76') + const redeemedAssets = ethers.parseEther('4.76') + let redeemedShares: bigint const vaultParams = { capacity: ethers.parseEther('1000'), feePercent: 1000, @@ -68,6 +73,15 @@ describe('EthVault - redeem osToken', () => { await vault.connect(owner).deposit(owner.address, ZERO_ADDRESS, { value: shares }) await setAvgRewardPerSecond(dao, vault, keeper, 0) + await osTokenConfig.connect(dao).updateConfig({ + redeemFromLtvPercent: OSTOKEN_REDEEM_FROM_LTV, + redeemToLtvPercent: OSTOKEN_REDEEM_TO_LTV, + liqThresholdPercent: OSTOKEN_LIQ_THRESHOLD, + liqBonusPercent: OSTOKEN_LIQ_BONUS, + ltvPercent: OSTOKEN_LTV, + }) + osTokenShares = await osTokenVaultController.convertToShares(osTokenAssets) + redeemedShares = await osTokenVaultController.convertToShares(redeemedAssets) await vault.connect(owner).mintOsToken(owner.address, osTokenShares, ZERO_ADDRESS) // penalty received @@ -182,17 +196,22 @@ describe('EthVault - redeem osToken', () => { expect(await vault.getShares(owner.address)).to.be.eq(shares) const balanceBefore = await ethers.provider.getBalance(receiver.address) - const redeemedAssets = await osTokenVaultController.convertToAssets(redeemedShares) - const burnedShares = await vault.convertToShares(redeemedAssets) + let burnedShares = await vault.convertToShares(redeemedAssets) + let receiverAssets = redeemedAssets const receipt = await vault .connect(redeemer) .redeemOsToken(redeemedShares, owner.address, receiver.address) + if (MAINNET_FORK.enabled) { + burnedShares -= 1n // rounding error + receiverAssets -= 1n // rounding error + } + expect(await osToken.balanceOf(redeemer.address)).to.eq(osTokenShares - redeemedShares) expect(await vault.osTokenPositions(owner.address)).to.be.eq(osTokenShares - redeemedShares) expect(await vault.getShares(owner.address)).to.be.eq(shares - burnedShares) - expect(await ethers.provider.getBalance(receiver.address)).to.eq(balanceBefore + redeemedAssets) + expect(await ethers.provider.getBalance(receiver.address)).to.eq(balanceBefore + receiverAssets) await expect(receipt) .to.emit(vault, 'OsTokenRedeemed') @@ -202,14 +221,14 @@ describe('EthVault - redeem osToken', () => { receiver.address, redeemedShares, burnedShares, - redeemedAssets + receiverAssets ) await expect(receipt) .to.emit(osToken, 'Transfer') .withArgs(redeemer.address, ZERO_ADDRESS, redeemedShares) await expect(receipt) .to.emit(osTokenVaultController, 'Burn') - .withArgs(await vault.getAddress(), redeemer.address, redeemedShares, redeemedAssets) + .withArgs(await vault.getAddress(), redeemer.address, receiverAssets, redeemedShares) await snapshotGasCost(receipt) }) diff --git a/test/EthVault.register.spec.ts b/test/EthVault.register.spec.ts index a4cb3cf5..4aa0f238 100644 --- a/test/EthVault.register.spec.ts +++ b/test/EthVault.register.spec.ts @@ -142,6 +142,7 @@ describe('EthVault - register', () => { }) it('succeeds', async () => { + const index = await validatorsRegistry.get_deposit_count() const receipt = await vault.registerValidator(approvalParams, proof) const publicKey = `0x${validator.subarray(0, 48).toString('hex')}` await expect(receipt).to.emit(vault, 'ValidatorRegistered').withArgs(publicKey) @@ -152,7 +153,7 @@ describe('EthVault - register', () => { toHexString(getWithdrawalCredentials(await vault.getAddress())), toHexString(Buffer.from(uintSerializer.serialize(Number(validatorDeposit / gwei)))), toHexString(validator.subarray(48, 144)), - toHexString(Buffer.from(uintSerializer.serialize(0))) + index ) await snapshotGasCost(receipt) }) @@ -433,6 +434,9 @@ describe('EthVault - register', () => { }) it('succeeds', async () => { + const startIndex = uintSerializer.deserialize( + ethers.getBytes(await validatorsRegistry.get_deposit_count()) + ) const receipt = await vault.registerValidators( approvalParams, indexes, @@ -450,7 +454,7 @@ describe('EthVault - register', () => { toHexString(getWithdrawalCredentials(await vault.getAddress())), toHexString(Buffer.from(uintSerializer.serialize(Number(validatorDeposit / gwei)))), toHexString(validator.subarray(48, 144)), - toHexString(Buffer.from(uintSerializer.serialize(i))) + toHexString(Buffer.from(uintSerializer.serialize(startIndex + i))) ) } await snapshotGasCost(receipt) diff --git a/test/EthVault.withdraw.spec.ts b/test/EthVault.withdraw.spec.ts index 43517646..4df7d0a9 100644 --- a/test/EthVault.withdraw.spec.ts +++ b/test/EthVault.withdraw.spec.ts @@ -642,6 +642,9 @@ describe('EthVault - withdraw', () => { await vault.resetSecurityDeposit() const alice = holder const bob = other + let sharedMevEscrowBalance = await ethers.provider.getBalance( + await sharedMevEscrow.getAddress() + ) // collateralize vault by registering validator await collateralizeEthVault(vault, keeper, validatorsRegistry, admin) @@ -667,7 +670,9 @@ describe('EthVault - withdraw', () => { expect(await vault.convertToAssets(aliceShares)).to.be.eq(aliceAssets) expect(await vault.convertToAssets(bobShares)).to.be.eq(bobAssets) expect(await vault.totalShares()).to.be.eq(totalShares) - expect(await ethers.provider.getBalance(await sharedMevEscrow.getAddress())).to.be.eq(0) + expect(await ethers.provider.getBalance(await sharedMevEscrow.getAddress())).to.be.eq( + sharedMevEscrowBalance + ) expect(await ethers.provider.getBalance(await vault.getAddress())).to.be.eq(vaultLiquidAssets) expect(await vault.totalAssets()).to.be.eq(totalAssets) expect(await vault.queuedShares()).to.be.eq(queuedShares) @@ -719,6 +724,7 @@ describe('EthVault - withdraw', () => { }) aliceAssets += 1000n bobAssets += 2000n + sharedMevEscrowBalance = 0n await checkVaultState() diff --git a/test/KeeperRewards.spec.ts b/test/KeeperRewards.spec.ts index c22c50af..1b8c46fb 100644 --- a/test/KeeperRewards.spec.ts +++ b/test/KeeperRewards.spec.ts @@ -36,6 +36,7 @@ describe('KeeperRewards', () => { validatorsRegistry: Contract, sharedMevEscrow: SharedMevEscrow, osTokenVaultController: OsTokenVaultController + let globalRewardsNonce: number beforeEach(async () => { ;[dao, admin, oracle, other] = await (ethers as any).getSigners() @@ -65,7 +66,10 @@ describe('KeeperRewards', () => { unlockedMevReward: ethers.parseEther('1'), vault: await vault.getAddress(), } - const rewardsUpdate = await getKeeperRewardsUpdateData([vaultReward], keeper) + globalRewardsNonce = Number(await keeper.rewardsNonce()) + const rewardsUpdate = await getKeeperRewardsUpdateData([vaultReward], keeper, { + nonce: globalRewardsNonce, + }) rewardsUpdateParams = { rewardsRoot: rewardsUpdate.root, updateTimestamp: rewardsUpdate.updateTimestamp, @@ -73,6 +77,7 @@ describe('KeeperRewards', () => { avgRewardPerSecond: rewardsUpdate.avgRewardPerSecond, signatures: getOraclesSignatures(rewardsUpdate.signingData), } + await increaseTime(REWARDS_DELAY) }) it('fails with invalid IPFS hash', async () => { @@ -121,7 +126,7 @@ describe('KeeperRewards', () => { vault: await vault.getAddress(), } const newRewardsUpdate = await getKeeperRewardsUpdateData([newVaultReward], keeper, { - nonce: 2, + nonce: globalRewardsNonce + 1, updateTimestamp: 1680255895, }) expect(await keeper.canUpdateRewards()).to.eq(false) @@ -173,7 +178,9 @@ describe('KeeperRewards', () => { }) it('succeeds with all signatures', async () => { - const rewardsUpdate = await getKeeperRewardsUpdateData([vaultReward], keeper) + const rewardsUpdate = await getKeeperRewardsUpdateData([vaultReward], keeper, { + nonce: globalRewardsNonce, + }) const params = { ...rewardsUpdateParams, signatures: getOraclesSignatures(rewardsUpdate.signingData, ORACLES.length), @@ -183,8 +190,10 @@ describe('KeeperRewards', () => { }) it('succeeds', async () => { - expect(await keeper.lastRewardsTimestamp()).to.eq(0) expect(await keeper.canUpdateRewards()).to.eq(true) + const prevRewardsRoot = await keeper.rewardsRoot() + const prevRewardsTimestamp = await keeper.lastRewardsTimestamp() + let receipt = await keeper.connect(oracle).updateRewards(rewardsUpdateParams) await expect(receipt) .to.emit(keeper, 'RewardsUpdated') @@ -193,19 +202,20 @@ describe('KeeperRewards', () => { rewardsUpdateParams.rewardsRoot, rewardsUpdateParams.avgRewardPerSecond, rewardsUpdateParams.updateTimestamp, - 1, + globalRewardsNonce, rewardsUpdateParams.rewardsIpfsHash ) await expect(receipt) .to.emit(osTokenVaultController, 'AvgRewardPerSecondUpdated') .withArgs(rewardsUpdateParams.avgRewardPerSecond) - expect(await keeper.prevRewardsRoot()).to.eq(ZERO_BYTES32) + + expect(await keeper.prevRewardsRoot()).to.eq(prevRewardsRoot) expect(await keeper.rewardsRoot()).to.eq(rewardsUpdateParams.rewardsRoot) - expect(await keeper.rewardsNonce()).to.eq(2) + expect(await keeper.rewardsNonce()).to.eq(globalRewardsNonce + 1) expect(await osTokenVaultController.avgRewardPerSecond()).to.eq( rewardsUpdateParams.avgRewardPerSecond ) - expect(await keeper.lastRewardsTimestamp()).to.not.eq(0) + expect(await keeper.lastRewardsTimestamp()).to.not.eq(prevRewardsTimestamp) expect(await keeper.canUpdateRewards()).to.eq(false) await snapshotGasCost(receipt) @@ -216,7 +226,7 @@ describe('KeeperRewards', () => { vault: await vault.getAddress(), } const newRewardsUpdate = await getKeeperRewardsUpdateData([newVaultReward], keeper, { - nonce: 2, + nonce: globalRewardsNonce + 1, updateTimestamp: 1670256000, }) await increaseTime(REWARDS_DELAY) @@ -234,12 +244,12 @@ describe('KeeperRewards', () => { newRewardsUpdate.root, newRewardsUpdate.avgRewardPerSecond, newRewardsUpdate.updateTimestamp, - 2, + globalRewardsNonce + 1, newRewardsUpdate.ipfsHash ) expect(await keeper.prevRewardsRoot()).to.eq(rewardsUpdateParams.rewardsRoot) expect(await keeper.rewardsRoot()).to.eq(newRewardsUpdate.root) - expect(await keeper.rewardsNonce()).to.eq(3) + expect(await keeper.rewardsNonce()).to.eq(globalRewardsNonce + 2) await snapshotGasCost(receipt) }) }) @@ -253,6 +263,7 @@ describe('KeeperRewards', () => { feePercent, metadataIpfsHash, }) + await increaseTime(REWARDS_DELAY) }) it('returns false for uncollateralized vault', async () => { @@ -272,6 +283,7 @@ describe('KeeperRewards', () => { expect(await keeper.canHarvest(await vault.getAddress())).to.equal(false) expect(await keeper.isHarvestRequired(await vault.getAddress())).to.equal(false) expect(await vault.isStateUpdateRequired()).to.equal(false) + const globalRewardsNonce = Number(await keeper.rewardsNonce()) // update rewards first time let newVaultReward = { @@ -281,13 +293,13 @@ describe('KeeperRewards', () => { } let newRewardsUpdate = await getKeeperRewardsUpdateData([newVaultReward], keeper, { updateTimestamp: 1670258895, + nonce: globalRewardsNonce, }) await keeper.connect(oracle).updateRewards({ rewardsRoot: newRewardsUpdate.root, rewardsIpfsHash: newRewardsUpdate.ipfsHash, updateTimestamp: newRewardsUpdate.updateTimestamp, avgRewardPerSecond: newRewardsUpdate.avgRewardPerSecond, - signatures: getOraclesSignatures(newRewardsUpdate.signingData), }) @@ -304,7 +316,7 @@ describe('KeeperRewards', () => { vault: await vault.getAddress(), } newRewardsUpdate = await getKeeperRewardsUpdateData([newVaultReward], keeper, { - nonce: 2, + nonce: globalRewardsNonce + 1, updateTimestamp: newTimestamp, }) await increaseTime(REWARDS_DELAY) @@ -313,7 +325,6 @@ describe('KeeperRewards', () => { rewardsIpfsHash: newRewardsUpdate.ipfsHash, updateTimestamp: newRewardsUpdate.updateTimestamp, avgRewardPerSecond: newRewardsUpdate.avgRewardPerSecond, - signatures: getOraclesSignatures(newRewardsUpdate.signingData), }) @@ -327,6 +338,7 @@ describe('KeeperRewards', () => { describe('harvest (own escrow)', () => { let harvestParams: IKeeperRewards.HarvestParamsStruct let ownMevVault: EthVault + let globalRewardsNonce: number beforeEach(async () => { ownMevVault = await createVault( @@ -361,7 +373,11 @@ describe('KeeperRewards', () => { }) } - const rewardsUpdate = await getKeeperRewardsUpdateData(vaultRewards, keeper) + globalRewardsNonce = Number(await keeper.rewardsNonce()) + const rewardsUpdate = await getKeeperRewardsUpdateData(vaultRewards, keeper, { + nonce: globalRewardsNonce, + }) + await increaseTime(REWARDS_DELAY) await keeper.connect(oracle).updateRewards({ rewardsRoot: rewardsUpdate.root, updateTimestamp: rewardsUpdate.updateTimestamp, @@ -375,6 +391,8 @@ describe('KeeperRewards', () => { unlockedMevReward: vaultReward.unlockedMevReward, proof: getRewardsRootProof(rewardsUpdate.tree, vaultReward), } + globalRewardsNonce += 1 + expect(await keeper.rewardsNonce()).to.eq(globalRewardsNonce) }) it('only vault can harvest', async () => { @@ -415,7 +433,7 @@ describe('KeeperRewards', () => { unlockedMevReward: sharedMevEscrowBalance, } const rewardsUpdate = await getKeeperRewardsUpdateData([vaultReward], keeper, { - nonce: 2, + nonce: globalRewardsNonce, updateTimestamp: 1680255895, }) await keeper.connect(oracle).updateRewards({ @@ -458,7 +476,7 @@ describe('KeeperRewards', () => { ) const rewards = await keeper.rewards(await ownMevVault.getAddress()) - expect(rewards.nonce).to.equal(2) + expect(rewards.nonce).to.equal(globalRewardsNonce) expect(rewards.assets).to.equal(harvestParams.reward) const unlockedMevRewards = await keeper.unlockedMevRewards(await ownMevVault.getAddress()) @@ -494,7 +512,7 @@ describe('KeeperRewards', () => { unlockedMevReward: 0n, } const rewardsUpdate = await getKeeperRewardsUpdateData([vaultReward], keeper, { - nonce: 2, + nonce: globalRewardsNonce, updateTimestamp: 1680255895, }) await keeper.connect(oracle).updateRewards({ @@ -522,7 +540,7 @@ describe('KeeperRewards', () => { ) let rewards = await keeper.rewards(await ownMevVault.getAddress()) - expect(rewards.nonce).to.equal(2) + expect(rewards.nonce).to.equal(globalRewardsNonce) expect(rewards.assets).to.equal(prevHarvestParams.reward) expect(await keeper.isCollateralized(await ownMevVault.getAddress())).to.equal(true) @@ -541,7 +559,7 @@ describe('KeeperRewards', () => { ) rewards = await keeper.rewards(await ownMevVault.getAddress()) - expect(rewards.nonce).to.equal(3) + expect(rewards.nonce).to.equal(globalRewardsNonce + 1) expect(rewards.assets).to.equal(currHarvestParams.reward) expect(await keeper.isCollateralized(await ownMevVault.getAddress())).to.equal(true) @@ -569,6 +587,7 @@ describe('KeeperRewards', () => { describe('harvest (shared escrow)', () => { let harvestParams: IKeeperRewards.HarvestParamsStruct let sharedMevVault: EthVault + let globalRewardsNonce: number beforeEach(async () => { sharedMevVault = await createVault( @@ -604,7 +623,11 @@ describe('KeeperRewards', () => { }) } - const rewardsUpdate = await getKeeperRewardsUpdateData(vaultRewards, keeper) + globalRewardsNonce = Number(await keeper.rewardsNonce()) + const rewardsUpdate = await getKeeperRewardsUpdateData(vaultRewards, keeper, { + nonce: globalRewardsNonce, + }) + await increaseTime(REWARDS_DELAY) await keeper.connect(oracle).updateRewards({ rewardsRoot: rewardsUpdate.root, updateTimestamp: rewardsUpdate.updateTimestamp, @@ -619,6 +642,8 @@ describe('KeeperRewards', () => { proof: getRewardsRootProof(rewardsUpdate.tree, vaultReward), } await setBalance(await sharedMevEscrow.getAddress(), BigInt(harvestParams.unlockedMevReward)) + globalRewardsNonce += 1 + expect(await keeper.rewardsNonce()).to.eq(globalRewardsNonce) }) it('only vault can harvest', async () => { @@ -663,11 +688,11 @@ describe('KeeperRewards', () => { .withArgs(await sharedMevVault.getAddress(), harvestParams.unlockedMevReward) const rewards = await keeper.rewards(await sharedMevVault.getAddress()) - expect(rewards.nonce).to.equal(2) + expect(rewards.nonce).to.equal(globalRewardsNonce) expect(rewards.assets).to.equal(harvestParams.reward) const unlockedMevRewards = await keeper.unlockedMevRewards(await sharedMevVault.getAddress()) - expect(unlockedMevRewards.nonce).to.equal(2) + expect(unlockedMevRewards.nonce).to.equal(globalRewardsNonce) expect(unlockedMevRewards.assets).to.equal(harvestParams.unlockedMevReward) expect(await keeper.isCollateralized(await sharedMevVault.getAddress())).to.equal(true) @@ -691,7 +716,7 @@ describe('KeeperRewards', () => { } await setBalance(await sharedMevEscrow.getAddress(), vaultReward.unlockedMevReward) const rewardsUpdate = await getKeeperRewardsUpdateData([vaultReward], keeper, { - nonce: 2, + nonce: globalRewardsNonce, updateTimestamp: 1680255895, }) await keeper.connect(oracle).updateRewards({ @@ -725,7 +750,7 @@ describe('KeeperRewards', () => { .withArgs(await sharedMevVault.getAddress(), prevHarvestParams.unlockedMevReward) let rewards = await keeper.rewards(await sharedMevVault.getAddress()) - expect(rewards.nonce).to.equal(2) + expect(rewards.nonce).to.equal(globalRewardsNonce) expect(rewards.assets).to.equal(prevHarvestParams.reward) expect(await keeper.isCollateralized(await sharedMevVault.getAddress())).to.equal(true) @@ -750,7 +775,7 @@ describe('KeeperRewards', () => { expect(await ethers.provider.getBalance(await sharedMevEscrow.getAddress())).to.equal(0) rewards = await keeper.rewards(await sharedMevVault.getAddress()) - expect(rewards.nonce).to.equal(3) + expect(rewards.nonce).to.equal(globalRewardsNonce + 1) expect(rewards.assets).to.equal(currHarvestParams.reward) expect(await keeper.isCollateralized(await sharedMevVault.getAddress())).to.equal(true) diff --git a/test/KeeperValidators.spec.ts b/test/KeeperValidators.spec.ts index 4ef966c0..baa3797f 100644 --- a/test/KeeperValidators.spec.ts +++ b/test/KeeperValidators.spec.ts @@ -168,6 +168,7 @@ describe('KeeperValidators', () => { let rewards = await keeper.rewards(await vault.getAddress()) expect(rewards.nonce).to.eq(0) expect(rewards.assets).to.eq(0) + const globalRewardsNonce = await keeper.rewardsNonce() let receipt = await vault.registerValidator(approveParams, proof) await expect(receipt) @@ -176,7 +177,7 @@ describe('KeeperValidators', () => { // collateralize vault rewards = await keeper.rewards(await vault.getAddress()) - expect(rewards.nonce).to.eq(1) + expect(rewards.nonce).to.eq(globalRewardsNonce) expect(rewards.assets).to.eq(0) await snapshotGasCost(receipt) @@ -216,7 +217,7 @@ describe('KeeperValidators', () => { // doesn't collateralize twice rewards = await keeper.rewards(await vault.getAddress()) - expect(rewards.nonce).to.eq(1) + expect(rewards.nonce).to.eq(globalRewardsNonce) expect(rewards.assets).to.eq(0) await snapshotGasCost(receipt) @@ -370,6 +371,7 @@ describe('KeeperValidators', () => { expect(rewards.nonce).to.eq(0) expect(rewards.assets).to.eq(0) const validatorsConcat = Buffer.concat(validators) + const globalRewardsNonce = await keeper.rewardsNonce() let receipt = await vault.registerValidators( approveParams, @@ -382,7 +384,7 @@ describe('KeeperValidators', () => { .withArgs(await vault.getAddress(), approveParams.exitSignaturesIpfsHash) rewards = await keeper.rewards(await vault.getAddress()) - expect(rewards.nonce).to.eq(1) + expect(rewards.nonce).to.eq(globalRewardsNonce) expect(rewards.assets).to.eq(0) await snapshotGasCost(receipt) @@ -424,7 +426,7 @@ describe('KeeperValidators', () => { // doesn't collateralize twice rewards = await keeper.rewards(await vault.getAddress()) - expect(rewards.nonce).to.eq(1) + expect(rewards.nonce).to.eq(globalRewardsNonce) expect(rewards.assets).to.eq(0) await snapshotGasCost(receipt) diff --git a/test/OsToken.spec.ts b/test/OsToken.spec.ts index 8b54372e..4a36b431 100644 --- a/test/OsToken.spec.ts +++ b/test/OsToken.spec.ts @@ -25,7 +25,8 @@ import { describe('OsToken', () => { const assets = ethers.parseEther('2') - const initialSupply = 1000 + const initialHolderShares = 1000n + let initialSupply: bigint let initialHolder: Wallet, spender: Wallet, admin: Wallet, dao: Wallet, recipient: Wallet let osTokenVaultController: OsTokenVaultController, osToken: OsToken @@ -51,7 +52,8 @@ describe('OsToken', () => { .deposit(initialHolder.address, ZERO_ADDRESS, { value: assets }) await vault .connect(initialHolder) - .mintOsToken(initialHolder.address, initialSupply, ZERO_ADDRESS) + .mintOsToken(initialHolder.address, initialHolderShares, ZERO_ADDRESS) + initialSupply = await osToken.totalSupply() }) describe('capacity', () => { @@ -123,9 +125,11 @@ describe('OsToken', () => { it('controller can mint', async () => { await osToken.connect(dao).setController(initialHolder.address, true) - const receipt = await osToken.connect(initialHolder).mint(dao.address, 1) - await expect(receipt).to.emit(osToken, 'Transfer').withArgs(ZERO_ADDRESS, dao.address, 1) - expect(await osToken.balanceOf(dao.address)).to.eq(1) + const receipt = await osToken.connect(initialHolder).mint(recipient.address, 1) + await expect(receipt) + .to.emit(osToken, 'Transfer') + .withArgs(ZERO_ADDRESS, recipient.address, 1) + expect(await osToken.balanceOf(recipient.address)).to.eq(1) }) it('not controller cannot burn', async () => { @@ -136,10 +140,12 @@ describe('OsToken', () => { it('controller can burn', async () => { await osToken.connect(dao).setController(initialHolder.address, true) - await osToken.connect(initialHolder).mint(dao.address, 1) - const receipt = await osToken.connect(initialHolder).burn(dao.address, 1) - await expect(receipt).to.emit(osToken, 'Transfer').withArgs(dao.address, ZERO_ADDRESS, 1) - expect(await osToken.balanceOf(dao.address)).to.eq(0) + await osToken.connect(initialHolder).mint(recipient.address, 1) + const receipt = await osToken.connect(initialHolder).burn(recipient.address, 1) + await expect(receipt) + .to.emit(osToken, 'Transfer') + .withArgs(recipient.address, ZERO_ADDRESS, 1) + expect(await osToken.balanceOf(recipient.address)).to.eq(0) }) }) @@ -223,16 +229,16 @@ describe('OsToken', () => { describe('when the requested account has some tokens', () => { it('returns the total amount of tokens', async () => { - expect(await osToken.balanceOf(initialHolder.address)).to.eq(initialSupply) + expect(await osToken.balanceOf(initialHolder.address)).to.eq(initialHolderShares) }) }) }) describe('transfer', () => { - const balance = initialSupply + const balance = initialHolderShares it('reverts when the sender does not have enough balance', async () => { - const amount = balance + 1 + const amount = balance + 1n await expect( osToken.connect(initialHolder).transfer(recipient.address, amount) ).to.be.revertedWithCustomError(osToken, 'ERC20InsufficientBalance') @@ -245,7 +251,7 @@ describe('OsToken', () => { }) describe('when the sender transfers all balance', () => { - const amount = initialSupply + const amount = initialHolderShares it('transfers the requested amount', async () => { const receipt = await osToken.connect(initialHolder).transfer(recipient.address, amount) @@ -263,7 +269,7 @@ describe('OsToken', () => { describe('when the sender transfers zero tokens', () => { const amount = 0 - const balance = initialSupply + const balance = initialHolderShares it('transfers the requested amount', async () => { const receipt = await osToken.connect(initialHolder).transfer(recipient.address, amount) @@ -283,11 +289,11 @@ describe('OsToken', () => { describe('transfer from', () => { describe('when the spender has enough allowance', () => { beforeEach(async () => { - await osToken.connect(initialHolder).approve(spender.address, initialSupply) + await osToken.connect(initialHolder).approve(spender.address, initialHolderShares) }) describe('when the token owner has enough balance', () => { - const amount = initialSupply + const amount = initialHolderShares it('transfers the requested amount', async () => { const receipt = await osToken @@ -315,7 +321,7 @@ describe('OsToken', () => { }) describe('when the token owner does not have enough balance', () => { - const amount = initialSupply + const amount = initialHolderShares beforeEach('reducing balance', async () => { await osToken.connect(initialHolder).transfer(spender.address, 1) @@ -330,14 +336,14 @@ describe('OsToken', () => { }) describe('when the spender does not have enough allowance', () => { - const allowance = initialSupply - 1 + const allowance = initialHolderShares - 1n beforeEach(async () => { await osToken.connect(initialHolder).approve(spender.address, allowance) }) describe('when the token owner has enough balance', () => { - const amount = initialSupply + const amount = initialHolderShares it('reverts', async () => { await expect( @@ -385,7 +391,7 @@ describe('OsToken', () => { }) describe('when the sender has enough balance', () => { - const amount = initialSupply + const amount = initialHolderShares it('emits an approval event', async () => { await expect(osToken.connect(initialHolder).approve(spender.address, amount)) @@ -415,7 +421,7 @@ describe('OsToken', () => { }) describe('when the sender does not have enough balance', () => { - const amount = initialSupply + 1 + const amount = initialHolderShares + 1n it('emits an approval event', async () => { await expect(osToken.connect(initialHolder).approve(spender.address, amount)) diff --git a/test/OsTokenConfig.spec.ts b/test/OsTokenConfig.spec.ts index e19f9597..fcb31af5 100644 --- a/test/OsTokenConfig.spec.ts +++ b/test/OsTokenConfig.spec.ts @@ -13,6 +13,7 @@ import { MAX_UINT16, } from './shared/constants' import snapshotGasCost from './shared/snapshotGasCost' +import { MAINNET_FORK } from '../helpers/constants' describe('OsTokenConfig', () => { const newConfig = { @@ -31,6 +32,7 @@ describe('OsTokenConfig', () => { }) it('updates in constructor', async () => { + if (MAINNET_FORK.enabled) return expect(await osTokenConfig.redeemFromLtvPercent()).to.be.eq(OSTOKEN_REDEEM_FROM_LTV) expect(await osTokenConfig.redeemToLtvPercent()).to.be.eq(OSTOKEN_REDEEM_TO_LTV) expect(await osTokenConfig.liqThresholdPercent()).to.be.eq(OSTOKEN_LIQ_THRESHOLD) diff --git a/test/PriceFeed.test.ts b/test/PriceFeed.test.ts index 308a1455..4c34eeaa 100644 --- a/test/PriceFeed.test.ts +++ b/test/PriceFeed.test.ts @@ -7,6 +7,7 @@ import { createPriceFeed, ethVaultFixture } from './shared/fixtures' import { ONE_DAY, ZERO_ADDRESS } from './shared/constants' import { collateralizeEthVault, getRewardsRootProof, updateRewards } from './shared/rewards' import { increaseTime } from './shared/utils' +import { MAINNET_FORK } from '../helpers/constants' describe('PriceFeed', () => { const shares = ethers.parseEther('2') @@ -76,6 +77,7 @@ describe('PriceFeed', () => { }) it('works with zero supply', async () => { + if (MAINNET_FORK.enabled) return const expectedValue = ethers.parseEther('1') expect(await osTokenVaultController.totalShares()).to.eq(0) expect(await priceFeed.latestAnswer()).to.eq(expectedValue) @@ -87,7 +89,6 @@ describe('PriceFeed', () => { it('increments over time', async () => { await vault.connect(sender).mintOsToken(sender.address, osTokenShares, ZERO_ADDRESS) const value = await priceFeed.latestAnswer() - expect(value).to.eq(ethers.parseEther('1')) let latestRoundData = await priceFeed.latestRoundData() expect(latestRoundData[1]).to.eq(value) diff --git a/test/__snapshots__/EthErc20Vault.spec.ts.snap b/test/__snapshots__/EthErc20Vault.spec.ts.snap index 966c66ae..3cd5008f 100644 --- a/test/__snapshots__/EthErc20Vault.spec.ts.snap +++ b/test/__snapshots__/EthErc20Vault.spec.ts.snap @@ -31,6 +31,6 @@ Object { exports[`EthErc20Vault update exit queue emits transfer event 1`] = ` Object { "calldataByteLength": 196, - "gasUsed": 137297, + "gasUsed": 137285, } `; diff --git a/test/__snapshots__/EthGenesisVault.spec.ts.snap b/test/__snapshots__/EthGenesisVault.spec.ts.snap index c7228cb3..ed52fc8b 100644 --- a/test/__snapshots__/EthGenesisVault.spec.ts.snap +++ b/test/__snapshots__/EthGenesisVault.spec.ts.snap @@ -3,35 +3,35 @@ exports[`EthGenesisVault can deposit through receive fallback function 1`] = ` Object { "calldataByteLength": 4, - "gasUsed": 74328, + "gasUsed": 76526, } `; exports[`EthGenesisVault migrate migrates from rewardEthToken 1`] = ` Object { "calldataByteLength": 100, - "gasUsed": 79648, + "gasUsed": 127843, } `; exports[`EthGenesisVault pulls assets on claim exited assets 1`] = ` Object { "calldataByteLength": 100, - "gasUsed": 62296, + "gasUsed": 67250, } `; exports[`EthGenesisVault pulls assets on multiple validators registration 1`] = ` Object { "calldataByteLength": 3716, - "gasUsed": 666476, + "gasUsed": 587247, } `; exports[`EthGenesisVault pulls assets on single validator registration 1`] = ` Object { - "calldataByteLength": 1476, - "gasUsed": 336175, + "calldataByteLength": 1156, + "gasUsed": 282840, } `; @@ -45,13 +45,13 @@ Object { exports[`EthGenesisVault update state splits penalty between rewardEthToken and vault 1`] = ` Object { "calldataByteLength": 196, - "gasUsed": 100284, + "gasUsed": 134548, } `; exports[`EthGenesisVault update state splits reward between rewardEthToken and vault 1`] = ` Object { "calldataByteLength": 196, - "gasUsed": 167315, + "gasUsed": 149683, } `; diff --git a/test/__snapshots__/EthVault.burn.spec.ts.snap b/test/__snapshots__/EthVault.burn.spec.ts.snap index 59d844fa..bf42b8c6 100644 --- a/test/__snapshots__/EthVault.burn.spec.ts.snap +++ b/test/__snapshots__/EthVault.burn.spec.ts.snap @@ -3,13 +3,13 @@ exports[`EthVault - burn burns osTokens 1`] = ` Object { "calldataByteLength": 36, - "gasUsed": 101220, + "gasUsed": 84144, } `; exports[`EthVault - burn updates position accumulated fee 1`] = ` Object { "calldataByteLength": 36, - "gasUsed": 101220, + "gasUsed": 84144, } `; diff --git a/test/__snapshots__/EthVault.liquidate.spec.ts.snap b/test/__snapshots__/EthVault.liquidate.spec.ts.snap index 1f09f2a0..32ab5925 100644 --- a/test/__snapshots__/EthVault.liquidate.spec.ts.snap +++ b/test/__snapshots__/EthVault.liquidate.spec.ts.snap @@ -3,13 +3,13 @@ exports[`EthVault - liquidate calculates liquidation correctly 1`] = ` Object { "calldataByteLength": 100, - "gasUsed": 107267, + "gasUsed": 116891, } `; exports[`EthVault - liquidate can liquidate 1`] = ` Object { "calldataByteLength": 100, - "gasUsed": 109928, + "gasUsed": 119552, } `; diff --git a/test/__snapshots__/EthVault.mint.spec.ts.snap b/test/__snapshots__/EthVault.mint.spec.ts.snap index 7ea316c8..ea4da426 100644 --- a/test/__snapshots__/EthVault.mint.spec.ts.snap +++ b/test/__snapshots__/EthVault.mint.spec.ts.snap @@ -3,20 +3,13 @@ exports[`EthVault - mint mints osTokens to the receiver 1`] = ` Object { "calldataByteLength": 100, - "gasUsed": 171574, + "gasUsed": 150609, } `; exports[`EthVault - mint updates position accumulated fee 1`] = ` Object { "calldataByteLength": 100, - "gasUsed": 171574, -} -`; - -exports[`EthVault - mint updates position accumulated fee 2`] = ` -Object { - "calldataByteLength": 100, - "gasUsed": 133786, + "gasUsed": 116686, } `; diff --git a/test/__snapshots__/EthVault.redeem.spec.ts.snap b/test/__snapshots__/EthVault.redeem.spec.ts.snap index b8e1fc9a..e3e6db9b 100644 --- a/test/__snapshots__/EthVault.redeem.spec.ts.snap +++ b/test/__snapshots__/EthVault.redeem.spec.ts.snap @@ -3,13 +3,13 @@ exports[`EthVault - redeem osToken calculates redeem correctly 1`] = ` Object { "calldataByteLength": 100, - "gasUsed": 123428, + "gasUsed": 123452, } `; exports[`EthVault - redeem osToken can redeem 1`] = ` Object { "calldataByteLength": 100, - "gasUsed": 126089, + "gasUsed": 126113, } `; diff --git a/test/__snapshots__/EthVault.register.spec.ts.snap b/test/__snapshots__/EthVault.register.spec.ts.snap index 248373bb..a4037b7f 100644 --- a/test/__snapshots__/EthVault.register.spec.ts.snap +++ b/test/__snapshots__/EthVault.register.spec.ts.snap @@ -2,14 +2,14 @@ exports[`EthVault - register multiple validators succeeds 1`] = ` Object { - "calldataByteLength": 3652, - "gasUsed": 707252, + "calldataByteLength": 3332, + "gasUsed": 586323, } `; exports[`EthVault - register single validator succeeds 1`] = ` Object { - "calldataByteLength": 1444, - "gasUsed": 356960, + "calldataByteLength": 1124, + "gasUsed": 287468, } `; diff --git a/test/__snapshots__/EthVault.state.spec.ts.snap b/test/__snapshots__/EthVault.state.spec.ts.snap index 21a81c05..06f3ed08 100644 --- a/test/__snapshots__/EthVault.state.spec.ts.snap +++ b/test/__snapshots__/EthVault.state.spec.ts.snap @@ -17,6 +17,6 @@ Object { exports[`EthVault - state updates exit queue 1`] = ` Object { "calldataByteLength": 196, - "gasUsed": 144533, + "gasUsed": 144545, } `; diff --git a/test/__snapshots__/EthVault.token.spec.ts.snap b/test/__snapshots__/EthVault.token.spec.ts.snap index aabfb852..fcc2060a 100644 --- a/test/__snapshots__/EthVault.token.spec.ts.snap +++ b/test/__snapshots__/EthVault.token.spec.ts.snap @@ -17,7 +17,7 @@ Object { exports[`EthVault - token permit accepts owner signature 1`] = ` Object { "calldataByteLength": 228, - "gasUsed": 82423, + "gasUsed": 83244, } `; diff --git a/test/__snapshots__/KeeperRewards.spec.ts.snap b/test/__snapshots__/KeeperRewards.spec.ts.snap index 92e3b9f7..6a888547 100644 --- a/test/__snapshots__/KeeperRewards.spec.ts.snap +++ b/test/__snapshots__/KeeperRewards.spec.ts.snap @@ -2,15 +2,15 @@ exports[`KeeperRewards harvest (own escrow) succeeds for latest rewards root 1`] = ` Object { - "calldataByteLength": 292, - "gasUsed": 110690, + "calldataByteLength": 324, + "gasUsed": 111406, } `; exports[`KeeperRewards harvest (own escrow) succeeds for previous rewards root 1`] = ` Object { - "calldataByteLength": 292, - "gasUsed": 112844, + "calldataByteLength": 324, + "gasUsed": 113560, } `; @@ -23,15 +23,15 @@ Object { exports[`KeeperRewards harvest (shared escrow) succeeds for latest rewards root 1`] = ` Object { - "calldataByteLength": 292, - "gasUsed": 143945, + "calldataByteLength": 324, + "gasUsed": 144631, } `; exports[`KeeperRewards harvest (shared escrow) succeeds for previous rewards root 1`] = ` Object { - "calldataByteLength": 292, - "gasUsed": 146099, + "calldataByteLength": 324, + "gasUsed": 146785, } `; @@ -52,20 +52,20 @@ Object { exports[`KeeperRewards update rewards succeeds 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 140739, + "gasUsed": 149905, } `; exports[`KeeperRewards update rewards succeeds 2`] = ` Object { "calldataByteLength": 772, - "gasUsed": 123555, + "gasUsed": 130005, } `; exports[`KeeperRewards update rewards succeeds with all signatures 1`] = ` Object { "calldataByteLength": 1156, - "gasUsed": 146931, + "gasUsed": 156085, } `; diff --git a/test/__snapshots__/KeeperValidators.spec.ts.snap b/test/__snapshots__/KeeperValidators.spec.ts.snap index c4bb0624..e7f18639 100644 --- a/test/__snapshots__/KeeperValidators.spec.ts.snap +++ b/test/__snapshots__/KeeperValidators.spec.ts.snap @@ -3,28 +3,28 @@ exports[`KeeperValidators register multiple validators succeeds 1`] = ` Object { "calldataByteLength": 3716, - "gasUsed": 708307, + "gasUsed": 592652, } `; exports[`KeeperValidators register multiple validators succeeds 2`] = ` Object { "calldataByteLength": 3716, - "gasUsed": 616737, + "gasUsed": 567598, } `; exports[`KeeperValidators register single validator succeeds 1`] = ` Object { "calldataByteLength": 1508, - "gasUsed": 358020, + "gasUsed": 293808, } `; exports[`KeeperValidators register single validator succeeds 2`] = ` Object { "calldataByteLength": 1540, - "gasUsed": 305338, + "gasUsed": 259181, } `; diff --git a/test/__snapshots__/OsToken.spec.ts.snap b/test/__snapshots__/OsToken.spec.ts.snap index 85f2f08b..29d492c5 100644 --- a/test/__snapshots__/OsToken.spec.ts.snap +++ b/test/__snapshots__/OsToken.spec.ts.snap @@ -38,7 +38,7 @@ Object { exports[`OsToken fee percent owner can change 1`] = ` Object { "calldataByteLength": 36, - "gasUsed": 77683, + "gasUsed": 60583, } `; @@ -52,7 +52,7 @@ Object { exports[`OsToken permit accepts owner signature 1`] = ` Object { "calldataByteLength": 228, - "gasUsed": 74554, + "gasUsed": 74791, } `; diff --git a/test/__snapshots__/RewardSplitterFactory.spec.ts.snap b/test/__snapshots__/RewardSplitterFactory.spec.ts.snap index 744fad00..647b75cc 100644 --- a/test/__snapshots__/RewardSplitterFactory.spec.ts.snap +++ b/test/__snapshots__/RewardSplitterFactory.spec.ts.snap @@ -3,6 +3,6 @@ exports[`RewardSplitterFactory splitter deployment gas 1`] = ` Object { "calldataByteLength": 36, - "gasUsed": 138074, + "gasUsed": 138062, } `; diff --git a/test/__snapshots__/SharedMevEscrow.spec.ts.snap b/test/__snapshots__/SharedMevEscrow.spec.ts.snap index d26d366c..89854280 100644 --- a/test/__snapshots__/SharedMevEscrow.spec.ts.snap +++ b/test/__snapshots__/SharedMevEscrow.spec.ts.snap @@ -3,6 +3,6 @@ exports[`SharedMevEscrow vault deployment gas 1`] = ` Object { "calldataByteLength": 683, - "gasUsed": 167457, + "gasUsed": 167445, } `; diff --git a/test/shared/constants.ts b/test/shared/constants.ts index b3341539..049f0d64 100644 --- a/test/shared/constants.ts +++ b/test/shared/constants.ts @@ -32,12 +32,12 @@ export const ORACLES = [ Buffer.from('ca960119ad719a55764d0f5913fb354c301f614d3f49219c7b03202b2062890f', 'hex'), ] export const REWARDS_MIN_ORACLES = 6 -export const VALIDATORS_MIN_ORACLES = 11 +export const VALIDATORS_MIN_ORACLES = 6 export const ORACLES_CONFIG = 'QmbwQ6zFEWs1SjLPGk4NNJqn4wduVe6dK3xyte2iG59Uru' export const EXITING_ASSETS_MIN_DELAY = ONE_DAY export const OSTOKEN_FEE = 500 // 5% export const OSTOKEN_CAPACITY = ethers.parseEther('10000000') -export const OSTOKEN_NAME = 'SW Staked ETH' +export const OSTOKEN_NAME = 'Staked ETH' export const OSTOKEN_SYMBOL = 'osETH' export const OSTOKEN_LIQ_THRESHOLD = 9200 // 92% diff --git a/test/shared/fixtures.ts b/test/shared/fixtures.ts index 72ced0d1..d2801e86 100644 --- a/test/shared/fixtures.ts +++ b/test/shared/fixtures.ts @@ -1,6 +1,10 @@ import hre, { ethers } from 'hardhat' -import { BigNumberish, Contract, Wallet } from 'ethers' +import { BigNumberish, Contract, Signer, Wallet } from 'ethers' import { simulateDeployImpl } from '@openzeppelin/hardhat-upgrades/dist/utils' +import { + impersonateAccount, + stopImpersonatingAccount, +} from '@nomicfoundation/hardhat-toolbox/network-helpers' import { signTypedData, SignTypedDataVersion } from '@metamask/eth-sig-util' import EthereumWallet from 'ethereumjs-wallet' import { @@ -58,13 +62,39 @@ import { VALIDATORS_MIN_ORACLES, } from './constants' import { EthErc20VaultInitParamsStruct, EthVaultInitParamsStruct } from './types' -import { extractVaultAddress } from './utils' import { DepositorMock } from '../../typechain-types/contracts/mocks/DepositorMock' import { DepositorMock__factory } from '../../typechain-types/factories/contracts/mocks/DepositorMock__factory' import { UnknownVaultMock } from '../../typechain-types/contracts/mocks/UnknownVaultMock' import { UnknownVaultMock__factory } from '../../typechain-types/factories/contracts/mocks/UnknownVaultMock__factory' import { MulticallMock__factory } from '../../typechain-types/factories/contracts/mocks/MulticallMock__factory' import { MulticallMock } from '../../typechain-types/contracts/mocks/MulticallMock' +import { extractVaultAddress, setBalance } from './utils' +import { NETWORKS, MAINNET_FORK } from '../../helpers/constants' +import mainnetDeployment from '../../deployments/mainnet.json' + +export const transferOwnership = async function ( + contract: + | Keeper + | VaultsRegistry + | OsTokenVaultController + | OsToken + | OsTokenConfig + | CumulativeMerkleDrop, + newOwner: Signer +) { + const currentOwnerAddr = await contract.owner() + const newOwnerAddr = await newOwner.getAddress() + if (currentOwnerAddr == newOwnerAddr) return + + await impersonateAccount(currentOwnerAddr) + const currentOwner = await ethers.provider.getSigner(currentOwnerAddr) + + await setBalance(currentOwnerAddr, ethers.parseEther('1')) + await contract.connect(currentOwner).transferOwnership(newOwnerAddr) + await stopImpersonatingAccount(currentOwnerAddr) + + await contract.connect(newOwner).acceptOwnership() +} export const createDepositorMock = async function (vault: EthVault): Promise { const depositorMockFactory = await ethers.getContractFactory('DepositorMock') @@ -96,64 +126,81 @@ export const createMulticallMock = async function (): Promise { } export const createValidatorsRegistry = async function (): Promise { const validatorsRegistryFactory = await getValidatorsRegistryFactory() + const signer = await ethers.provider.getSigner() + + if (MAINNET_FORK.enabled) { + return new Contract( + NETWORKS.mainnet.validatorsRegistry, + validatorsRegistryFactory.interface, + signer + ) + } const contract = await validatorsRegistryFactory.deploy() - return new Contract( - await contract.getAddress(), - validatorsRegistryFactory.interface, - await ethers.provider.getSigner() - ) + return new Contract(await contract.getAddress(), validatorsRegistryFactory.interface, signer) } export const createPoolEscrow = async function ( stakedEthTokenAddress: string ): Promise { + const signer = await ethers.provider.getSigner() + + if (MAINNET_FORK.enabled) { + return PoolEscrowMock__factory.connect(NETWORKS.mainnet.genesisVault.poolEscrow, signer) + } const factory = await ethers.getContractFactory('PoolEscrowMock') const contract = await factory.deploy(stakedEthTokenAddress) - return PoolEscrowMock__factory.connect( - await contract.getAddress(), - await ethers.provider.getSigner() - ) + return PoolEscrowMock__factory.connect(await contract.getAddress(), signer) } export const createVaultsRegistry = async function (): Promise { + const signer = await ethers.provider.getSigner() + + if (MAINNET_FORK.enabled) { + const contract = VaultsRegistry__factory.connect(mainnetDeployment.VaultsRegistry, signer) + await transferOwnership(contract, signer) + return contract + } const factory = await ethers.getContractFactory('VaultsRegistry') const contract = await factory.deploy() - return VaultsRegistry__factory.connect( - await contract.getAddress(), - await ethers.provider.getSigner() - ) + return VaultsRegistry__factory.connect(await contract.getAddress(), signer) } export const createSharedMevEscrow = async function ( vaultsRegistry: VaultsRegistry ): Promise { + const signer = await ethers.provider.getSigner() + if (MAINNET_FORK.enabled) { + return SharedMevEscrow__factory.connect(mainnetDeployment.SharedMevEscrow, signer) + } const factory = await ethers.getContractFactory('SharedMevEscrow') const contract = await factory.deploy(await vaultsRegistry.getAddress()) - return SharedMevEscrow__factory.connect( - await contract.getAddress(), - await ethers.provider.getSigner() - ) + return SharedMevEscrow__factory.connect(await contract.getAddress(), signer) } export const createPriceFeed = async function ( osTokenVaultController: OsTokenVaultController, description: string ): Promise { + const signer = await ethers.provider.getSigner() + if (MAINNET_FORK.enabled) { + return PriceFeed__factory.connect(mainnetDeployment.PriceFeed, signer) + } const factory = await ethers.getContractFactory('PriceFeed') const contract = await factory.deploy(await osTokenVaultController.getAddress(), description) - return PriceFeed__factory.connect(await contract.getAddress(), await ethers.provider.getSigner()) + return PriceFeed__factory.connect(await contract.getAddress(), signer) } export const createRewardSplitterFactory = async function (): Promise { + const signer = await ethers.provider.getSigner() + if (MAINNET_FORK.enabled) { + return RewardSplitterFactory__factory.connect(mainnetDeployment.RewardSplitterFactory, signer) + } let factory = await ethers.getContractFactory('RewardSplitter') const rewardSplitterImpl = await factory.deploy() factory = await ethers.getContractFactory('RewardSplitterFactory') const contract = await factory.deploy(await rewardSplitterImpl.getAddress()) - return RewardSplitterFactory__factory.connect( - await contract.getAddress(), - await ethers.provider.getSigner() - ) + return RewardSplitterFactory__factory.connect(await contract.getAddress(), signer) } export const createOsTokenVaultController = async function ( @@ -165,6 +212,16 @@ export const createOsTokenVaultController = async function ( feePercent: BigNumberish, capacity: BigNumberish ): Promise { + const signer = await ethers.provider.getSigner() + if (MAINNET_FORK.enabled) { + const contract = OsTokenVaultController__factory.connect( + mainnetDeployment.OsTokenVaultController, + signer + ) + await transferOwnership(contract, governor) + await contract.connect(governor).setTreasury(treasury.address) + return contract + } const factory = await ethers.getContractFactory('OsTokenVaultController') const contract = await factory.deploy( keeperAddress, @@ -175,10 +232,7 @@ export const createOsTokenVaultController = async function ( feePercent, capacity ) - return OsTokenVaultController__factory.connect( - await contract.getAddress(), - await ethers.provider.getSigner() - ) + return OsTokenVaultController__factory.connect(await contract.getAddress(), signer) } export const createOsToken = async function ( @@ -187,6 +241,12 @@ export const createOsToken = async function ( name: string, symbol: string ): Promise { + const signer = await ethers.provider.getSigner() + if (MAINNET_FORK.enabled) { + const contract = OsToken__factory.connect(mainnetDeployment.OsToken, signer) + await transferOwnership(contract, governor) + return contract + } const factory = await ethers.getContractFactory('OsToken') const contract = await factory.deploy( governor.address, @@ -194,7 +254,7 @@ export const createOsToken = async function ( name, symbol ) - return OsToken__factory.connect(await contract.getAddress(), await ethers.provider.getSigner()) + return OsToken__factory.connect(await contract.getAddress(), signer) } export const createOsTokenConfig = async function ( @@ -205,6 +265,12 @@ export const createOsTokenConfig = async function ( liqBonusPercent: BigNumberish, ltvPercent: BigNumberish ): Promise { + const signer = await ethers.provider.getSigner() + if (MAINNET_FORK.enabled) { + const contract = OsTokenConfig__factory.connect(mainnetDeployment.OsTokenConfig, signer) + await transferOwnership(contract, owner) + return contract + } const factory = await ethers.getContractFactory('OsTokenConfig') const contract = await factory.deploy(owner.address, { redeemFromLtvPercent, @@ -213,10 +279,7 @@ export const createOsTokenConfig = async function ( liqBonusPercent, ltvPercent, }) - return OsTokenConfig__factory.connect( - await contract.getAddress(), - await ethers.provider.getSigner() - ) + return OsTokenConfig__factory.connect(await contract.getAddress(), signer) } export const createCumulativeMerkleDrop = async function ( @@ -243,19 +306,33 @@ export const createKeeper = async function ( validatorsRegistry: Contract, validatorsMinOracles: BigNumberish ): Promise { - const factory = await ethers.getContractFactory('Keeper') - const contract = await factory.deploy( - await sharedMevEscrow.getAddress(), - await vaultsRegistry.getAddress(), - await osTokenVaultController.getAddress(), - rewardsDelay, - maxAvgRewardPerSecond, - await validatorsRegistry.getAddress() - ) - const keeper = Keeper__factory.connect( - await contract.getAddress(), - await ethers.provider.getSigner() - ) + const signer = await ethers.provider.getSigner() + let keeper: Keeper + if (MAINNET_FORK.enabled) { + keeper = Keeper__factory.connect(mainnetDeployment.Keeper, signer) + } else { + const factory = await ethers.getContractFactory('Keeper') + const contract = await factory.deploy( + await sharedMevEscrow.getAddress(), + await vaultsRegistry.getAddress(), + await osTokenVaultController.getAddress(), + rewardsDelay, + maxAvgRewardPerSecond, + await validatorsRegistry.getAddress() + ) + keeper = Keeper__factory.connect(await contract.getAddress(), signer) + } + + if (MAINNET_FORK.enabled) { + // transfer dao ownership + await transferOwnership(keeper, signer) + + // drop mainnet oracles + for (const oracleAddr of MAINNET_FORK.oracles) { + await keeper.removeOracle(oracleAddr) + } + } + for (let i = 0; i < initialOracles.length; i++) { await keeper.addOracle(initialOracles[i]) } @@ -433,7 +510,7 @@ export const ethVaultFixture = async function (): Promise { // 4. deploy osToken const osToken = await createOsToken(dao, osTokenVaultController, OSTOKEN_NAME, OSTOKEN_SYMBOL) - if (_osTokenAddress != (await osToken.getAddress())) { + if (!MAINNET_FORK.enabled && _osTokenAddress != (await osToken.getAddress())) { throw new Error('Invalid calculated OsToken address') } @@ -455,7 +532,7 @@ export const ethVaultFixture = async function (): Promise { validatorsRegistry, VALIDATORS_MIN_ORACLES ) - if (_keeperAddress != (await keeper.getAddress())) { + if (!MAINNET_FORK.enabled && _keeperAddress != (await keeper.getAddress())) { throw new Error('Invalid calculated Keeper address') } @@ -471,44 +548,76 @@ export const ethVaultFixture = async function (): Promise { // 7. deploy implementations and factories const factories: EthVaultFactory[] = [] - for (const vaultType of [ - 'EthVault', - 'EthVaultMock', - 'EthPrivVault', - 'EthErc20Vault', - 'EthPrivErc20Vault', - ]) { - const vaultImpl = await deployVaultImplementation( - vaultType, - keeper, - vaultsRegistry, - await validatorsRegistry.getAddress(), - osTokenVaultController, - osTokenConfig, - sharedMevEscrow, - EXITING_ASSETS_MIN_DELAY - ) - const vaultFactory = await createEthVaultFactory(vaultImpl, vaultsRegistry) - await vaultsRegistry.addFactory(await vaultFactory.getAddress()) - await vaultsRegistry.addVaultImpl(vaultImpl) - factories.push(vaultFactory) + if (MAINNET_FORK.enabled) { + const signer = await ethers.provider.getSigner() + for (const factory of [ + mainnetDeployment.EthVaultFactory, + mainnetDeployment.EthPrivVaultFactory, + mainnetDeployment.EthErc20VaultFactory, + mainnetDeployment.EthPrivErc20VaultFactory, + ]) { + factories.push(EthVaultFactory__factory.connect(factory, signer)) + } + } else { + for (const vaultType of ['EthVault', 'EthPrivVault', 'EthErc20Vault', 'EthPrivErc20Vault']) { + const vaultImpl = await deployVaultImplementation( + vaultType, + keeper, + vaultsRegistry, + await validatorsRegistry.getAddress(), + osTokenVaultController, + osTokenConfig, + sharedMevEscrow, + EXITING_ASSETS_MIN_DELAY + ) + const vaultFactory = await createEthVaultFactory(vaultImpl, vaultsRegistry) + await vaultsRegistry.addFactory(await vaultFactory.getAddress()) + await vaultsRegistry.addVaultImpl(vaultImpl) + factories.push(vaultFactory) + } } + const vaultImpl = await deployVaultImplementation( + 'EthVaultMock', + keeper, + vaultsRegistry, + await validatorsRegistry.getAddress(), + osTokenVaultController, + osTokenConfig, + sharedMevEscrow, + EXITING_ASSETS_MIN_DELAY + ) + const vaultFactory = await createEthVaultFactory(vaultImpl, vaultsRegistry) + await vaultsRegistry.addFactory(await vaultFactory.getAddress()) + await vaultsRegistry.addVaultImpl(vaultImpl) + factories.push(vaultFactory) + // change ownership - await vaultsRegistry.initialize(dao.address) - await keeper.initialize(dao.address) + if (MAINNET_FORK.enabled) { + await transferOwnership(vaultsRegistry, dao) + await transferOwnership(keeper, dao) + } else { + await vaultsRegistry.initialize(dao.address) + await keeper.initialize(dao.address) + } + + const ethVaultFactory = factories[0] + const ethPrivVaultFactory = factories[1] + const ethErc20VaultFactory = factories[2] + const ethPrivErc20VaultFactory = factories[3] + const ethVaultMockFactory = factories[4] return { vaultsRegistry, sharedMevEscrow, keeper, validatorsRegistry, - ethVaultFactory: factories[0], - ethVaultMockFactory: factories[1], - ethPrivVaultFactory: factories[2], - ethErc20VaultFactory: factories[3], - ethPrivErc20VaultFactory: factories[4], - osTokenVaultController: osTokenVaultController, + ethVaultFactory, + ethPrivVaultFactory, + ethErc20VaultFactory, + ethPrivErc20VaultFactory, + ethVaultMockFactory, + osTokenVaultController, osTokenConfig, osToken, createEthVault: async ( @@ -516,7 +625,7 @@ export const ethVaultFixture = async function (): Promise { vaultParams: EthVaultInitParamsStruct, isOwnMevEscrow = false ): Promise => { - const tx = await factories[0] + const tx = await ethVaultFactory .connect(admin) .createVault(encodeEthVaultInitParams(vaultParams), isOwnMevEscrow, { value: SECURITY_DEPOSIT, @@ -529,7 +638,7 @@ export const ethVaultFixture = async function (): Promise { vaultParams: EthVaultInitParamsStruct, isOwnMevEscrow = false ): Promise => { - const tx = await factories[1] + const tx = await ethVaultMockFactory .connect(admin) .createVault(encodeEthVaultInitParams(vaultParams), isOwnMevEscrow, { value: SECURITY_DEPOSIT, @@ -542,7 +651,7 @@ export const ethVaultFixture = async function (): Promise { vaultParams: EthVaultInitParamsStruct, isOwnMevEscrow = false ): Promise => { - const tx = await factories[2] + const tx = await ethPrivVaultFactory .connect(admin) .createVault(encodeEthVaultInitParams(vaultParams), isOwnMevEscrow, { value: SECURITY_DEPOSIT, @@ -555,7 +664,7 @@ export const ethVaultFixture = async function (): Promise { vaultParams: EthErc20VaultInitParamsStruct, isOwnMevEscrow = false ): Promise => { - const tx = await factories[3] + const tx = await ethErc20VaultFactory .connect(admin) .createVault(encodeEthErc20VaultInitParams(vaultParams), isOwnMevEscrow, { value: SECURITY_DEPOSIT, @@ -568,7 +677,7 @@ export const ethVaultFixture = async function (): Promise { vaultParams: EthErc20VaultInitParamsStruct, isOwnMevEscrow = false ): Promise => { - const tx = await factories[4] + const tx = await ethPrivErc20VaultFactory .connect(admin) .createVault(encodeEthErc20VaultInitParams(vaultParams), isOwnMevEscrow, { value: SECURITY_DEPOSIT, diff --git a/test/shared/rewards.ts b/test/shared/rewards.ts index d61b4318..6f5b253a 100644 --- a/test/shared/rewards.ts +++ b/test/shared/rewards.ts @@ -1,5 +1,5 @@ import { ethers, network } from 'hardhat' -import { Contract, Signer, Wallet } from 'ethers' +import { Contract, Signer } from 'ethers' import { StandardMerkleTree } from '@openzeppelin/merkle-tree' import { EthVault, IKeeperRewards, Keeper } from '../../typechain-types' import { @@ -109,13 +109,15 @@ export async function collateralizeEthVault( vault: EthVault, keeper: Keeper, validatorsRegistry: Contract, - admin: Wallet + admin: Signer ) { const vaultAddress = await vault.getAddress() const balanceBefore = await ethers.provider.getBalance(vaultAddress) + const adminAddr = await admin.getAddress() + // register validator const validatorDeposit = ethers.parseEther('32') - await vault.connect(admin).deposit(admin.address, ZERO_ADDRESS, { value: validatorDeposit }) + await vault.connect(admin).deposit(adminAddr, ZERO_ADDRESS, { value: validatorDeposit }) await registerEthValidator(vault, keeper, validatorsRegistry, admin) // update rewards tree @@ -129,7 +131,7 @@ export async function collateralizeEthVault( }) // exit validator - const response = await vault.connect(admin).enterExitQueue(validatorDeposit, admin.address) + const response = await vault.connect(admin).enterExitQueue(validatorDeposit, adminAddr) const positionTicket = await extractExitPositionTicket(response) const timestamp = await getBlockTimestamp(response) diff --git a/test/shared/utils.ts b/test/shared/utils.ts index 3b871cd4..a160fae4 100644 --- a/test/shared/utils.ts +++ b/test/shared/utils.ts @@ -54,6 +54,17 @@ export const extractExitPositionTicket = async ( return log.args?.positionTicket as bigint } +export const extractDepositShares = async ( + response: ContractTransactionResponse +): Promise => { + const receipt = (await response.wait()) as ContractTransactionReceipt + const log = receipt.logs?.[receipt.logs.length - 1] + if (!('args' in log)) { + throw new Error('No logs found') + } + return log.args?.shares as bigint +} + export async function domainSeparator(name, version, chainId, verifyingContract) { return ( '0x' + diff --git a/test/shared/validators.ts b/test/shared/validators.ts index b0538a92..d9ae99ce 100644 --- a/test/shared/validators.ts +++ b/test/shared/validators.ts @@ -2,7 +2,7 @@ import { ByteVectorType, ContainerType, Type, UintNumberType } from '@chainsafe/ import { StandardMerkleTree } from '@openzeppelin/merkle-tree' import { ethers, network } from 'hardhat' import { Buffer } from 'buffer' -import { BytesLike, Contract, ContractTransactionResponse, Wallet } from 'ethers' +import { BytesLike, Contract, ContractTransactionResponse, Signer } from 'ethers' import bls from 'bls-eth-wasm' import { EthVault, Keeper } from '../../typechain-types' import { @@ -279,7 +279,7 @@ export async function registerEthValidator( vault: EthVault, keeper: Keeper, validatorsRegistry: Contract, - admin: Wallet + admin: Signer ): Promise { const validatorsData = await createEthValidatorsData(vault) const validatorsRegistryRoot = await validatorsRegistry.get_deposit_root()