From ef7e731b45b97cc043d2f346967440d526df6474 Mon Sep 17 00:00:00 2001 From: Shane Earley Date: Thu, 8 Jun 2023 01:38:39 -0400 Subject: [PATCH] Fix dev reward script --- contracts/ethereum/README.md | 85 +++--- contracts/ethereum/docs/index.md | 382 ++++++++++++++------------- contracts/ethereum/scripts/dev.ts | 43 +-- contracts/ethereum/test/operators.ts | 5 - contracts/ethereum/test/users.ts | 22 +- 5 files changed, 269 insertions(+), 268 deletions(-) diff --git a/contracts/ethereum/README.md b/contracts/ethereum/README.md index f9f38e9ac..2d6595873 100644 --- a/contracts/ethereum/README.md +++ b/contracts/ethereum/README.md @@ -23,11 +23,15 @@ graph LR B(Manager Contract) C(Beacon Deposit Contract) D(SSV Contract) - G(Oracle Contract) H(Functions Contract) I(Automation Contract) end + subgraph Oracle Dao + G(Oracle) + end + G --> B + A((User)) --> B B --> C @@ -50,9 +54,8 @@ graph LR E2 --> F24(SSV Operator n) end - G <--> I + I --> B H <--> I - I <--> B subgraph Chainlink J1(Chainlink Node 1) @@ -60,11 +63,6 @@ graph LR J3(Chainlink Node 3) J4(Chainlink Node n) end - - J1 --> G - J2 --> G - J3 --> G - J4 --> G J1 --> H J2 --> H @@ -79,7 +77,7 @@ graph LR ### Contracts -Casimir deploys two internal contracts and interfaces with suite of vendor contracts from the Consensus Specs, Chainlink, OpenZeppelin, SSV, and Uniswap. All contract source code is located in the [./src](./src) directory. A Hardhat environment for development and deployment is configured in the [hardhat.config.ts](./hardhat.config.ts) file. The following contract scripts can be executed from the **monorepo root** directory: +Casimir v1 contains five internal contracts with interfaces and uses a suite of vendor contracts from Chainlink, OpenZeppelin, SSV, and Uniswap. All contract source code is located in the [./src/v1](./src/v1) directory. A Hardhat environment for development and deployment is configured in the [hardhat.config.ts](./hardhat.config.ts) file. The following contract scripts can be executed from the **monorepo root** directory: - `npm run dev:ethereum` - Run a local Ethereum network and deploy contracts - `npm run test:ethereum` - Run tests for the Ethereum contracts @@ -88,40 +86,41 @@ Casimir deploys two internal contracts and interfaces with suite of vendor contr **Internal Contracts:** -Core internal contracts and interfaces are located in the [src](./src) directory. +Core internal contracts and interfaces are located in the root of the [src/v1](./src/v1) directory. | Contract | Description | Docs | | --- | --- | --- | -| [CasimirManager](./src/CasimirManager.sol) | Manages stake distribution | [docs/index.md#casimirmanager](./docs/index.md#casimirmanager) | -| [CasimirUpkeep](./src/CasimirUpkeep.sol) | Automates event handling | [docs/index.md#CasimirUpkeep](./docs/index.md#CasimirUpkeep) | +| [CasimirManager](./src/v1/CasimirManager.sol) | Accepts and distributes deposits | [docs/index.md#casimirmanager](./docs/index.md#casimirmanager) | +| [CasimirPool](./src/v1/CasimirPool.sol) | Accepts deposits and stakes a validator | [docs/index.md#casimirpool](./docs/index.md#casimirpool) | +| [CasimirRegistry](./src/v1/CasimirRegistry.sol) | Manages operator registration | [docs/index.md#casimirregistry](./docs/index.md#casimirregistry) | +| [CasimirUpkeep](./src/v1/CasimirUpkeep.sol) | Automates and handles reports | [docs/index.md#CasimirUpkeep](./docs/index.md#CasimirUpkeep) | +| [CasimirViews](./src/v1/CasimirViews.sol) | Provides read-only access to state | [docs/index.md#casimirviews](./docs/index.md#casimirviews) | **Vendor Contracts:** -Vendor contracts and interfaces are located in the [src/vendor](./src/vendor) directory. +Vendor contracts and interfaces are located in the [src/v1/vendor](./src/v1/vendor) directory. | Contract | Description | Docs | | --- | --- | --- | -| [DepositContract](./src/vendor/interfaces/IDepositContract.sol) | Accepts Beacon deposits | Todo | -| [Functions](./src/vendor/Functions.sol) | Provides a library for Chainlink functions | Todo | -| [FunctionsBillingRegistry](./src/vendor/interfaces/FunctionsBillingRegistryInterface.sol) | Manages Chainlink function billing | Todo | -| [FunctionsClient](./src/vendor/FunctionsClient.sol) | Executes Chainlink function requests | Todo | -| [FunctionsOracle](./src/vendor/interfaces/FunctionsOracleInterface.sol) | Handles Chainlink function requests | Todo | -| [KeeperRegistry](./src/vendor/interfaces/IKeeperRegistry.sol) | Manages Chainlink upkeeps | Todo | -| [SSVNetwork](./src/vendor/interfaces/ISSVNetwork.sol) | Connects distributed validators | Todo | -| [SSVToken](./src/vendor/interfaces/ISSVToken.sol) | Serves as operator utility token | Todo | -| [WETH](./src/vendor/interfaces/IWETH.sol) | Wraps ETH for swapping | Todo | +| [DepositContract](./src/v1/vendor/interfaces/IDepositContract.sol) | Accepts Beacon deposits | – | +| [SSVNetwork](./src/v1/vendor/interfaces/ISSVNetwork.sol) | Connects distributed validators | – | +| [WETH](./src/v1/vendor/interfaces/IWETH.sol) | Wraps ETH for swapping | – | **Mock Contracts:** -Mock (development-only) contracts and interfaces are located in the [src/mock](./src/mock) directory. +Mock (development-only) contracts and interfaces are located in the [src/mock](./src/v1/mock) directory. + +| Contract | Description | Docs | +| --- | --- | --- | +| [MockFunctionsOracle](./src/v1/mock/MockFunctionsOracle.sol) | Mock Chainlink Functions | [docs/index.md#mockfunctionsoracle](./docs/index.md#mockfunctionsoracle) | ### Distributed Key Generation -Casimir trustlessly distributes validator key shares to operators using the [rockx-dkg-cli](https://github.com/RockX-SG/rockx-dkg-cli). +Casimir distributes validator key shares to operators using the [rockx-dkg-cli](https://github.com/RockX-SG/rockx-dkg-cli). The CLI is still in development, but it can be used with the local Hardhat network to generate keys and perform DKG operations. The CLI is integrated into the Casimir oracle – use the `--mock` flag when running `npm run dev:ethereum` to enable the mock DKG CLI. Otherwise, the oracle helper scripts in [./helpers/oracle](./helpers/oracle) will use pregenerated DKG keys. ### Oracles -The contract loosely depends on two decentralized oracles. The first oracle automatically syncs validator configuration, statuses, and balances when necessary conditions are met (see [Chainlink Automation](https://docs.chain.link/chainlink-automation/introduction)) by performing external requests with trust-minimized compute infrastructure (see [Chainlink Functions](https://docs.chain.link/chainlink-functions)). The second oracle watches the manager contract events, automatically executes zero-coordination distributed key generation (DKG) operations: validator key creating, resharing, and exiting (see [Chainlink Keepers](https://docs.chain.link/chainlink-keepers/introduction)) off-chain, and submits ceremony verification proofs. +The contract uses two oracles to automate the Casimir staking experience and ensure the security of user funds. The first oracle, the Casimir upkeep, reports total validator balance, swept balance, and validator actions once per day (see [Chainlink Automation](https://docs.chain.link/chainlink-automation/introduction)) using trust-minimized compute infrastructure (see [Chainlink Functions](https://docs.chain.link/chainlink-functions)). The second oracle, the Casimir DAO oracle, watches the manager contract events and automatically executes zero-coordination distributed key generation (DKG) operations: validator key creating, resharing, and exiting (see [Chainlink Keepers](https://docs.chain.link/chainlink-keepers/introduction)) off-chain, and submits ceremony verification proofs. The DAO oracle also submits verifiable report details in response to reported validator actions (like completed exits). ## 👥 Users @@ -129,65 +128,59 @@ Users can deposit any amount of ETH to the manager contract. Their deposits are ### User Fees -The contract charges a small user fee for each deposit (and some amount TBD in reward distribution) to fund the contract's operations. The fee is a percentage of the amount deposited by a user or reward distibution. +The contract charges a 5% user fee on deposits and rewards to fund the contract's operations. **User Fee Calculation:** -1. $feePercent = fees_{LINK} + fees_{SSV}$ +1. $ethAmount = depositAmount\times{\frac{100}{100 + feePercent}}$ -2. $ethAmount = depositAmount\times{\frac{100}{100 + feePercent}}$ - -3. $feeAmount = depositAmount - ethAmount$ +2. $feeAmount = depositAmount - ethAmount$ *Where:* -- $fees_{LINK}$ is the LINK fee percentage, which is [**`getLINKFee()`**](./docs/index.md#getlinkfee) in the contract. -- $fees_{SSV}$ is the SSV fee percentage, which is [**`getSSVFee()`**](./docs/index.md#getssvfee) in the contract. -- $feePercent$ is the total fee percentage, which is the sum of the LINK and SSV fees. +- $feePercent$ is the total fee percentage, which is the sum of the required ETH, LINK, and SSV fees. - $depositAmount$ is the amount of ETH deposited. - $ethAmount$ is the amount of ETH to be distributed into the contract. - $feeAmount$ is the amount of ETH to be swapped for LINK and SSV to operate the contract. ### User Stake -The manager contract adjusts a user's stake based on the change in the total reward-to-stake distribution sum since their last interaction with the contract. Each time new rewards are distributed (after either a heartbeat interval or a threshold change is detected in the oracle), the distribution sum is updated and the new rewards are staked in an auto-compounding fashion. +The manager contract adjusts a user's stake based on the change in the total reward-to-stake ratio sum since their last interaction with the contract. Each time new rewards are reported, the ratio sum is updated to include the new rewards-to-stake ratio. The ratio sum is used to calculate a user's current stake, including compounded rewards, at any time. **User Stake Calculation:** -1. Whenever a user deposits or updates their stake, their initial stake and the current distribution sum are recorded. -2. When rewards are distributed, the distribution sum is updated to include the new reward-to-stake ratio. -3. $userStake =userStake_0\times{\frac{distributionSum}{userDistributionSum_0}}$ calculates a user's current compounded stake at any time. +1. Whenever a user deposits or updates their stake, their initial stake and the current ratio sum are recorded. +2. When rewards are distributed, the ratio sum is updated to include the new reward-to-stake ratio. +3. $userStake =userStake_0\times{\frac{stakeRatioSum}{userStakeRatioSum_0}}$ calculates a user's current compounded stake at any time. *Where:* - $userStake$ is the calculated current stake of the user, including compounded rewards. This is [**`users[userAddress].stake`**](./docs/index.md#user) in the contract. - $userStake_0$ is the initial stake of the user at the time of their last deposit or stake update. This is also [**`users[userAddress].stake`**](./docs/index.md#user) in the contract, but it is accessed before settling the user's current stake. -- $distributionSum$ is the current cumulative sum of reward-to-stake ratios in the contract. This is [**`distributionSum`**](./docs/index.md#distributionsum) in the contract. -- $userDistributionSum_0$ is the cumulative sum of reward-to-stake ratios at the time the user made their last deposit or update to their stake. This is [**`users[userAddress].distributionSum0`**](./docs/index.md#user) in the contract. +- $stakeRatioSum$ is the current cumulative sum of reward-to-stake ratios in the contract. This is [**`stakeRatioSum`**](./docs/index.md#stakeratiosum) in the contract. +- $userStakeRatioSum_0$ is the cumulative sum of reward-to-stake ratios at the time the user made their last deposit or update to their stake. This is [**`users[userAddress].stakeRatioSum0`**](./docs/index.md#user) in the contract. ### User Withdrawals -Users can initiate a withdrawal of any amount of their stake at any time. **Full exits and withdrawal liquidity are still a WIP.** In the meantime, valid user withdrawals up to the to total current `readyDeposits` will be fulfilled by the contract. Note, more notes are coming soon on withdrawal liquidity, alongside an additional contract. +Users can request a withdrawal of any amount of their stake at any time. If the requested amount is available in the buffered balance (prepooled balance plus withdrawn balance), the withdrawal is fulfilled immediately. Otherwise, the withdrawal is added to the pending withdrawals queue and fulfilled when the requested amount is available (usually within 1-4 days, depending on the amount). ## 👷 Operators -Each Casimir validator is run by four selected operators holding key share to perform duties with threshold signatures on SSV. Registration is open to any SSV operator (see [Operator Registration](./README.md#operatorregistration). Operators are selected by an algorithm that ensures high-performance but emphasizes decentralization (see [Operator Selection](./README.md#operatorselection)) as user's deposit stake and new validators are required. +Each Casimir validator is run by four selected operators holding the key shares to perform duties with threshold signatures on SSV. Registration is open to any SSV operator (see [Operator Registration](./README.md#operatorregistration). Operators are selected by an algorithm that ensures high-performance but emphasizes decentralization (see [Operator Selection](./README.md#operatorselection)) as user's deposit stake and new validators are required. ### Operator Registration -Operators can join the contract registry with a small deposit of ETH for slashing collateral (see [Operator Collateral](./README.md#operatorcollateral)) and a lightweight SSV node config add-on (see [Operator Config](./README.md#operatorconfig)). +Operators can join the contract registry with a deposit of 4 ETH for collateral (see [Operator Collateral](./README.md#operatorcollateral)) and a lightweight SSV node config add-on (see [Operator Config](./README.md#operatorconfig)). ### Operator Selection Operators are chosen to run validators based on metrics fetched and derived directly from the SSV network. These metrics are mainly performance, market share, and fees. -Todo @elizyoung0011 - we should add your details about operator selection and performance monitoring thresholds. - -Operator performance is reported by (Chainlink) monitoring SSV exporter attestations. If an operator's performance is poor for an extended period of time, and their slashing collateral is below a threshold, Casimir removes the operator from existing operator groups by resharing or exiting. The latter is only required in the case that a validator has already undergone more than two reshares to avoid leaving the full key recoverable outside of the currently selected operators. +If an operator's performance is poor for an extended period of time, or their collateral is below the threshold, Casimir removes the operator from existing operator groups by resharing or exiting. The latter is only required in the case that a validator has already undergone more than two reshares to avoid leaving the full key recoverable outside of the currently selected operators. ### Operator Collateral -Todo add notes. +Collateral is used to recover lost validator effective balance at the time of completing an exit. The loss blame is assigned to the four responsible operators based on performance over the duration of the validator's existence. ### Operator Config diff --git a/contracts/ethereum/docs/index.md b/contracts/ethereum/docs/index.md index 7015b625f..ffbadf017 100644 --- a/contracts/ethereum/docs/index.md +++ b/contracts/ethereum/docs/index.md @@ -58,6 +58,14 @@ uint256 requestedWithdrawalBalance Total pending withdrawal amount +### requestedExits + +```solidity +uint256 requestedExits +``` + +Exiting pool count + ### onlyPool ```solidity @@ -1206,6 +1214,152 @@ Fulfill the request for testing | response | bytes | Aggregated response from the user code | | err | bytes | Aggregated error from the user code or from the sweptStake pipeline Either response or error parameter will be set, but never both | +## CasimirViews + +### constructor + +```solidity +constructor(address managerAddress, address registryAddress) public +``` + +Constructor + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| managerAddress | address | The manager address | +| registryAddress | address | The registry address | + +### getCompoundablePoolIds + +```solidity +function getCompoundablePoolIds(uint256 startIndex, uint256 endIndex) external view returns (uint32[5] poolIds) +``` + +Get the next five compoundable pool IDs + +_Should be called off-chain_ + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| startIndex | uint256 | The start index | +| endIndex | uint256 | The end index | + +#### Return Values + +| Name | Type | Description | +| ---- | ---- | ----------- | +| poolIds | uint32[5] | The next five compoundable pool IDs | + +### getOperators + +```solidity +function getOperators(uint256 startIndex, uint256 endIndex) external view returns (struct ICasimirRegistry.Operator[]) +``` + +Get operators + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| startIndex | uint256 | The start index | +| endIndex | uint256 | The end index | + +#### Return Values + +| Name | Type | Description | +| ---- | ---- | ----------- | +| [0] | struct ICasimirRegistry.Operator[] | operators The operators | + +### getPendingValidatorPublicKeys + +```solidity +function getPendingValidatorPublicKeys(uint256 startIndex, uint256 endIndex) external view returns (bytes[]) +``` + +Get the pending validator public keys + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| startIndex | uint256 | The start index | +| endIndex | uint256 | The end index | + +#### Return Values + +| Name | Type | Description | +| ---- | ---- | ----------- | +| [0] | bytes[] | validatorPublicKeys The pending validator public keys | + +### getStakedValidatorPublicKeys + +```solidity +function getStakedValidatorPublicKeys(uint256 startIndex, uint256 endIndex) external view returns (bytes[]) +``` + +Get the staked validator public keys + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| startIndex | uint256 | The start index | +| endIndex | uint256 | The end index | + +#### Return Values + +| Name | Type | Description | +| ---- | ---- | ----------- | +| [0] | bytes[] | validatorPublicKeys The staked validator public keys | + +### getPoolDetails + +```solidity +function getPoolDetails(uint32 poolId) external view returns (struct ICasimirPool.PoolDetails poolDetails) +``` + +Get a pool's details by ID + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| poolId | uint32 | The pool ID | + +#### Return Values + +| Name | Type | Description | +| ---- | ---- | ----------- | +| poolDetails | struct ICasimirPool.PoolDetails | The pool details | + +### getSweptBalance + +```solidity +function getSweptBalance(uint256 startIndex, uint256 endIndex) public view returns (uint256 balance) +``` + +Get the swept balance + +_Should be called off-chain_ + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| startIndex | uint256 | The start index | +| endIndex | uint256 | The end index | + +#### Return Values + +| Name | Type | Description | +| ---- | ---- | ----------- | +| balance | uint256 | The swept balance | + ## ICasimirManager ### Token @@ -1497,6 +1651,12 @@ function feePercent() external view returns (uint32) function requestedWithdrawalBalance() external view returns (uint256) ``` +### requestedExits + +```solidity +function requestedExits() external view returns (uint256) +``` + ### finalizableCompletedExits ```solidity @@ -1863,6 +2023,44 @@ method._ | upkeepNeeded | bool | boolean to indicate whether the keeper should call performUpkeep or not. | | performData | bytes | bytes that the keeper should call performUpkeep with, if upkeep is needed. If you would like to encode data to decode later, try `abi.encode`. | +## ICasimirViews + +### getCompoundablePoolIds + +```solidity +function getCompoundablePoolIds(uint256 startIndex, uint256 endIndex) external view returns (uint32[5]) +``` + +### getOperators + +```solidity +function getOperators(uint256 startIndex, uint256 endIndex) external view returns (struct ICasimirRegistry.Operator[]) +``` + +### getPoolDetails + +```solidity +function getPoolDetails(uint32 poolId) external view returns (struct ICasimirPool.PoolDetails) +``` + +### getPendingValidatorPublicKeys + +```solidity +function getPendingValidatorPublicKeys(uint256 startIndex, uint256 endIndex) external view returns (bytes[]) +``` + +### getStakedValidatorPublicKeys + +```solidity +function getStakedValidatorPublicKeys(uint256 startIndex, uint256 endIndex) external view returns (bytes[]) +``` + +### getSweptBalance + +```solidity +function getSweptBalance(uint256 startIndex, uint256 endIndex) external view returns (uint256) +``` + ## Types32Array ### remove @@ -2027,190 +2225,6 @@ struct RegistrationParams { function registerUpkeep(struct KeeperRegistrarInterface.RegistrationParams requestParams) external returns (uint256) ``` -## CasimirViews - -### constructor - -```solidity -constructor(address managerAddress, address registryAddress) public -``` - -Constructor - -#### Parameters - -| Name | Type | Description | -| ---- | ---- | ----------- | -| managerAddress | address | The manager address | -| registryAddress | address | The registry address | - -### getCompoundablePoolIds - -```solidity -function getCompoundablePoolIds(uint256 startIndex, uint256 endIndex) external view returns (uint32[5] poolIds) -``` - -Get the next five compoundable pool IDs - -_Should be called off-chain_ - -#### Parameters - -| Name | Type | Description | -| ---- | ---- | ----------- | -| startIndex | uint256 | The start index | -| endIndex | uint256 | The end index | - -#### Return Values - -| Name | Type | Description | -| ---- | ---- | ----------- | -| poolIds | uint32[5] | The next five compoundable pool IDs | - -### getOperators - -```solidity -function getOperators(uint256 startIndex, uint256 endIndex) external view returns (struct ICasimirRegistry.Operator[]) -``` - -Get operators - -#### Parameters - -| Name | Type | Description | -| ---- | ---- | ----------- | -| startIndex | uint256 | The start index | -| endIndex | uint256 | The end index | - -#### Return Values - -| Name | Type | Description | -| ---- | ---- | ----------- | -| [0] | struct ICasimirRegistry.Operator[] | operators The operators | - -### getPendingValidatorPublicKeys - -```solidity -function getPendingValidatorPublicKeys(uint256 startIndex, uint256 endIndex) external view returns (bytes[]) -``` - -Get the pending validator public keys - -#### Parameters - -| Name | Type | Description | -| ---- | ---- | ----------- | -| startIndex | uint256 | The start index | -| endIndex | uint256 | The end index | - -#### Return Values - -| Name | Type | Description | -| ---- | ---- | ----------- | -| [0] | bytes[] | validatorPublicKeys The pending validator public keys | - -### getStakedValidatorPublicKeys - -```solidity -function getStakedValidatorPublicKeys(uint256 startIndex, uint256 endIndex) external view returns (bytes[]) -``` - -Get the staked validator public keys - -#### Parameters - -| Name | Type | Description | -| ---- | ---- | ----------- | -| startIndex | uint256 | The start index | -| endIndex | uint256 | The end index | - -#### Return Values - -| Name | Type | Description | -| ---- | ---- | ----------- | -| [0] | bytes[] | validatorPublicKeys The staked validator public keys | - -### getPoolDetails - -```solidity -function getPoolDetails(uint32 poolId) external view returns (struct ICasimirPool.PoolDetails poolDetails) -``` - -Get a pool's details by ID - -#### Parameters - -| Name | Type | Description | -| ---- | ---- | ----------- | -| poolId | uint32 | The pool ID | - -#### Return Values - -| Name | Type | Description | -| ---- | ---- | ----------- | -| poolDetails | struct ICasimirPool.PoolDetails | The pool details | - -### getSweptBalance - -```solidity -function getSweptBalance(uint256 startIndex, uint256 endIndex) public view returns (uint256 balance) -``` - -Get the swept balance - -_Should be called off-chain_ - -#### Parameters - -| Name | Type | Description | -| ---- | ---- | ----------- | -| startIndex | uint256 | The start index | -| endIndex | uint256 | The end index | - -#### Return Values - -| Name | Type | Description | -| ---- | ---- | ----------- | -| balance | uint256 | The swept balance | - -## ICasimirViews - -### getCompoundablePoolIds - -```solidity -function getCompoundablePoolIds(uint256 startIndex, uint256 endIndex) external view returns (uint32[5]) -``` - -### getOperators - -```solidity -function getOperators(uint256 startIndex, uint256 endIndex) external view returns (struct ICasimirRegistry.Operator[]) -``` - -### getPoolDetails - -```solidity -function getPoolDetails(uint32 poolId) external view returns (struct ICasimirPool.PoolDetails) -``` - -### getPendingValidatorPublicKeys - -```solidity -function getPendingValidatorPublicKeys(uint256 startIndex, uint256 endIndex) external view returns (bytes[]) -``` - -### getStakedValidatorPublicKeys - -```solidity -function getStakedValidatorPublicKeys(uint256 startIndex, uint256 endIndex) external view returns (bytes[]) -``` - -### getSweptBalance - -```solidity -function getSweptBalance(uint256 startIndex, uint256 endIndex) external view returns (uint256) -``` - ## MockFunctionsOracle ### constructor diff --git a/contracts/ethereum/scripts/dev.ts b/contracts/ethereum/scripts/dev.ts index b0f36c57b..74029cf03 100644 --- a/contracts/ethereum/scripts/dev.ts +++ b/contracts/ethereum/scripts/dev.ts @@ -67,13 +67,17 @@ void async function () { await result.wait() } + /** + * We are simulating the oracle reporting on a more frequent basis + * We also do not sweep or compound the rewards in this script + * Exit balances are swept as needed + */ let requestId = 0 - const blocksPerReport = 25 + const blocksPerReport = 10 const rewardPerValidator = 0.105 let lastReportBlock = await ethers.provider.getBlockNumber() ethers.provider.on('block', async (block) => { if (block - blocksPerReport >= lastReportBlock) { - console.log('Attempting report') await time.increase(time.duration.days(1)) lastReportBlock = await ethers.provider.getBlockNumber() await runUpkeep({ upkeep, keeper }) @@ -81,13 +85,12 @@ void async function () { const stakedPoolCount = stakedPoolIds.length const pendingPoolCount = (await manager.getPendingPoolIds()).length if (pendingPoolCount + stakedPoolCount > 0) { - console.log('Reporting') const activatedBalance = pendingPoolCount * 32 const exitingPoolCount = await manager.requestedExits() const sweptExitedBalance = exitingPoolCount.toNumber() * 32 const rewardAmount = rewardPerValidator * stakedPoolCount const latestActiveBalance = await manager.latestActiveBalance() - const nextActiveBalance = round(parseFloat(ethers.utils.formatEther(latestActiveBalance.add(activatedBalance).sub(sweptExitedBalance))) + rewardAmount) + const nextActiveBalance = round(parseFloat(ethers.utils.formatEther(latestActiveBalance)) + activatedBalance - sweptExitedBalance + rewardAmount, 10) const nextActivatedDeposits = (await manager.getPendingPoolIds()).length const nextValues = { activeBalance: nextActiveBalance, @@ -104,20 +107,23 @@ void async function () { requestId }) let remaining = exitingPoolCount.toNumber() - for (const poolId of stakedPoolIds) { - if (remaining === 0) break - const poolDetails = await views.getPoolDetails(poolId) - if (poolDetails.status === 3) { - remaining-- - const poolAddress = await manager.getPoolAddress(poolId) - const currentBalance = await ethers.provider.getBalance(poolAddress) - const nextBalance = currentBalance.add(ethers.utils.parseEther(sweptExitedBalance.toString())) - await setBalance(poolAddress, nextBalance) + if (remaining) { + for (const poolId of stakedPoolIds) { + if (remaining === 0) break + const poolDetails = await views.getPoolDetails(poolId) + if (poolDetails.status === 3) { + remaining-- + const poolAddress = await manager.getPoolAddress(poolId) + const currentBalance = await ethers.provider.getBalance(poolAddress) + const poolSweptExitedBalance = sweptExitedBalance / exitingPoolCount.toNumber() + const nextBalance = currentBalance.add(ethers.utils.parseEther(poolSweptExitedBalance.toString())) + await setBalance(poolAddress, nextBalance) + } + } + let finalizableCompletedExits = await manager.finalizableCompletedExits() + while (finalizableCompletedExits.toNumber() !== exitingPoolCount.toNumber()) { + finalizableCompletedExits = await manager.finalizableCompletedExits() } - } - let finalizableCompletedExits = await manager.finalizableCompletedExits() - while (finalizableCompletedExits.toNumber() !== exitingPoolCount.toNumber()) { - finalizableCompletedExits = await manager.finalizableCompletedExits() } await runUpkeep({ upkeep, keeper }) } @@ -131,6 +137,9 @@ void async function () { if (process.env.MOCK_ORACLE) await depositUpkeepBalanceHandler({ manager, signer: oracle }) }, 2500) + /** + * We are simulating the DAO oracle (@casimir/oracle) using the oracle helper + */ if (process.env.MOCK_ORACLE) { const handlers = { DepositRequested: initiateDepositHandler, diff --git a/contracts/ethereum/test/operators.ts b/contracts/ethereum/test/operators.ts index 50f784a03..0edefc92d 100644 --- a/contracts/ethereum/test/operators.ts +++ b/contracts/ethereum/test/operators.ts @@ -118,9 +118,7 @@ describe('Operators', async function () { await setBalance(withdrawnPoolAddress, nextBalance) await time.increase(time.duration.days(1)) - await runUpkeep({ upkeep, keeper }) - const nextValues = { activeBalance: 0, sweptBalance: sweptExitedBalance, @@ -129,16 +127,13 @@ describe('Operators', async function () { completedExits: 1, compoundablePoolIds: [0, 0, 0, 0, 0] } - await fulfillReport({ upkeep, keeper, values: nextValues, requestId }) - await reportCompletedExitsHandler({ manager, views, signer: oracle, args: { count: 1 } }) - await runUpkeep({ upkeep, keeper }) const stake = await manager.getTotalStake() diff --git a/contracts/ethereum/test/users.ts b/contracts/ethereum/test/users.ts index 182087e8c..9ff95459f 100644 --- a/contracts/ethereum/test/users.ts +++ b/contracts/ethereum/test/users.ts @@ -50,9 +50,7 @@ describe('Users', async function () { expect(pendingPoolIds.length).equal(2) await time.increase(time.duration.days(1)) - await runUpkeep({ upkeep, keeper }) - let requestId = 0 const firstReportValues = { activeBalance: 64, @@ -68,7 +66,6 @@ describe('Users', async function () { values: firstReportValues, requestId }) - await runUpkeep({ upkeep, keeper }) const stakedPoolIds = await manager.getStakedPoolIds() @@ -91,17 +88,9 @@ describe('Users', async function () { expect(ethers.utils.formatEther(stake)).equal('32.0') expect(ethers.utils.formatEther(userStake)).equal('32.0') - const sweptExitedBalance = 32 - const exitedPoolId = (await manager.getStakedPoolIds())[0] - const exitedPoolAddress = await manager.getPoolAddress(exitedPoolId) - const currentBalance = await ethers.provider.getBalance(exitedPoolAddress) - const nextBalance = currentBalance.add(ethers.utils.parseEther(sweptExitedBalance.toString())) - await setBalance(exitedPoolAddress, nextBalance) - await time.increase(time.duration.days(1)) - await runUpkeep({ upkeep, keeper }) - + const sweptExitedBalance = 32 const secondReportValues = { activeBalance: 32, sweptBalance: sweptExitedBalance, @@ -116,13 +105,14 @@ describe('Users', async function () { values: secondReportValues, requestId }) - + const exitedPoolId = (await manager.getStakedPoolIds())[0] + const exitedPoolAddress = await manager.getPoolAddress(exitedPoolId) + const currentBalance = await ethers.provider.getBalance(exitedPoolAddress) + const nextBalance = currentBalance.add(ethers.utils.parseEther(sweptExitedBalance.toString())) + await setBalance(exitedPoolAddress, nextBalance) await reportCompletedExitsHandler({ manager, views, signer: oracle, args: { count: 1 } }) - const finalizableCompletedExits = await manager.finalizableCompletedExits() - expect(finalizableCompletedExits.toNumber()).equal(1) - await runUpkeep({ upkeep, keeper }) stake = await manager.getTotalStake()