staking/contracts/StakeManager.sol

158 lines
5.3 KiB
Solidity
Raw Normal View History

2023-05-16 21:59:25 +00:00
// SPDX-License-Identifier: MIT
2023-06-20 14:53:34 +00:00
pragma solidity 0.8.19;
2023-05-16 21:59:25 +00:00
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
2023-06-23 18:03:52 +00:00
contract StakeManager is Controlled {
2023-05-16 21:59:25 +00:00
2023-06-20 14:53:34 +00:00
struct Account {
2023-06-23 16:09:07 +00:00
uint256 lockUntil;
2023-06-20 14:53:34 +00:00
uint256 balance;
uint256 multiplier;
2023-06-23 16:09:07 +00:00
uint256 lastAccured;
2023-06-21 14:20:23 +00:00
uint256 epoch;
2023-06-20 14:53:34 +00:00
}
2023-05-16 21:59:25 +00:00
2023-06-21 14:20:23 +00:00
struct Epoch {
uint256 startTime;
uint256 totalReward;
}
2023-06-23 16:31:13 +00:00
uint256 public constant EPOCH_SIZE = 1 week;
uint256 public constant MP_APY = 1;
uint256 public constant STAKE_APY = 1;
uint256 public constant MAX_BOOST = 1;
uint256 public constant MAX_MP = 1;
mapping (address => Account) accounts;
mapping (uint256 => Epoch) epoch;
2023-06-23 18:03:52 +00:00
mapping (bytes32 => bool) isVault;
2023-06-23 16:31:13 +00:00
ERC20 stakedToken;
2023-06-21 14:20:23 +00:00
uint256 currentEpoch;
uint256 pendingReward;
2023-06-23 16:09:07 +00:00
uint256 public totalSupply;
2023-05-16 21:59:25 +00:00
2023-06-23 18:03:52 +00:00
modifier onlyVault {
require(isVault[msg.sender.codehash], "Not a vault")
_;
}
2023-06-21 14:20:23 +00:00
constructor() {
epoch[0].startTime = now();
}
2023-06-23 16:47:37 +00:00
/**
* Increases balance of msg.sender;
* @param _amount Amount of balance to be decreased.
* @param _time Seconds from now() to lock balance.
*/
2023-06-20 14:53:34 +00:00
function increaseBalance(uint256 _amount, uint256 _time) external {
2023-06-22 18:42:42 +00:00
Account storage account = accounts[msg.sender];
2023-06-23 16:29:36 +00:00
uint256 increasedMultiplier = _amount * (_time + 1);
2023-06-22 18:42:42 +00:00
account.balance += _amount;
account.multiplier += mp;
2023-06-23 16:09:07 +00:00
account.lastAccured = now();
account.lockUntil = now() + _time;
2023-06-23 16:29:36 +00:00
multiplierSupply += increasedMultiplier;
2023-06-23 16:09:07 +00:00
totalSupply += _amount;
2023-05-16 21:59:25 +00:00
}
2023-06-23 16:47:37 +00:00
/**
* Decreases balance of msg.sender;
* @param _amount Amount of balance to be decreased
*/
2023-06-20 14:53:34 +00:00
function decreaseBalance(uint256 _amount) external {
2023-06-22 18:42:42 +00:00
Account storage account = accounts[msg.sender];
2023-06-23 16:09:07 +00:00
uint256 reducedMultiplier = (_amount * account.multiplier) / account.balance;
account.multiplier -= reducedMultiplier;
2023-06-22 18:42:42 +00:00
account.balance -= _amount;
2023-06-23 16:09:07 +00:00
multiplierSupply -= reducedMultiplier;
totalSupply -= _amount;
2023-05-16 21:59:25 +00:00
}
2023-06-23 16:29:36 +00:00
/**
2023-06-23 16:47:37 +00:00
* @notice Locks entire balance for more amount of time.
2023-06-23 16:29:36 +00:00
* @param _time amount of time to lock from now.
*/
2023-06-20 14:53:34 +00:00
function balanceLock(uint256 _time) external {
2023-06-22 18:42:42 +00:00
Account storage account = accounts[msg.sender];
2023-06-23 16:09:07 +00:00
require(now() + _time > account.lockUntil, "Cannot decrease lock time");
2023-06-23 16:29:36 +00:00
//if balance still locked, multipliers must be minted from difference of time.
uint256 dT = account.lockUntil > now() ? now() + _time - account.lockUntil : _time);
2023-06-23 16:09:07 +00:00
account.lockUntil = now() + _time;
2023-06-23 16:29:36 +00:00
uint256 increasedMultiplier = _amount * dT;
account.multiplier += increasedMultiplier;
multiplierSupply += increasedMultiplier;
2023-05-16 21:59:25 +00:00
}
2023-06-20 14:53:34 +00:00
/**
2023-06-23 16:47:37 +00:00
* @notice Increase the multiplier points of an account
* @param _vault Referring account
2023-06-20 14:53:34 +00:00
*/
function mintMultiplierPoints(address _vault) external {
2023-06-22 18:42:42 +00:00
Account storage account = accounts[msg.sender];
2023-06-23 16:29:36 +00:00
uint256 lastCall = now() - account.lastAccured;
uint256 increasedMultiplier = checkMaxMultiplier(
account.balance * (MP_APY * lastCall),
account.multiplier);
2023-06-23 16:09:07 +00:00
account.lastAccured = now();
2023-06-23 16:29:36 +00:00
account.multiplier += increasedMultiplier;
multiplierSupply += increasedMultiplier;
2023-05-16 21:59:25 +00:00
}
2023-06-23 16:47:37 +00:00
/**
* @notice Release rewards for current epoch and increase epoch.
*/
2023-06-21 14:20:23 +00:00
function executeEpochReward() external {
if(now() > epoch[currentEpoch].startTime + EPOCH_SIZE){
uint256 epochReward = stakedToken.balanceOf(this) - pendingReward;
epoch[currentEpoch].totalReward = epochReward;
pendingReward += epochReward;
currentEpoch++;
epoch[currentEpoch].startTime = now();
}
}
2023-06-23 16:47:37 +00:00
/**
* @notice Execute rewards for account until limit has reached
* @param _vault Referred account
* @param _limit Until what epoch it should be executed
*/
2023-06-21 15:32:30 +00:00
function executeUserReward(address _vault, uint256 _limitEpoch) external {
2023-06-22 18:42:42 +00:00
Account storage account = accounts[msg.sender];
2023-06-21 15:32:30 +00:00
uint256 userReward;
2023-06-22 18:42:42 +00:00
uint256 userEpoch = account.epoch
2023-06-21 15:32:30 +00:00
require(_limitEpoch <= currentEpoch, "Epoch not reached");
require(_limitEpoch > userEpoch, "Epoch already claimed");
2023-06-21 15:35:47 +00:00
uint256 totalShare = this.totalSupply + this.multiplierSupply;
2023-06-22 18:42:42 +00:00
uint256 userShare = account.balance + account.multiplier;
2023-06-21 15:35:47 +00:00
uint256 userRatio = userShare / totalShare; //TODO: might lose precision, multiply by 100 and divide back later?
2023-06-21 15:32:30 +00:00
for (; userEpoch < _limitEpoch; userEpoch++) {
2023-06-22 18:42:42 +00:00
userReward += userRatio * epoch[userEpoch].totalReward;
2023-06-21 15:32:30 +00:00
}
2023-06-22 18:42:42 +00:00
account.epoch = userEpoch;
2023-06-21 14:20:23 +00:00
pendingReward -= userReward;
stakedToken.transfer(_vault, userReward);
}
2023-06-23 18:03:52 +00:00
/**
* @notice Enables a contract class to interact with staking functions
* @param _codehash bytecode hash of contract
*/
function setVault(bytes32 _codehash) external onlyController {
isVault[_codehash] = true;
}
2023-06-21 14:20:23 +00:00
2023-06-23 16:29:36 +00:00
function checkMaxMultiplier(uint256 _increasedMultiplier, uint256 _currentMp) private view returns(uint256 _maxToIncrease) {
uint256 newMp = _increasedMultiplier + _currentMp;
return newMp > MAX_MP ? MAX_MP - newMp : _increasedMultiplier;
2023-05-24 13:25:52 +00:00
}
2023-05-16 21:59:25 +00:00
}