fix(StakeManager): add checks for whether lockup period is in range (#39)

This commit introduces `MIN_LOCKUP_PERIOD` and `MAX_LOCKUP_PERIOD` and
makes use of them within `StakeManager.stake()` and
`StakeManager.lock()` accordingly.

When users deposit tokens into their vault via `stake()`, they can
provide an optional lockup time. If the value is `0` it implies users do
not want to lock their stake.

If the value is `> 0` it has to be within the range of
`MIN_LOCKUP_PERIOD` and `MAX_LOCKUP_PERIOD`.

Properly addresses #15
This commit is contained in:
r4bbit 2023-12-06 12:10:07 +01:00 committed by GitHub
parent f259286e98
commit d9a64559a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 66 additions and 19 deletions

View File

@ -1,24 +1,26 @@
CreateVaultTest:testDeployment() (gas: 9774) CreateVaultTest:testDeployment() (gas: 9774)
CreateVaultTest:test_createVault() (gas: 650992) CreateVaultTest:test_createVault() (gas: 650970)
ExecuteAccountTest:testDeployment() (gas: 26400) ExecuteAccountTest:testDeployment() (gas: 26421)
ExecuteAccountTest:test_RevertWhen_InvalidLimitEpoch() (gas: 991602) ExecuteAccountTest:test_RevertWhen_InvalidLimitEpoch() (gas: 992511)
LeaveTest:testDeployment() (gas: 26172) LeaveTest:testDeployment() (gas: 26193)
LeaveTest:test_RevertWhen_NoPendingMigration() (gas: 678051) LeaveTest:test_RevertWhen_NoPendingMigration() (gas: 678007)
LeaveTest:test_RevertWhen_SenderIsNotVault() (gas: 10562) LeaveTest:test_RevertWhen_SenderIsNotVault() (gas: 10540)
LockTest:testDeployment() (gas: 26400) LockTest:testDeployment() (gas: 26421)
LockTest:test_RevertWhen_DecreasingLockTime() (gas: 994528) LockTest:test_RevertWhen_DecreasingLockTime() (gas: 995651)
LockTest:test_RevertWhen_SenderIsNotVault() (gas: 10607) LockTest:test_RevertWhen_InvalidLockupPeriod() (gas: 815891)
MigrateTest:testDeployment() (gas: 26172) LockTest:test_RevertWhen_SenderIsNotVault() (gas: 10652)
MigrateTest:test_RevertWhen_NoPendingMigration() (gas: 677890) MigrateTest:testDeployment() (gas: 26193)
MigrateTest:test_RevertWhen_NoPendingMigration() (gas: 677868)
MigrateTest:test_RevertWhen_SenderIsNotVault() (gas: 10629) MigrateTest:test_RevertWhen_SenderIsNotVault() (gas: 10629)
SetStakeManagerTest:testDeployment() (gas: 9774) SetStakeManagerTest:testDeployment() (gas: 9774)
SetStakeManagerTest:test_RevertWhen_InvalidStakeManagerAddress() (gas: 20481) SetStakeManagerTest:test_RevertWhen_InvalidStakeManagerAddress() (gas: 20481)
SetStakeManagerTest:test_SetStakeManager() (gas: 19869) SetStakeManagerTest:test_SetStakeManager() (gas: 19869)
StakeManagerTest:testDeployment() (gas: 26172) StakeManagerTest:testDeployment() (gas: 26193)
StakeTest:testDeployment() (gas: 26172) StakeTest:testDeployment() (gas: 26421)
StakeTest:test_RevertWhen_SenderIsNotVault() (gas: 10638) StakeTest:test_RevertWhen_InvalidLockupPeriod() (gas: 827181)
StakeTest:test_RevertWhen_SenderIsNotVault() (gas: 10672)
StakedTokenTest:testStakeToken() (gas: 7638) StakedTokenTest:testStakeToken() (gas: 7638)
UnstakeTest:testDeployment() (gas: 26355) UnstakeTest:testDeployment() (gas: 26376)
UnstakeTest:test_RevertWhen_FundsLocked() (gas: 990991) UnstakeTest:test_RevertWhen_FundsLocked() (gas: 991901)
UnstakeTest:test_RevertWhen_SenderIsNotVault() (gas: 10609) UnstakeTest:test_RevertWhen_SenderIsNotVault() (gas: 10609)
VaultFactoryTest:testDeployment() (gas: 9774) VaultFactoryTest:testDeployment() (gas: 9774)

View File

@ -14,6 +14,7 @@ contract StakeManager is Ownable {
error StakeManager__PendingMigration(); error StakeManager__PendingMigration();
error StakeManager__SenderIsNotPreviousStakeManager(); error StakeManager__SenderIsNotPreviousStakeManager();
error StakeManager__InvalidLimitEpoch(); error StakeManager__InvalidLimitEpoch();
error StakeManager__InvalidLockupPeriod();
struct Account { struct Account {
uint256 lockUntil; uint256 lockUntil;
@ -32,6 +33,8 @@ contract StakeManager is Ownable {
uint256 public constant EPOCH_SIZE = 1 weeks; uint256 public constant EPOCH_SIZE = 1 weeks;
uint256 public constant YEAR = 365 days; uint256 public constant YEAR = 365 days;
uint256 public constant MIN_LOCKUP_PERIOD = 12 weeks; // 3 months
uint256 public constant MAX_LOCKUP_PERIOD = 4 * YEAR; // 4 years
uint256 public constant MP_APY = 1; uint256 public constant MP_APY = 1;
uint256 public constant MAX_BOOST = 4; uint256 public constant MAX_BOOST = 4;
@ -64,8 +67,13 @@ contract StakeManager is Ownable {
* Increases balance of msg.sender; * Increases balance of msg.sender;
* @param _amount Amount of balance to be decreased. * @param _amount Amount of balance to be decreased.
* @param _time Seconds from block.timestamp to lock balance. * @param _time Seconds from block.timestamp to lock balance.
*
* @dev Reverts when `_time` is not in range of [MIN_LOCKUP_PERIOD, MAX_LOCKUP_PERIOD]
*/ */
function stake(uint256 _amount, uint256 _time) external onlyVault { function stake(uint256 _amount, uint256 _time) external onlyVault {
if (_time > 0 && (_time < MIN_LOCKUP_PERIOD || _time > MAX_LOCKUP_PERIOD)) {
revert StakeManager__InvalidLockupPeriod();
}
Account storage account = accounts[msg.sender]; Account storage account = accounts[msg.sender];
processAccount(account, currentEpoch); processAccount(account, currentEpoch);
account.balance += _amount; account.balance += _amount;
@ -94,8 +102,14 @@ contract StakeManager is Ownable {
/** /**
* @notice Locks entire balance for more amount of time. * @notice Locks entire balance for more amount of time.
* @param _time amount of time to lock from now. * @param _time amount of time to lock from now.
*
* @dev Reverts when `_time` is bigger than `MAX_LOCKUP_PERIOD`
* @dev Reverts when `_time + block.timestamp` is smaller than current lock time.
*/ */
function lock(uint256 _time) external onlyVault { function lock(uint256 _time) external onlyVault {
if (_time > MAX_LOCKUP_PERIOD) {
revert StakeManager__InvalidLockupPeriod();
}
Account storage account = accounts[msg.sender]; Account storage account = accounts[msg.sender];
processAccount(account, currentEpoch); processAccount(account, currentEpoch);
if (block.timestamp + _time < account.lockUntil) { if (block.timestamp + _time < account.lockUntil) {

View File

@ -54,6 +54,23 @@ contract StakeTest is StakeManagerTest {
vm.expectRevert(StakeManager.StakeManager__SenderIsNotVault.selector); vm.expectRevert(StakeManager.StakeManager__SenderIsNotVault.selector);
stakeManager.stake(100, 1); stakeManager.stake(100, 1);
} }
function test_RevertWhen_InvalidLockupPeriod() public {
// ensure user has funds
deal(stakeToken, testUser, 1000);
StakeVault userVault = _createTestVault(testUser);
vm.startPrank(testUser);
ERC20(stakeToken).approve(address(userVault), 100);
uint256 lockTime = stakeManager.MIN_LOCKUP_PERIOD() - 1;
vm.expectRevert(StakeManager.StakeManager__InvalidLockupPeriod.selector);
userVault.stake(100, lockTime);
lockTime = stakeManager.MAX_LOCKUP_PERIOD() + 1;
vm.expectRevert(StakeManager.StakeManager__InvalidLockupPeriod.selector);
userVault.stake(100, lockTime);
}
} }
contract UnstakeTest is StakeManagerTest { contract UnstakeTest is StakeManagerTest {
@ -74,7 +91,7 @@ contract UnstakeTest is StakeManagerTest {
vm.startPrank(testUser); vm.startPrank(testUser);
ERC20(stakeToken).approve(address(userVault), 100); ERC20(stakeToken).approve(address(userVault), 100);
uint256 lockTime = 1 days; uint256 lockTime = stakeManager.MIN_LOCKUP_PERIOD();
userVault.stake(100, lockTime); userVault.stake(100, lockTime);
vm.expectRevert(StakeManager.StakeManager__FundsLocked.selector); vm.expectRevert(StakeManager.StakeManager__FundsLocked.selector);
@ -92,6 +109,20 @@ contract LockTest is StakeManagerTest {
stakeManager.lock(100); stakeManager.lock(100);
} }
function test_RevertWhen_InvalidLockupPeriod() public {
// ensure user has funds
deal(stakeToken, testUser, 1000);
StakeVault userVault = _createTestVault(testUser);
vm.startPrank(testUser);
// ensure user vault can spend user tokens
ERC20(stakeToken).approve(address(userVault), 100);
uint256 lockTime = stakeManager.MAX_LOCKUP_PERIOD() + 1;
vm.expectRevert(StakeManager.StakeManager__InvalidLockupPeriod.selector);
userVault.stake(100, lockTime);
}
function test_RevertWhen_DecreasingLockTime() public { function test_RevertWhen_DecreasingLockTime() public {
// ensure user has funds // ensure user has funds
deal(stakeToken, testUser, 1000); deal(stakeToken, testUser, 1000);
@ -101,7 +132,7 @@ contract LockTest is StakeManagerTest {
// ensure user vault can spend user tokens // ensure user vault can spend user tokens
ERC20(stakeToken).approve(address(userVault), 100); ERC20(stakeToken).approve(address(userVault), 100);
uint256 lockTime = 1 days; uint256 lockTime = stakeManager.MIN_LOCKUP_PERIOD() + 1;
userVault.stake(100, lockTime); userVault.stake(100, lockTime);
vm.expectRevert(StakeManager.StakeManager__DecreasingLockTime.selector); vm.expectRevert(StakeManager.StakeManager__DecreasingLockTime.selector);
@ -157,7 +188,7 @@ contract ExecuteAccountTest is StakeManagerTest {
vm.startPrank(testUser); vm.startPrank(testUser);
ERC20(stakeToken).approve(address(userVault), 100); ERC20(stakeToken).approve(address(userVault), 100);
uint256 lockTime = 1 days; uint256 lockTime = stakeManager.MIN_LOCKUP_PERIOD();
userVault.stake(100, lockTime); userVault.stake(100, lockTime);
uint256 currentEpoch = stakeManager.currentEpoch(); uint256 currentEpoch = stakeManager.currentEpoch();