From dcad95875dfe176f0d23572a3763fa8f5886bc63 Mon Sep 17 00:00:00 2001 From: BkChoy Date: Wed, 6 Sep 2023 12:25:53 -0400 Subject: [PATCH] support redepositing of rewards in staking/priority pools --- contracts/core/PriorityPool.sol | 72 ++++++++++++++-------- contracts/core/StakingPool.sol | 37 +++++++++-- contracts/core/interfaces/IStakingPool.sol | 4 ++ 3 files changed, 81 insertions(+), 32 deletions(-) diff --git a/contracts/core/PriorityPool.sol b/contracts/core/PriorityPool.sol index 1b070cdc..f28d2c61 100644 --- a/contracts/core/PriorityPool.sol +++ b/contracts/core/PriorityPool.sol @@ -31,7 +31,8 @@ contract PriorityPool is UUPSUpgradeable, OwnableUpgradeable, PausableUpgradeabl ISDLPool public sdlPool; address public distributionOracle; - uint256 public queueDepositThreshold; + uint128 public queueDepositMin; + uint128 public queueDepositMax; PoolStatus public poolStatus; bytes32 public merkleRoot; @@ -59,7 +60,7 @@ contract PriorityPool is UUPSUpgradeable, OwnableUpgradeable, PausableUpgradeabl uint256 incrementalSharesAmount ); event SetPoolStatus(PoolStatus status); - event SetQueueDepositThreshold(uint256 threshold); + event SetQueueDepositParams(uint128 queueDepositMin, uint128 queueDepositMax); event DepositQueuedTokens(uint256 amount); error InvalidValue(); @@ -86,13 +87,15 @@ contract PriorityPool is UUPSUpgradeable, OwnableUpgradeable, PausableUpgradeabl * @param _token address of asset token * @param _stakingPool address of staking pool * @param _sdlPool address of SDL pool - * @param _queueDepositThreshold min amount of tokens needed to execute deposit + * @param _queueDepositMin min amount of tokens needed to execute deposit + * @param _queueDepositMax max amount of tokens in a single deposit **/ function initialize( address _token, address _stakingPool, address _sdlPool, - uint256 _queueDepositThreshold + uint128 _queueDepositMin, + uint128 _queueDepositMax ) public initializer { __UUPSUpgradeable_init(); __Ownable_init(); @@ -100,7 +103,8 @@ contract PriorityPool is UUPSUpgradeable, OwnableUpgradeable, PausableUpgradeabl token = IERC20Upgradeable(_token); stakingPool = IStakingPool(_stakingPool); sdlPool = ISDLPool(_sdlPool); - queueDepositThreshold = _queueDepositThreshold; + queueDepositMin = _queueDepositMin; + queueDepositMax = _queueDepositMax; accounts.push(address(0)); token.safeIncreaseAllowance(_stakingPool, type(uint256).max); } @@ -323,32 +327,35 @@ contract PriorityPool is UUPSUpgradeable, OwnableUpgradeable, PausableUpgradeabl /** * @notice deposits queued tokens into the staking pool - * @dev bypasses queueDepositThreshold + * @dev bypasses queueDepositMin */ function depositQueuedTokens() external { _depositQueuedTokens(0); } /** - * @notice returns whether a call should be made to performUpkeep to deposit queued tokens + * @notice returns whether a call should be made to performUpkeep to deposit queued/unused tokens * into the staking pool * @dev used by chainlink keepers */ function checkUpkeep(bytes calldata) external view returns (bool, bytes memory) { - uint256 canDeposit = stakingPool.canDeposit(); + uint256 strategyDepositRoom = stakingPool.getStrategyDepositRoom(); + uint256 unusedDeposits = stakingPool.getUnusedDeposits(); return ( - poolStatus == PoolStatus.OPEN && totalQueued >= queueDepositThreshold && canDeposit >= queueDepositThreshold, + poolStatus == PoolStatus.OPEN && + strategyDepositRoom >= queueDepositMin && + (totalQueued + unusedDeposits) >= queueDepositMin, bytes("") ); } /** * @notice deposits queued tokens into the staking pool - * @dev will revert if less than queueDepositThreshold tokens can be deposited + * @dev will revert if less than queueDepositMin tokens can be deposited * @dev used by chainlink keepers */ function performUpkeep(bytes calldata) external { - _depositQueuedTokens(queueDepositThreshold); + _depositQueuedTokens(queueDepositMin); } /** @@ -442,12 +449,14 @@ contract PriorityPool is UUPSUpgradeable, OwnableUpgradeable, PausableUpgradeabl } /** - * @notice sets the minimum amount of tokens needed to execute a deposit - * @param _queueDepositThreshold min amount of tokens + * @notice sets the minimum and maximum amount that can be deposited + * @param _queueDepositMin min amount of tokens + * @param _queueDepositMax max amount of tokens */ - function setQueueDepositThreshold(uint256 _queueDepositThreshold) external onlyOwner { - queueDepositThreshold = _queueDepositThreshold; - emit SetQueueDepositThreshold(_queueDepositThreshold); + function setQueueDepositThreshold(uint128 _queueDepositMin, uint128 _queueDepositMax) external onlyOwner { + queueDepositMin = _queueDepositMin; + queueDepositMax = _queueDepositMin; + emit SetQueueDepositParams(_queueDepositMin, _queueDepositMax); } /** @@ -524,25 +533,34 @@ contract PriorityPool is UUPSUpgradeable, OwnableUpgradeable, PausableUpgradeabl /** * @notice deposits queued tokens - * @param _depositThreshold min amount of tokens required for successful deposit + * @param _depositMin min amount of tokens required to deposit **/ - function _depositQueuedTokens(uint256 _depositThreshold) internal { + function _depositQueuedTokens(uint256 _depositMin) internal { if (poolStatus != PoolStatus.OPEN) revert DepositsDisabled(); - uint256 canDeposit = stakingPool.canDeposit(); - if (canDeposit == 0 || canDeposit < _depositThreshold) revert InsufficientDepositRoom(); + uint256 strategyDepositRoom = stakingPool.getStrategyDepositRoom(); + if (strategyDepositRoom == 0 || strategyDepositRoom < _depositMin) revert InsufficientDepositRoom(); uint256 _totalQueued = totalQueued; - if (_totalQueued == 0 || _totalQueued < _depositThreshold) revert InsufficientQueuedTokens(); + uint256 unusedDeposits = stakingPool.getUnusedDeposits(); + uint256 canDeposit = _totalQueued + unusedDeposits; + if (canDeposit == 0 || canDeposit < _depositMin) revert InsufficientQueuedTokens(); - uint256 toDeposit = _totalQueued <= canDeposit ? _totalQueued : canDeposit; + uint256 toDepositFromStakingPool = MathUpgradeable.min( + MathUpgradeable.min(unusedDeposits, strategyDepositRoom), + queueDepositMax + ); + uint256 toDepositFromQueue = MathUpgradeable.min( + MathUpgradeable.min(_totalQueued, strategyDepositRoom - toDepositFromStakingPool), + queueDepositMax - toDepositFromStakingPool + ); - totalQueued = _totalQueued - toDeposit; - depositsSinceLastUpdate += toDeposit; - sharesSinceLastUpdate += stakingPool.getSharesByStake(toDeposit); - stakingPool.deposit(address(this), toDeposit); + totalQueued = _totalQueued - toDepositFromQueue; + depositsSinceLastUpdate += toDepositFromQueue; + sharesSinceLastUpdate += stakingPool.getSharesByStake(toDepositFromQueue); + stakingPool.deposit(address(this), toDepositFromQueue); - emit DepositQueuedTokens(toDeposit); + emit DepositQueuedTokens(toDepositFromQueue); } /** diff --git a/contracts/core/StakingPool.sol b/contracts/core/StakingPool.sol index 9db76d2e..50a4182b 100644 --- a/contracts/core/StakingPool.sol +++ b/contracts/core/StakingPool.sol @@ -164,6 +164,31 @@ contract StakingPool is StakingRewardsPool { return min; } + /** + * @notice returns the amont of tokens sitting in the pool outside a strategy + * @dev these tokens earn no yield and should be deposited ASAP + * @return amount of tokens outside a strategy + */ + function getUnusedDeposits() external view returns (uint256) { + return token.balanceOf(address(this)); + } + + /** + * @notice returns the available deposit room for this pool's strategies + * @return strategy deposit room + */ + function getStrategyDepositRoom() external view returns (uint256) { + uint256 depositRoom; + for (uint256 i = 0; i < strategies.length; ++i) { + uint strategyDepositRoom = IStrategy(strategies[i]).canDeposit(); + if (strategyDepositRoom >= type(uint256).max - depositRoom) { + return type(uint256).max; + } + depositRoom += strategyDepositRoom; + } + return depositRoom; + } + /** * @notice returns the available deposit room for this pool * @return available deposit room @@ -205,13 +230,14 @@ contract StakingPool is StakingRewardsPool { /** * @notice removes a strategy * @param _index index of strategy + * @param _strategyUpdateData encoded data to be passed to strategy **/ - function removeStrategy(uint256 _index) external onlyOwner { + function removeStrategy(uint256 _index, bytes memory _strategyUpdateData) external onlyOwner { require(_index < strategies.length, "Strategy does not exist"); uint256[] memory idxs = new uint256[](1); idxs[0] = _index; - updateStrategyRewards(idxs); + updateStrategyRewards(idxs, _strategyUpdateData); IStrategy strategy = IStrategy(strategies[_index]); uint256 totalStrategyDeposits = strategy.getTotalDeposits(); @@ -286,7 +312,7 @@ contract StakingPool is StakingRewardsPool { * @return total rewards * @return total fees **/ - function getStrategyRewards(uint256[] memory _strategyIdxs) external view returns (int256, uint256) { + function getStrategyRewards(uint256[] calldata _strategyIdxs) external view returns (int256, uint256) { int256 totalRewards; uint256 totalFees; @@ -308,8 +334,9 @@ contract StakingPool is StakingRewardsPool { /** * @notice updates and distributes rewards based on balance changes in strategies * @param _strategyIdxs indexes of strategies to update rewards for + * @param _data encoded data to be passed to each strategy **/ - function updateStrategyRewards(uint256[] memory _strategyIdxs) public { + function updateStrategyRewards(uint256[] memory _strategyIdxs, bytes memory _data) public { int256 totalRewards; uint256 totalFeeAmounts; uint256 totalFeeCount; @@ -320,7 +347,7 @@ contract StakingPool is StakingRewardsPool { IStrategy strategy = IStrategy(strategies[_strategyIdxs[i]]); (int256 depositChange, address[] memory strategyReceivers, uint256[] memory strategyFeeAmounts) = strategy - .updateDeposits(); + .updateDeposits(_data); totalRewards += depositChange; if (strategyReceivers.length != 0) { diff --git a/contracts/core/interfaces/IStakingPool.sol b/contracts/core/interfaces/IStakingPool.sol index e69f18db..6188fc14 100644 --- a/contracts/core/interfaces/IStakingPool.sol +++ b/contracts/core/interfaces/IStakingPool.sol @@ -37,4 +37,8 @@ interface IStakingPool is IStakingRewardsPool { function poolIndex() external view returns (uint16); function canWithdraw() external view returns (uint256); + + function getStrategyDepositRoom() external view returns (uint256); + + function getUnusedDeposits() external view returns (uint256); }