Skip to content

Commit

Permalink
added withdrawal execution logic to withdrawal pool
Browse files Browse the repository at this point in the history
  • Loading branch information
BkChoy committed Jun 11, 2024
1 parent 24ce280 commit 07208bf
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 18 deletions.
4 changes: 4 additions & 0 deletions contracts/core/interfaces/IPriorityPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ interface IPriorityPool {

function poolStatus() external view returns (PoolStatus);

function canWithdraw(address _account, uint256 _distributionAmount) external view returns (uint256);

function pauseForUpdate() external;

function setPoolStatus(PoolStatus _status) external;
Expand All @@ -24,4 +26,6 @@ interface IPriorityPool {
uint256 _amountDistributed,
uint256 _sharesAmountDistributed
) external;

function executeQueuedWithdrawals(uint256 _amount, bytes[] calldata _data) external;
}
22 changes: 22 additions & 0 deletions contracts/core/priorityPool/PriorityPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,14 @@ contract PriorityPool is UUPSUpgradeable, OwnableUpgradeable, PausableUpgradeabl
_;
}

/**
* @notice reverts if sender is not withdrawal pool
**/
modifier onlyWithdrawalPool() {
if (msg.sender != address(withdrawalPool)) revert SenderNotAuthorized();
_;
}

/**
* @notice returns a list of all accounts in the order that they appear in the merkle tree
* @return list of accounts
Expand Down Expand Up @@ -462,6 +470,12 @@ contract PriorityPool is UUPSUpgradeable, OwnableUpgradeable, PausableUpgradeabl
_pause();
}

function executeQueuedWithdrawals(uint256 _amount, bytes[] calldata _data) external onlyWithdrawalPool {
IERC20Upgradeable(address(stakingPool)).safeTransferFrom(msg.sender, address(this), _amount);
stakingPool.withdraw(address(this), address(this), _amount, _data);
token.safeTransfer(msg.sender, _amount);
}

/**
* @notice sets the pool's status
* @param _status pool status
Expand Down Expand Up @@ -515,6 +529,14 @@ contract PriorityPool is UUPSUpgradeable, OwnableUpgradeable, PausableUpgradeabl
* @param _withdrawalPool address of withdrawal pool
*/
function setWithdrawalPool(address _withdrawalPool) external onlyOwner {
if (address(withdrawalPool) != address(0)) {
IERC20Upgradeable(address(stakingPool)).safeApprove(address(withdrawalPool), 0);
token.safeApprove(address(withdrawalPool), 0);
}

IERC20Upgradeable(address(stakingPool)).safeApprove(_withdrawalPool, type(uint256).max);
token.safeApprove(_withdrawalPool, type(uint256).max);

withdrawalPool = IWithdrawalPool(_withdrawalPool);
}

Expand Down
65 changes: 51 additions & 14 deletions contracts/core/priorityPool/WithdrawalPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";

import "../interfaces/IStakingPool.sol";
import "../interfaces/IPriorityPool.sol";

