Skip to content

Commit

Permalink
chore: introduce IStakeManager interface
Browse files Browse the repository at this point in the history
This introduces a first version of `IStakeManager`, highly inspired by
the changes done in
#39.

However, in this commit, it only adds the methods that are currently
supported by both, `StakeManager` and `RewardStreamerMP`.

Future commits will add APIs for locking and leaving.
  • Loading branch information
0x-r4bbit committed Oct 17, 2024
1 parent 010b336 commit d4b25c8
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 23 deletions.
15 changes: 8 additions & 7 deletions src/RewardsStreamerMP.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,25 @@ pragma solidity ^0.8.26;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { IStakeManager } from "./interfaces/IStakeManager.sol";

// Rewards Streamer with Multiplier Points
contract RewardsStreamerMP is ReentrancyGuard {
contract RewardsStreamerMP is ReentrancyGuard, IStakeManager {
error StakingManager__AmountCannotBeZero();
error StakingManager__TransferFailed();
error StakingManager__InsufficientBalance();
error StakingManager__InvalidLockingPeriod();
error StakingManager__CannotRestakeWithLockedFunds();
error StakingManager__TokensAreLocked();

IERC20 public immutable STAKING_TOKEN;
IERC20 public immutable STAKE_TOKEN;
IERC20 public immutable REWARD_TOKEN;

uint256 public constant SCALE_FACTOR = 1e18;
uint256 public constant MP_RATE_PER_YEAR = 1e18;

uint256 public constant MIN_LOCKING_PERIOD = 90 days;
uint256 public constant MAX_LOCKING_PERIOD = 4 * 365 days;
uint256 public constant MIN_LOCKUP_PERIOD = 90 days;
uint256 public constant MAX_LOCKUP_PERIOD = 4 * 365 days;
uint256 public constant MAX_MULTIPLIER = 4;

uint256 public totalStaked;
Expand All @@ -42,7 +43,7 @@ contract RewardsStreamerMP is ReentrancyGuard {
mapping(address account => Account data) public accounts;

constructor(address _stakingToken, address _rewardToken) {
STAKING_TOKEN = IERC20(_stakingToken);
STAKE_TOKEN = IERC20(_stakingToken);
REWARD_TOKEN = IERC20(_rewardToken);
lastMPUpdatedTime = block.timestamp;
}
Expand All @@ -52,7 +53,7 @@ contract RewardsStreamerMP is ReentrancyGuard {
revert StakingManager__AmountCannotBeZero();
}

if (lockPeriod != 0 && (lockPeriod < MIN_LOCKING_PERIOD || lockPeriod > MAX_LOCKING_PERIOD)) {
if (lockPeriod != 0 && (lockPeriod < MIN_LOCKUP_PERIOD || lockPeriod > MAX_LOCKUP_PERIOD)) {
revert StakingManager__InvalidLockingPeriod();
}

Expand All @@ -77,7 +78,7 @@ contract RewardsStreamerMP is ReentrancyGuard {
uint256 bonusMP = 0;

if (lockPeriod != 0) {
uint256 lockMultiplier = (lockPeriod * MAX_MULTIPLIER * SCALE_FACTOR) / MAX_LOCKING_PERIOD;
uint256 lockMultiplier = (lockPeriod * MAX_MULTIPLIER * SCALE_FACTOR) / MAX_LOCKUP_PERIOD;
bonusMP = amount * lockMultiplier / SCALE_FACTOR;
account.lockUntil = block.timestamp + lockPeriod;
} else {
Expand Down
8 changes: 4 additions & 4 deletions src/StakeVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.26;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { RewardsStreamerMP } from "./RewardsStreamerMP.sol";
import { IStakeManager } from "./interfaces/IStakeManager.sol";

/**
* @title StakeVault
Expand All @@ -23,7 +23,7 @@ contract StakeVault is Ownable {
//if is needed that STAKE_TOKEN to be a variable, RewardStreamerMP should be changed to check codehash and
//StakeVault(msg.sender).STAKE_TOKEN()
IERC20 public immutable STAKE_TOKEN;
RewardsStreamerMP private stakeManager;
IStakeManager private stakeManager;

/**
* @dev Emitted when tokens are staked.
Expand All @@ -46,8 +46,8 @@ contract StakeVault is Ownable {
* @param _owner The address of the owner.
* @param _stakeManager The address of the RewardStreamerMP contract.
*/
constructor(address _owner, RewardsStreamerMP _stakeManager) Ownable(_owner) {
STAKE_TOKEN = _stakeManager.STAKING_TOKEN();
constructor(address _owner, IStakeManager _stakeManager) Ownable(_owner) {
STAKE_TOKEN = _stakeManager.STAKE_TOKEN();
stakeManager = _stakeManager;
}

Expand Down
26 changes: 26 additions & 0 deletions src/interfaces/IStakeManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IStakeManager {
error StakeManager__FundsLocked();
error StakeManager__InvalidLockTime();
error StakeManager__InsufficientFunds();
error StakeManager__StakeIsTooLow();

function stake(uint256 _amount, uint256 _seconds) external;
function unstake(uint256 _amount) external;

function totalStaked() external view returns (uint256);
function totalMP() external view returns (uint256);
function totalMaxMP() external view returns (uint256);
function getStakedBalance(address _vault) external view returns (uint256 _balance);

function STAKE_TOKEN() external view returns (IERC20);
function REWARD_TOKEN() external view returns (IERC20);
function MIN_LOCKUP_PERIOD() external view returns (uint256);
function MAX_LOCKUP_PERIOD() external view returns (uint256);
function MP_RATE_PER_YEAR() external view returns (uint256);
function MAX_MULTIPLIER() external view returns (uint256);
}
24 changes: 12 additions & 12 deletions test/RewardsStreamerMP.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ contract RewardsStreamerMPTest is Test {

function _calculateBonusMP(uint256 amount, uint256 lockupTime) public view returns (uint256) {
return amount
* (lockupTime * streamer.MAX_MULTIPLIER() * streamer.SCALE_FACTOR() / streamer.MAX_LOCKING_PERIOD())
* (lockupTime * streamer.MAX_MULTIPLIER() * streamer.SCALE_FACTOR() / streamer.MAX_LOCKUP_PERIOD())
/ streamer.SCALE_FACTOR();
}

Expand Down Expand Up @@ -535,15 +535,15 @@ contract StakeTest is RewardsStreamerMPTest {

function test_StakeOneAccountWithMinLockUp() public {
uint256 stakeAmount = 10e18;
uint256 lockUpPeriod = streamer.MIN_LOCKING_PERIOD();
uint256 lockUpPeriod = streamer.MIN_LOCKUP_PERIOD();
uint256 expectedBonusMP = _calculateBonusMP(stakeAmount, lockUpPeriod);

_stake(alice, stakeAmount, lockUpPeriod);

checkStreamer(
CheckStreamerParams({
totalStaked: stakeAmount,
// 10e18 + (amount * (lockPeriod * MAX_MULTIPLIER * SCALE_FACTOR / MAX_LOCKING_PERIOD) / SCALE_FACTOR)
// 10e18 + (amount * (lockPeriod * MAX_MULTIPLIER * SCALE_FACTOR / MAX_LOCKUP_PERIOD) / SCALE_FACTOR)
totalMP: stakeAmount + expectedBonusMP,
totalMaxMP: 52_465_753_424_657_534_240,
stakingBalance: stakeAmount,
Expand All @@ -556,15 +556,15 @@ contract StakeTest is RewardsStreamerMPTest {

function test_StakeOneAccountWithMaxLockUp() public {
uint256 stakeAmount = 10e18;
uint256 lockUpPeriod = streamer.MAX_LOCKING_PERIOD();
uint256 lockUpPeriod = streamer.MAX_LOCKUP_PERIOD();
uint256 expectedBonusMP = _calculateBonusMP(stakeAmount, lockUpPeriod);

_stake(alice, stakeAmount, lockUpPeriod);

checkStreamer(
CheckStreamerParams({
totalStaked: stakeAmount,
// 10 + (amount * (lockPeriod * MAX_MULTIPLIER * SCALE_FACTOR / MAX_LOCKING_PERIOD) / SCALE_FACTOR)
// 10 + (amount * (lockPeriod * MAX_MULTIPLIER * SCALE_FACTOR / MAX_LOCKUP_PERIOD) / SCALE_FACTOR)
totalMP: stakeAmount + expectedBonusMP,
totalMaxMP: 90e18,
stakingBalance: stakeAmount,
Expand All @@ -577,15 +577,15 @@ contract StakeTest is RewardsStreamerMPTest {

function test_StakeOneAccountWithRandomLockUp() public {
uint256 stakeAmount = 10e18;
uint256 lockUpPeriod = streamer.MIN_LOCKING_PERIOD() + 13 days;
uint256 lockUpPeriod = streamer.MIN_LOCKUP_PERIOD() + 13 days;
uint256 expectedBonusMP = _calculateBonusMP(stakeAmount, lockUpPeriod);

_stake(alice, stakeAmount, lockUpPeriod);

checkStreamer(
CheckStreamerParams({
totalStaked: stakeAmount,
// 10 + (amount * (lockPeriod * MAX_MULTIPLIER * SCALE_FACTOR / MAX_LOCKING_PERIOD) / SCALE_FACTOR)
// 10 + (amount * (lockPeriod * MAX_MULTIPLIER * SCALE_FACTOR / MAX_LOCKUP_PERIOD) / SCALE_FACTOR)
totalMP: stakeAmount + expectedBonusMP,
totalMaxMP: 52_821_917_808_219_178_080,
stakingBalance: stakeAmount,
Expand Down Expand Up @@ -860,7 +860,7 @@ contract StakeTest is RewardsStreamerMPTest {

function test_StakeMultipleAccountsWithMinLockUp() public {
uint256 aliceStakeAmount = 10e18;
uint256 aliceLockUpPeriod = streamer.MIN_LOCKING_PERIOD();
uint256 aliceLockUpPeriod = streamer.MIN_LOCKUP_PERIOD();
uint256 aliceExpectedBonusMP = _calculateBonusMP(aliceStakeAmount, aliceLockUpPeriod);

uint256 bobStakeAmount = 30e18;
Expand Down Expand Up @@ -891,11 +891,11 @@ contract StakeTest is RewardsStreamerMPTest {

function test_StakeMultipleAccountsWithRandomLockUp() public {
uint256 aliceStakeAmount = 10e18;
uint256 aliceLockUpPeriod = streamer.MAX_LOCKING_PERIOD() - 21 days;
uint256 aliceLockUpPeriod = streamer.MAX_LOCKUP_PERIOD() - 21 days;
uint256 aliceExpectedBonusMP = _calculateBonusMP(aliceStakeAmount, aliceLockUpPeriod);

uint256 bobStakeAmount = 30e18;
uint256 bobLockUpPeriod = streamer.MIN_LOCKING_PERIOD() + 43 days;
uint256 bobLockUpPeriod = streamer.MIN_LOCKUP_PERIOD() + 43 days;
uint256 bobExpectedBonusMP = _calculateBonusMP(bobStakeAmount, bobLockUpPeriod);

// alice stakes with lockup period
Expand Down Expand Up @@ -1157,7 +1157,7 @@ contract UnstakeTest is StakeTest {
test_StakeOneAccountWithMinLockUp();

uint256 stakeAmount = 10e18;
uint256 lockUpPeriod = streamer.MIN_LOCKING_PERIOD();
uint256 lockUpPeriod = streamer.MIN_LOCKUP_PERIOD();
// 10e18 is what's used in `test_StakeOneAccountWithMinLockUp`
uint256 expectedBonusMP = _calculateBonusMP(stakeAmount, lockUpPeriod);

Expand Down Expand Up @@ -1230,7 +1230,7 @@ contract UnstakeTest is StakeTest {
function test_UnstakeBonusMPAndAccuredMP() public {
// setup variables
uint256 amountStaked = 10e18;
uint256 secondsLocked = streamer.MIN_LOCKING_PERIOD();
uint256 secondsLocked = streamer.MIN_LOCKUP_PERIOD();
uint256 reducedStake = 5e18;
uint256 increasedTime = 365 days;

Expand Down

0 comments on commit d4b25c8

Please sign in to comment.