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";
|
|
|
|
|
|
|
|
contract StakeManager is ERC20 {
|
|
|
|
|
2023-06-20 14:53:34 +00:00
|
|
|
ERC20 stakedToken;
|
|
|
|
|
2023-06-21 13:59:30 +00:00
|
|
|
uint256 public constant MP_APY = 1;
|
2023-05-24 13:25:52 +00:00
|
|
|
uint256 public constant STAKE_APY = 1;
|
|
|
|
uint256 public constant MAX_BOOST = 1;
|
2023-06-20 14:53:34 +00:00
|
|
|
uint256 public constant MAX_MP = 1;
|
|
|
|
mapping (address => Account) accounts;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint256 currentEpoch;
|
|
|
|
uint256 pendingReward;
|
2023-06-23 16:09:07 +00:00
|
|
|
uint256 public totalSupply;
|
2023-06-21 14:20:23 +00:00
|
|
|
|
|
|
|
uint256 public constant EPOCH_SIZE = 1 week;
|
|
|
|
|
|
|
|
mapping (uint256 => Epoch) epoch;
|
2023-06-22 18:42:42 +00:00
|
|
|
mapping (address => Account) accounts;
|
2023-05-16 21:59:25 +00:00
|
|
|
|
|
|
|
|
2023-06-21 14:20:23 +00:00
|
|
|
constructor() {
|
|
|
|
epoch[0].startTime = now();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
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:09:07 +00:00
|
|
|
uint256 inceasedMultiplier = _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;
|
|
|
|
|
|
|
|
multiplierSupply += inceasedMultiplier;
|
|
|
|
totalSupply += _amount;
|
2023-05-16 21:59:25 +00:00
|
|
|
}
|
|
|
|
|
2023-06-23 16:09:07 +00:00
|
|
|
|
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-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");
|
|
|
|
uint256 dT = now() + _time - account.lockUntil;
|
|
|
|
account.lockUntil = now() + _time;
|
|
|
|
account.multiplier += _amount * dT;
|
2023-05-16 21:59:25 +00:00
|
|
|
}
|
|
|
|
|
2023-06-20 14:53:34 +00:00
|
|
|
/**
|
|
|
|
* @dev Function called to increase the Multiplier Points of a Vault
|
|
|
|
* @param _vault
|
|
|
|
*/
|
|
|
|
function mintMultiplierPoints(address _vault) external {
|
2023-06-22 18:42:42 +00:00
|
|
|
Account storage account = accounts[msg.sender];
|
2023-06-23 16:09:07 +00:00
|
|
|
uint256 dT = now() - account.lastAccured;
|
|
|
|
uint256 inceasedMultiplier = calcAccuredMultiplierPoints(account.balance, account.multiplier, dT);
|
|
|
|
account.lastAccured = now();
|
|
|
|
account.multiplier += inceasedMultiplier;
|
|
|
|
multiplierSupply += inceasedMultiplier;
|
2023-05-16 21:59:25 +00:00
|
|
|
}
|
|
|
|
|
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-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-20 14:53:34 +00:00
|
|
|
function calcAccuredMultiplierPoints(uint256 _balance, uint256 _currentMp, uint256 _deltaTime) pure public returns(uint256) {
|
|
|
|
uint256 accured = _balance * (MP_APY * _deltaTime);
|
|
|
|
uint256 newMp = accured + _currentMp;
|
|
|
|
return newMp > MAX_MP ? MAX_MP - newMp : accurred;
|
2023-05-24 13:25:52 +00:00
|
|
|
}
|
|
|
|
|
2023-05-16 21:59:25 +00:00
|
|
|
}
|