/**
* @title Withdrawal Pool
Expand All @@ -27,7 +28,7 @@ contract WithdrawalPool is UUPSUpgradeable, OwnableUpgradeable {

IERC20Upgradeable public token;
IERC20Upgradeable public lst;
address public priorityPool;
IPriorityPool public priorityPool;

Withdrawal[] internal queuedWithdrawals;
mapping(address => uint256[]) internal queuedWithdrawalsByAccount;
Expand All @@ -42,12 +43,13 @@ contract WithdrawalPool is UUPSUpgradeable, OwnableUpgradeable {

event QueueWithdrawal(address indexed account, uint256 amount);
event Withdraw(address indexed account, uint256 amount);
event Deposit(uint256 amount);
event WithdrawalsFinalized(uint256 amount);
event SetMinWithdrawalAmount(uint256 minWithdrawalAmount);

error SenderNotAuthorized();
error InvalidWithdrawalId();
error AmountTooSmall();
error NoUpkeepNeeded();

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
Expand All @@ -71,14 +73,15 @@ contract WithdrawalPool is UUPSUpgradeable, OwnableUpgradeable {
__Ownable_init();
token = IERC20Upgradeable(_token);
lst = IERC20Upgradeable(_lst);
priorityPool = _priorityPool;
lst.safeApprove(_priorityPool, type(uint256).max);
priorityPool = IPriorityPool(_priorityPool);
minWithdrawalAmount = _minWithdrawalAmount;
withdrawalBatches.push(WithdrawalBatch(0, 0));
queuedWithdrawals.push(Withdrawal(0, 0));
}

modifier onlyPriorityPool() {
if (msg.sender != priorityPool) revert SenderNotAuthorized();
if (msg.sender != address(priorityPool)) revert SenderNotAuthorized();
_;
}

Expand Down Expand Up @@ -274,7 +277,50 @@ contract WithdrawalPool is UUPSUpgradeable, OwnableUpgradeable {
function deposit(uint256 _amount) external onlyPriorityPool {
token.safeTransferFrom(msg.sender, address(this), _amount);
lst.safeTransfer(msg.sender, _amount);
_finalizeWithdrawals(_amount);
}

/**
* @notice Returns whether withdrawals should be executed based on available withdrawal space
* @return true if withdrawal should be executed, false otherwise
*/
function checkUpkeep(bytes calldata) external view returns (bool, bytes memory) {
if (_getStakeByShares(totalQueuedShareWithdrawals) != 0 && priorityPool.canWithdraw(address(this), 0) != 0) {
return (true, "");
}
return (false, "");
}

/**
* @notice Executes withdrawals if there is sufficient available withdrawal space
* @param _performData encoded withdrawal data to be passed to strategies
*/
function performUpkeep(bytes calldata _performData) external {
uint256 canWithdraw = priorityPool.canWithdraw(address(this), 0);
uint256 totalQueued = _getStakeByShares(totalQueuedShareWithdrawals);
if (totalQueued == 0 || canWithdraw == 0) revert NoUpkeepNeeded();

uint256 toWithdraw = totalQueued > canWithdraw ? canWithdraw : totalQueued;
bytes[] memory data = abi.decode(_performData, (bytes[]));

priorityPool.executeQueuedWithdrawals(toWithdraw, data);
_finalizeWithdrawals(toWithdraw);
}

/**
* @notice Sets the minimum amount of lst tokens that can be queued for withdrawal
* @param _minWithdrawalAmount min token amount
*/
function setMinWithdrawalAmount(uint256 _minWithdrawalAmount) external onlyOwner {
minWithdrawalAmount = _minWithdrawalAmount;
emit SetMinWithdrawalAmount(_minWithdrawalAmount);
}

