Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
0x-r4bbit committed Mar 20, 2024
1 parent 70b092a commit 8c5dd44
Show file tree
Hide file tree
Showing 4 changed files with 305 additions and 39 deletions.
1 change: 1 addition & 0 deletions contracts/StakeManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ contract StakeManager is Ownable {
uint256 public constant MAX_LOCKUP_PERIOD = 4 * YEAR; // 4 years
uint256 public constant MP_APY = 1;
uint256 public constant MAX_BOOST = 4;
uint256 public constant DEPOSIT_COOLDOWN_PERIOD = MIN_LOCKUP_PERIOD;

mapping(address index => Account value) public accounts;
mapping(uint256 index => Epoch value) public epochs;
Expand Down
96 changes: 85 additions & 11 deletions contracts/StakeVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,81 @@ import { StakeManager } from "./StakeManager.sol";
* @notice Secures user stake
*/
contract StakeVault is Ownable {

error StakeVault__MigrationNotAvailable();

error StakeVault__StakingFailed();
error StakeVault__DepositCoolingDown();

error StakeVault__DepositFailed();

error StakeVault__WithdrawFailed();

error StakeVault__UnstakingFailed();
error StakeVault__InsufficientFunds();

error StakeVault__InvalidLockTime();

StakeManager private stakeManager;

ERC20 public immutable STAKED_TOKEN;

uint256 public balance;

uint256 public depositCooldownUntil;

event Deposit(uint256 indexed amount);

event Withdraw(uint256 indexed amount);

event Staked(address from, address to, uint256 _amount, uint256 time);

modifier whenNotDepositCoolingDown() {
if (block.timestamp <= depositCooldownUntil) {
revert StakeVault__DepositCoolingDown();
}
_;
}

modifier onlySufficientBalance(uint256 _amount) {
uint256 availableFunds = _unstakedBalance();
if (_amount > availableFunds) {
revert StakeVault__InsufficientFunds();
}
_;
}

constructor(address _owner, ERC20 _stakedToken, StakeManager _stakeManager) {
_transferOwnership(_owner);
STAKED_TOKEN = _stakedToken;
stakeManager = _stakeManager;
}

function stake(uint256 _amount, uint256 _time) external onlyOwner {
bool success = STAKED_TOKEN.transferFrom(msg.sender, address(this), _amount);
function deposit(uint256 _amount) external onlyOwner whenNotDepositCoolingDown {
depositCooldownUntil = block.timestamp + stakeManager.DEPOSIT_COOLDOWN_PERIOD();
_deposit(msg.sender, _amount);
}

function withdraw(uint256 _amount) public onlyOwner onlySufficientBalance(_amount) {
balance -= _amount;
bool success = STAKED_TOKEN.transfer(msg.sender, _amount);
if (!success) {
revert StakeVault__StakingFailed();
revert StakeVault__WithdrawFailed();
}
stakeManager.stake(_amount, _time);
emit Withdraw(_amount);
}

emit Staked(msg.sender, address(this), _amount, _time);
function stake(uint256 _amount) public onlyOwner whenNotDepositCoolingDown onlySufficientBalance(_amount) {
_stake(_amount, 0);
}

function depositAndStake(uint256 _amount, uint256 _time) external onlyOwner whenNotDepositCoolingDown {
uint256 stakedBalance = _stakedBalance();
if (stakedBalance == 0 && _time == 0) {
// we expect `depositAndStake` to be called either with a lock time,
// or when there's already funds staked (because it's possible to top up stake without locking)
revert StakeVault__InvalidLockTime();
}
_deposit(msg.sender, _amount);
_stake(_amount, _time);
}

function lock(uint256 _time) external onlyOwner {
Expand All @@ -46,10 +95,11 @@ contract StakeVault is Ownable {

function unstake(uint256 _amount) external onlyOwner {
stakeManager.unstake(_amount);
bool success = STAKED_TOKEN.transfer(msg.sender, _amount);
if (!success) {
revert StakeVault__UnstakingFailed();
}
}

function unstakeAndWithdraw(uint256 _amount) external onlyOwner {
stakeManager.unstake(_amount);
withdraw(_amount);
}

function leave() external onlyOwner {
Expand All @@ -69,4 +119,28 @@ contract StakeVault is Ownable {
function stakedToken() external view returns (ERC20) {
return STAKED_TOKEN;
}

function _deposit(address _from, uint256 _amount) internal {
balance += _amount;
bool success = STAKED_TOKEN.transferFrom(_from, address(this), _amount);
if (!success) {
revert StakeVault__DepositFailed();
}
emit Deposit( _amount);
}

function _stake(uint256 _amount, uint256 _time) internal {
stakeManager.stake(_amount, _time);
emit Staked(msg.sender, address(this), _amount, _time);
}

function _unstakedBalance() internal view returns (uint256) {
(, uint256 stakedBalance, ,,,,) = stakeManager.accounts(address(this));
return balance - stakedBalance;
}

function _stakedBalance() internal view returns (uint256) {
(, uint256 stakedBalance, ,,,,) = stakeManager.accounts(address(this));
return stakedBalance;
}
}
41 changes: 24 additions & 17 deletions test/StakeManager.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,14 @@ contract StakeManagerTest is Test {
userVault = _createTestVault(owner);
vm.startPrank(owner);
ERC20(stakeToken).approve(address(userVault), mintAmount);
userVault.stake(amount, lockTime);

if (lockTime > 0) {
userVault.depositAndStake(amount, lockTime);
} else {
userVault.deposit(amount);
vm.warp(userVault.depositCooldownUntil() + 1);
userVault.stake(amount);
}
vm.stopPrank();
}
}
Expand All @@ -97,11 +104,11 @@ contract StakeTest is StakeManagerTest {

uint256 lockTime = stakeManager.MIN_LOCKUP_PERIOD() - 1;
vm.expectRevert(StakeManager.StakeManager__InvalidLockTime.selector);
userVault.stake(100, lockTime);
userVault.depositAndStake(100, lockTime);

lockTime = stakeManager.MAX_LOCKUP_PERIOD() + 1;
vm.expectRevert(StakeManager.StakeManager__InvalidLockTime.selector);
userVault.stake(100, lockTime);
userVault.depositAndStake(100, lockTime);
}

function test_StakeWithoutLockUpTimeMintsMultiplierPoints() public {
Expand Down Expand Up @@ -129,7 +136,7 @@ contract StakeTest is StakeManagerTest {
StakeVault userVault = _createStakingAccount(testUser, stakeAmount, lockToIncrease, mintAmount);

vm.prank(testUser);
userVault.stake(stakeAmount2, 0);
userVault.depositAndStake(stakeAmount2, 0);

(, uint256 balance,, uint256 currentMP,,,) = stakeManager.accounts(address(userVault));
assertEq(balance, stakeAmount + stakeAmount2, "account balance");
Expand All @@ -138,7 +145,7 @@ contract StakeTest is StakeManagerTest {
vm.warp(stakeManager.epochEnd());

vm.prank(testUser);
userVault.stake(stakeAmount3, 0);
userVault.depositAndStake(stakeAmount3, 0);

(, balance,, currentMP,,,) = stakeManager.accounts(address(userVault));
assertEq(balance, stakeAmount + stakeAmount2 + stakeAmount3, "account balance 2");
Expand All @@ -154,9 +161,9 @@ contract StakeTest is StakeManagerTest {
_createStakingAccount(testUser2, stakeAmount, stakeManager.MIN_LOCKUP_PERIOD(), mintAmount);

vm.prank(testUser);
userVault.stake(stakeAmount2, 0);
userVault.depositAndStake(stakeAmount2, 0);
vm.prank(testUser2);
userVault2.stake(stakeAmount2, 0);
userVault2.depositAndStake(stakeAmount2, 0);

(, uint256 balance,, uint256 currentMP,,,) = stakeManager.accounts(address(userVault));
assertEq(balance, stakeAmount + stakeAmount2, "account balance");
Expand All @@ -168,9 +175,9 @@ contract StakeTest is StakeManagerTest {
vm.warp(stakeManager.epochEnd());

vm.prank(testUser);
userVault.stake(stakeAmount2, 0);
userVault.depositAndStake(stakeAmount2, 0);
vm.prank(testUser2);
userVault2.stake(stakeAmount2, 0);
userVault2.depositAndStake(stakeAmount2, 0);

(, balance,, currentMP,,,) = stakeManager.accounts(address(userVault));
assertEq(balance, stakeAmount + stakeAmount2 + stakeAmount2, "account balance 2");
Expand All @@ -188,9 +195,9 @@ contract StakeTest is StakeManagerTest {
StakeVault userVault = _createStakingAccount(testUser, stakeAmount, 0, mintAmount);
StakeVault userVault2 = _createStakingAccount(testUser2, stakeAmount, lockToIncrease, mintAmount);
vm.prank(testUser);
userVault.stake(0, lockToIncrease);
userVault.depositAndStake(0, lockToIncrease);
vm.prank(testUser2);
userVault2.stake(0, lockToIncrease);
userVault2.depositAndStake(0, lockToIncrease);

(, uint256 balance,, uint256 currentMP,,,) = stakeManager.accounts(address(userVault));
assertEq(balance, stakeAmount, "account balance");
Expand All @@ -202,9 +209,9 @@ contract StakeTest is StakeManagerTest {
vm.warp(stakeManager.epochEnd());

vm.prank(testUser);
userVault.stake(0, lockToIncrease);
userVault.depositAndStake(0, lockToIncrease);
vm.prank(testUser2);
userVault2.stake(0, lockToIncrease);
userVault2.depositAndStake(0, lockToIncrease);

(, balance,, currentMP,,,) = stakeManager.accounts(address(userVault));
assertEq(balance, stakeAmount, "account balance 2");
Expand All @@ -223,9 +230,9 @@ contract StakeTest is StakeManagerTest {
StakeVault userVault2 = _createStakingAccount(testUser2, stakeAmount, lockToIncrease, mintAmount);

vm.prank(testUser);
userVault.stake(stakeAmount2, lockToIncrease);
userVault.depositAndStake(stakeAmount2, lockToIncrease);
vm.prank(testUser2);
userVault2.stake(stakeAmount2, lockToIncrease);
userVault2.depositAndStake(stakeAmount2, lockToIncrease);

(, uint256 balance,, uint256 currentMP,,,) = stakeManager.accounts(address(userVault));
assertEq(balance, stakeAmount + stakeAmount2, "account balance");
Expand All @@ -237,9 +244,9 @@ contract StakeTest is StakeManagerTest {
vm.warp(stakeManager.epochEnd());

vm.prank(testUser);
userVault.stake(stakeAmount2, lockToIncrease);
userVault.depositAndStake(stakeAmount2, lockToIncrease);
vm.prank(testUser2);
userVault2.stake(stakeAmount2, lockToIncrease);
userVault2.depositAndStake(stakeAmount2, lockToIncrease);

(, balance,, currentMP,,,) = stakeManager.accounts(address(userVault));
assertEq(balance, stakeAmount + stakeAmount2 + stakeAmount2, "account balance 2");
Expand Down

0 comments on commit 8c5dd44

Please sign in to comment.