/**
* @notice Finalizes withdrawal accounting after withdrawals have been executed
* @param _amount amount to finalize
*/
function _finalizeWithdrawals(uint256 _amount) internal {
uint256 sharesToWithdraw = _getSharesByStake(_amount);
uint256 numWithdrawals = queuedWithdrawals.length;

Expand Down Expand Up @@ -306,16 +352,7 @@ contract WithdrawalPool is UUPSUpgradeable, OwnableUpgradeable {

assert(sharesToWithdraw == 0);

emit Deposit(_amount);
}

/**
* @notice Sets the minimum amount of lst tokens that can be queued for withdrawal
* @param _minWithdrawalAmount min token amount
*/
function setMinWithdrawalAmount(uint256 _minWithdrawalAmount) external onlyOwner {
minWithdrawalAmount = _minWithdrawalAmount;
emit SetMinWithdrawalAmount(_minWithdrawalAmount);
emit WithdrawalsFinalized(_amount);
}

/**
Expand Down
50 changes: 46 additions & 4 deletions test/core/priorityPool/withdrawal-pool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import {
getAccounts,
setupToken,
} from '../../utils/helpers'
import { ERC677, StakingPool, StrategyMock } from '../../../typechain-types'
import { ERC677, PriorityPool, StakingPool, StrategyMock } from '../../../typechain-types'
import { ethers } from 'hardhat'
import { Signer } from 'ethers'
import { WithdrawalPool } from '../../../typechain-types/WithdrawalPool'

describe('WithdrawalPool', () => {
let withdrawalPool: WithdrawalPool
let stakingPool: StakingPool
let strategy: StrategyMock
let token: ERC677
let accounts: string[]
let signers: Signer[]
Expand All @@ -38,11 +39,11 @@ describe('WithdrawalPool', () => {
[],
])) as StakingPool

let strategy = (await deployUpgradeable('StrategyMock', [
strategy = (await deployUpgradeable('StrategyMock', [
token.address,
stakingPool.address,
toEther(1000000000),
toEther(0),
toEther(5000),
])) as StrategyMock

withdrawalPool = (await deployUpgradeable('WithdrawalPool', [
Expand All @@ -59,7 +60,7 @@ describe('WithdrawalPool', () => {
await token.approve(withdrawalPool.address, ethers.constants.MaxUint256)
await stakingPool.approve(withdrawalPool.address, ethers.constants.MaxUint256)

await stakingPool.deposit(accounts[0], toEther(100000))
await stakingPool.deposit(accounts[0], toEther(100000), ['0x'])
await token.transfer(strategy.address, toEther(100000))
await stakingPool.updateStrategyRewards([0], '0x')
})
Expand Down Expand Up @@ -351,4 +352,45 @@ describe('WithdrawalPool', () => {
)
assert.equal(fromEther(data[1]), 250)
})

it('checkUpkeep and performUpkeep should work correctly', async () => {
let priorityPool = (await deployUpgradeable('PriorityPool', [
token.address,
stakingPool.address,
accounts[0],
0,
0,
0,
])) as PriorityPool
withdrawalPool = (await deployUpgradeable('WithdrawalPool', [
stakingPool.address,
stakingPool.address,
priorityPool.address,
toEther(10),
])) as WithdrawalPool
await stakingPool.approve(priorityPool.address, ethers.constants.MaxUint256)
await stakingPool.setPriorityPool(priorityPool.address)
await priorityPool.setWithdrawalPool(withdrawalPool.address)

await priorityPool.withdraw(toEther(199000), 0, 0, [], false, true, ['0x'])
assert.deepEqual(await withdrawalPool.checkUpkeep('0x'), [false, '0x'])
await expect(withdrawalPool.performUpkeep('0x')).to.be.revertedWith('NoUpkeepNeeded()')

await strategy.setMinDeposits(toEther(4000))
assert.deepEqual(await withdrawalPool.checkUpkeep('0x'), [true, '0x'])
await withdrawalPool.performUpkeep(ethers.utils.defaultAbiCoder.encode(['bytes[]'], [['0x']]))
assert.equal(fromEther(await token.balanceOf(withdrawalPool.address)), 1000)
assert.equal(fromEther(await stakingPool.balanceOf(withdrawalPool.address)), 3000)
assert.equal(fromEther(await withdrawalPool.getTotalQueuedWithdrawals()), 3000)

await strategy.setMinDeposits(toEther(0))
assert.deepEqual(await withdrawalPool.checkUpkeep('0x'), [true, '0x'])
await withdrawalPool.performUpkeep(ethers.utils.defaultAbiCoder.encode(['bytes[]'], [['0x']]))
assert.equal(fromEther(await token.balanceOf(withdrawalPool.address)), 4000)
assert.equal(fromEther(await stakingPool.balanceOf(withdrawalPool.address)), 0)
assert.equal(fromEther(await withdrawalPool.getTotalQueuedWithdrawals()), 0)

assert.deepEqual(await withdrawalPool.checkUpkeep('0x'), [false, '0x'])
await expect(withdrawalPool.performUpkeep('0x')).to.be.revertedWith('NoUpkeepNeeded()')
})
})

0 comments on commit 07208bf

Please sign in to comment.