2023-05-16 21:59:25 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
2023-07-24 15:16:44 +00:00
|
|
|
pragma solidity ^0.8.18;
|
2023-05-16 21:59:25 +00:00
|
|
|
|
2023-09-12 16:37:30 +00:00
|
|
|
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
|
|
|
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
|
2024-02-24 19:39:38 +00:00
|
|
|
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
|
|
|
|
|
2023-09-12 16:37:30 +00:00
|
|
|
import { StakeVault } from "./StakeVault.sol";
|
2023-05-16 21:59:25 +00:00
|
|
|
|
2023-06-23 21:38:55 +00:00
|
|
|
contract StakeManager is Ownable {
|
2023-10-10 13:32:46 +00:00
|
|
|
error StakeManager__SenderIsNotVault();
|
2023-10-10 13:44:16 +00:00
|
|
|
error StakeManager__FundsLocked();
|
2024-02-18 21:37:15 +00:00
|
|
|
error StakeManager__InvalidLockTime();
|
2023-10-10 13:49:34 +00:00
|
|
|
error StakeManager__NoPendingMigration();
|
|
|
|
error StakeManager__PendingMigration();
|
|
|
|
error StakeManager__SenderIsNotPreviousStakeManager();
|
|
|
|
error StakeManager__InvalidLimitEpoch();
|
2024-02-18 21:37:15 +00:00
|
|
|
error StakeManager__AccountNotInitialized();
|
2024-02-22 21:55:24 +00:00
|
|
|
error StakeManager__InvalidMigration();
|
2024-03-04 15:23:47 +00:00
|
|
|
error StakeManager__AlreadyProcessedEpochs();
|
2024-03-04 15:11:01 +00:00
|
|
|
error StakeManager__InsufficientFunds();
|
2023-10-10 13:32:46 +00:00
|
|
|
|
2023-06-20 14:53:34 +00:00
|
|
|
struct Account {
|
2024-02-18 21:37:15 +00:00
|
|
|
address rewardAddress;
|
2023-06-20 14:53:34 +00:00
|
|
|
uint256 balance;
|
2024-02-18 21:37:15 +00:00
|
|
|
uint256 initialMP;
|
|
|
|
uint256 currentMP;
|
2023-06-26 14:52:51 +00:00
|
|
|
uint256 lastMint;
|
2024-02-18 21:37:15 +00:00
|
|
|
uint256 lockUntil;
|
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;
|
2023-09-12 16:37:30 +00:00
|
|
|
uint256 epochReward;
|
|
|
|
uint256 totalSupply;
|
2023-06-21 14:20:23 +00:00
|
|
|
}
|
|
|
|
|
2023-06-23 23:01:59 +00:00
|
|
|
uint256 public constant EPOCH_SIZE = 1 weeks;
|
2023-09-12 16:37:30 +00:00
|
|
|
uint256 public constant YEAR = 365 days;
|
2024-02-23 14:54:42 +00:00
|
|
|
uint256 public constant MIN_LOCKUP_PERIOD = 2 weeks;
|
2023-12-06 11:10:07 +00:00
|
|
|
uint256 public constant MAX_LOCKUP_PERIOD = 4 * YEAR; // 4 years
|
2023-09-12 16:37:30 +00:00
|
|
|
uint256 public constant MP_APY = 1;
|
|
|
|
uint256 public constant MAX_BOOST = 4;
|
2023-06-23 16:31:13 +00:00
|
|
|
|
2024-02-18 21:37:15 +00:00
|
|
|
mapping(address index => Account value) public accounts;
|
|
|
|
mapping(uint256 index => Epoch value) public epochs;
|
|
|
|
mapping(bytes32 codehash => bool approved) public isVault;
|
2023-06-23 16:31:13 +00:00
|
|
|
|
2023-06-26 14:52:51 +00:00
|
|
|
uint256 public currentEpoch;
|
|
|
|
uint256 public pendingReward;
|
2024-02-18 21:37:15 +00:00
|
|
|
uint256 public totalSupplyMP;
|
|
|
|
uint256 public totalSupplyBalance;
|
2023-06-23 21:38:55 +00:00
|
|
|
StakeManager public migration;
|
2024-06-19 09:26:03 +00:00
|
|
|
StakeManager public immutable previousManager;
|
2023-06-23 23:01:59 +00:00
|
|
|
ERC20 public immutable stakedToken;
|
2023-09-12 16:37:30 +00:00
|
|
|
|
2024-02-18 21:37:15 +00:00
|
|
|
/**
|
|
|
|
* @notice Only callable by vaults
|
|
|
|
*/
|
2023-09-12 16:37:30 +00:00
|
|
|
modifier onlyVault() {
|
2023-10-10 13:32:46 +00:00
|
|
|
if (!isVault[msg.sender.codehash]) {
|
|
|
|
revert StakeManager__SenderIsNotVault();
|
|
|
|
}
|
2023-06-23 18:03:52 +00:00
|
|
|
_;
|
|
|
|
}
|
|
|
|
|
2024-06-19 09:26:03 +00:00
|
|
|
modifier onlyAccountInitialized(address account) {
|
2024-02-22 21:23:03 +00:00
|
|
|
if (accounts[account].lockUntil == 0) {
|
|
|
|
revert StakeManager__AccountNotInitialized();
|
|
|
|
}
|
|
|
|
_;
|
|
|
|
}
|
|
|
|
|
2024-02-18 21:37:15 +00:00
|
|
|
/**
|
|
|
|
* @notice Only callable when migration is not initialized.
|
|
|
|
*/
|
2024-06-19 09:26:03 +00:00
|
|
|
modifier noPendingMigration() {
|
2024-01-24 15:07:23 +00:00
|
|
|
if (address(migration) != address(0)) {
|
|
|
|
revert StakeManager__PendingMigration();
|
|
|
|
}
|
|
|
|
_;
|
|
|
|
}
|
|
|
|
|
2024-02-18 21:37:15 +00:00
|
|
|
/**
|
|
|
|
* @notice Only callable when migration is initialized.
|
|
|
|
*/
|
2024-06-19 09:26:03 +00:00
|
|
|
modifier onlyPendingMigration() {
|
2024-01-24 15:07:23 +00:00
|
|
|
if (address(migration) == address(0)) {
|
|
|
|
revert StakeManager__NoPendingMigration();
|
|
|
|
}
|
|
|
|
_;
|
|
|
|
}
|
|
|
|
|
2024-02-18 21:37:15 +00:00
|
|
|
/**
|
|
|
|
* @notice Only callable from old manager.
|
|
|
|
*/
|
2024-06-19 09:26:03 +00:00
|
|
|
modifier onlyPreviousManager() {
|
|
|
|
if (msg.sender != address(previousManager)) {
|
2024-01-24 15:07:23 +00:00
|
|
|
revert StakeManager__SenderIsNotPreviousStakeManager();
|
|
|
|
}
|
|
|
|
_;
|
|
|
|
}
|
|
|
|
|
2024-02-18 21:37:15 +00:00
|
|
|
/**
|
|
|
|
* @notice Process epoch if it has ended
|
|
|
|
*/
|
2024-06-19 09:26:03 +00:00
|
|
|
modifier finalizeEpoch() {
|
2024-02-18 21:37:15 +00:00
|
|
|
if (block.timestamp >= epochEnd() && address(migration) == address(0)) {
|
|
|
|
//finalize current epoch
|
|
|
|
epochs[currentEpoch].epochReward = epochReward();
|
|
|
|
epochs[currentEpoch].totalSupply = totalSupply();
|
|
|
|
pendingReward += epochs[currentEpoch].epochReward;
|
|
|
|
//create new epoch
|
|
|
|
currentEpoch++;
|
|
|
|
epochs[currentEpoch].startTime = block.timestamp;
|
|
|
|
}
|
|
|
|
_;
|
|
|
|
}
|
|
|
|
|
2024-06-19 09:26:03 +00:00
|
|
|
constructor(address _stakedToken, address _previousManager) {
|
2023-06-26 14:52:51 +00:00
|
|
|
epochs[0].startTime = block.timestamp;
|
2024-06-19 09:26:03 +00:00
|
|
|
previousManager = StakeManager(_previousManager);
|
2023-09-12 16:37:30 +00:00
|
|
|
stakedToken = ERC20(_stakedToken);
|
2023-06-21 14:20:23 +00:00
|
|
|
}
|
|
|
|
|
2023-06-23 16:47:37 +00:00
|
|
|
/**
|
|
|
|
* Increases balance of msg.sender;
|
|
|
|
* @param _amount Amount of balance to be decreased.
|
2024-03-04 15:27:40 +00:00
|
|
|
* @param _timeToIncrease Seconds to increase in locked time. If stake is unlocked, increases from block.timestamp.
|
2023-12-06 11:10:07 +00:00
|
|
|
*
|
2024-03-04 15:27:40 +00:00
|
|
|
* @dev Reverts when resulting locked time is not in range of [MIN_LOCKUP_PERIOD, MAX_LOCKUP_PERIOD]
|
2023-06-23 16:47:37 +00:00
|
|
|
*/
|
2024-06-19 09:26:03 +00:00
|
|
|
function stake(uint256 _amount, uint256 _timeToIncrease) external onlyVault noPendingMigration finalizeEpoch {
|
2023-06-22 18:42:42 +00:00
|
|
|
Account storage account = accounts[msg.sender];
|
2024-06-19 09:26:03 +00:00
|
|
|
|
2024-02-22 21:23:03 +00:00
|
|
|
if (account.lockUntil == 0) {
|
|
|
|
// account not initialized
|
|
|
|
account.lockUntil = block.timestamp;
|
2024-02-24 19:34:15 +00:00
|
|
|
account.epoch = currentEpoch; //starts in current epoch
|
2024-02-22 21:23:03 +00:00
|
|
|
account.rewardAddress = StakeVault(msg.sender).owner();
|
|
|
|
} else {
|
|
|
|
_processAccount(account, currentEpoch);
|
|
|
|
}
|
2024-06-19 09:26:03 +00:00
|
|
|
|
2024-03-04 15:27:40 +00:00
|
|
|
uint256 deltaTime = 0;
|
2024-06-19 09:26:03 +00:00
|
|
|
|
2024-03-04 15:27:40 +00:00
|
|
|
if (_timeToIncrease > 0) {
|
|
|
|
uint256 lockUntil = account.lockUntil + _timeToIncrease;
|
|
|
|
if (lockUntil < block.timestamp) {
|
|
|
|
revert StakeManager__InvalidLockTime();
|
|
|
|
}
|
2024-06-19 09:26:03 +00:00
|
|
|
|
2024-03-04 15:27:40 +00:00
|
|
|
deltaTime = lockUntil - block.timestamp;
|
|
|
|
if (deltaTime < MIN_LOCKUP_PERIOD || deltaTime > MAX_LOCKUP_PERIOD) {
|
|
|
|
revert StakeManager__InvalidLockTime();
|
|
|
|
}
|
|
|
|
}
|
2024-06-19 09:26:03 +00:00
|
|
|
_mintInitialMP(account, deltaTime, _amount);
|
|
|
|
|
2024-02-18 21:37:15 +00:00
|
|
|
//update storage
|
|
|
|
totalSupplyBalance += _amount;
|
2023-06-22 18:42:42 +00:00
|
|
|
account.balance += _amount;
|
2024-03-04 15:27:40 +00:00
|
|
|
account.lockUntil += _timeToIncrease;
|
2023-05-16 21:59:25 +00:00
|
|
|
}
|
|
|
|
|
2023-06-23 16:47:37 +00:00
|
|
|
/**
|
2024-02-18 21:37:15 +00:00
|
|
|
* leaves the staking pool and withdraws all funds;
|
2023-06-23 16:47:37 +00:00
|
|
|
*/
|
2024-06-19 09:26:03 +00:00
|
|
|
function unstake(uint256 _amount)
|
|
|
|
external
|
|
|
|
onlyVault
|
|
|
|
onlyAccountInitialized(msg.sender)
|
|
|
|
noPendingMigration
|
|
|
|
finalizeEpoch
|
|
|
|
{
|
2023-06-22 18:42:42 +00:00
|
|
|
Account storage account = accounts[msg.sender];
|
2024-02-18 21:37:15 +00:00
|
|
|
if (_amount > account.balance) {
|
2024-03-04 15:11:01 +00:00
|
|
|
revert StakeManager__InsufficientFunds();
|
2024-02-18 21:37:15 +00:00
|
|
|
}
|
2023-10-10 13:44:16 +00:00
|
|
|
if (account.lockUntil > block.timestamp) {
|
|
|
|
revert StakeManager__FundsLocked();
|
|
|
|
}
|
2024-02-18 21:37:15 +00:00
|
|
|
_processAccount(account, currentEpoch);
|
|
|
|
|
2024-02-24 19:39:38 +00:00
|
|
|
uint256 reducedMP = Math.mulDiv(_amount, account.currentMP, account.balance);
|
|
|
|
uint256 reducedInitialMP = Math.mulDiv(_amount, account.initialMP, account.balance);
|
2024-02-18 21:37:15 +00:00
|
|
|
|
|
|
|
//update storage
|
2023-06-22 18:42:42 +00:00
|
|
|
account.balance -= _amount;
|
2024-02-18 21:37:15 +00:00
|
|
|
account.initialMP -= reducedInitialMP;
|
|
|
|
account.currentMP -= reducedMP;
|
|
|
|
totalSupplyBalance -= _amount;
|
|
|
|
totalSupplyMP -= reducedMP;
|
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.
|
2024-03-04 15:27:40 +00:00
|
|
|
* @param _timeToIncrease Seconds to increase in locked time. If stake is unlocked, increases from block.timestamp.
|
2023-12-06 11:10:07 +00:00
|
|
|
*
|
2024-03-04 15:27:40 +00:00
|
|
|
* @dev Reverts when resulting locked time is not in range of [MIN_LOCKUP_PERIOD, MAX_LOCKUP_PERIOD]
|
2023-06-23 16:29:36 +00:00
|
|
|
*/
|
2024-06-19 09:26:03 +00:00
|
|
|
function lock(uint256 _timeToIncrease)
|
|
|
|
external
|
|
|
|
onlyVault
|
|
|
|
onlyAccountInitialized(msg.sender)
|
|
|
|
noPendingMigration
|
|
|
|
finalizeEpoch
|
|
|
|
{
|
2023-06-22 18:42:42 +00:00
|
|
|
Account storage account = accounts[msg.sender];
|
2024-02-18 21:37:15 +00:00
|
|
|
_processAccount(account, currentEpoch);
|
2024-03-04 15:27:40 +00:00
|
|
|
uint256 lockUntil = account.lockUntil;
|
|
|
|
uint256 deltaTime;
|
|
|
|
if (lockUntil < block.timestamp) {
|
|
|
|
lockUntil = block.timestamp + _timeToIncrease;
|
|
|
|
deltaTime = _timeToIncrease;
|
|
|
|
} else {
|
|
|
|
lockUntil += _timeToIncrease;
|
|
|
|
deltaTime = lockUntil - block.timestamp;
|
|
|
|
}
|
|
|
|
if (deltaTime < MIN_LOCKUP_PERIOD || deltaTime > MAX_LOCKUP_PERIOD) {
|
2024-02-18 21:37:15 +00:00
|
|
|
revert StakeManager__InvalidLockTime();
|
2023-10-10 13:49:34 +00:00
|
|
|
}
|
2024-06-19 09:26:03 +00:00
|
|
|
_mintInitialMP(account, _timeToIncrease, 0);
|
2024-02-18 21:37:15 +00:00
|
|
|
//update account storage
|
2024-03-04 15:27:40 +00:00
|
|
|
account.lockUntil = lockUntil;
|
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.
|
2024-06-19 09:26:03 +00:00
|
|
|
* @dev only executes the prerequisite modifier finalizeEpoch
|
2023-06-23 16:47:37 +00:00
|
|
|
*/
|
2024-06-19 09:26:03 +00:00
|
|
|
function executeEpoch() external noPendingMigration finalizeEpoch {
|
|
|
|
return; //see modifier finalizeEpoch
|
2023-06-21 14:20:23 +00:00
|
|
|
}
|
|
|
|
|
2023-06-23 16:47:37 +00:00
|
|
|
/**
|
|
|
|
* @notice Execute rewards for account until limit has reached
|
|
|
|
* @param _vault Referred account
|
2023-06-23 23:01:59 +00:00
|
|
|
* @param _limitEpoch Until what epoch it should be executed
|
2023-06-23 16:47:37 +00:00
|
|
|
*/
|
2024-06-19 09:26:03 +00:00
|
|
|
function executeAccount(
|
|
|
|
address _vault,
|
|
|
|
uint256 _limitEpoch
|
|
|
|
)
|
|
|
|
external
|
|
|
|
onlyAccountInitialized(_vault)
|
|
|
|
finalizeEpoch
|
|
|
|
{
|
2024-02-18 21:37:15 +00:00
|
|
|
_processAccount(accounts[_vault], _limitEpoch);
|
2023-06-21 14:20:23 +00:00
|
|
|
}
|
2023-09-12 16:37:30 +00:00
|
|
|
|
2023-06-23 18:03:52 +00:00
|
|
|
/**
|
|
|
|
* @notice Enables a contract class to interact with staking functions
|
|
|
|
* @param _codehash bytecode hash of contract
|
|
|
|
*/
|
2023-06-23 21:38:55 +00:00
|
|
|
function setVault(bytes32 _codehash) external onlyOwner {
|
2023-09-12 16:37:30 +00:00
|
|
|
isVault[_codehash] = true;
|
2023-06-23 18:03:52 +00:00
|
|
|
}
|
2024-01-24 15:07:23 +00:00
|
|
|
|
2023-06-23 23:01:59 +00:00
|
|
|
/**
|
2024-01-24 15:07:23 +00:00
|
|
|
* @notice starts migration to new StakeManager
|
|
|
|
* @param _migration new StakeManager
|
2023-06-23 23:01:59 +00:00
|
|
|
*/
|
2024-06-19 09:26:03 +00:00
|
|
|
function startMigration(StakeManager _migration) external onlyOwner noPendingMigration finalizeEpoch {
|
2024-02-22 21:55:24 +00:00
|
|
|
if (_migration == this || address(_migration) == address(0)) {
|
|
|
|
revert StakeManager__InvalidMigration();
|
|
|
|
}
|
2024-01-24 15:07:23 +00:00
|
|
|
migration = _migration;
|
|
|
|
stakedToken.transfer(address(migration), epochReward());
|
2024-02-18 21:37:15 +00:00
|
|
|
migration.migrationInitialize(currentEpoch, totalSupplyMP, totalSupplyBalance, epochs[currentEpoch].startTime);
|
2024-01-24 15:07:23 +00:00
|
|
|
}
|
2023-09-12 16:37:30 +00:00
|
|
|
|
2024-01-24 15:07:23 +00:00
|
|
|
/**
|
|
|
|
* @dev Callable automatically from old StakeManager.startMigration(address)
|
|
|
|
* @notice Initilizes migration process
|
|
|
|
* @param _currentEpoch epoch of old manager
|
2024-02-18 21:37:15 +00:00
|
|
|
* @param _totalSupplyMP MP supply on old manager
|
|
|
|
* @param _totalSupplyBalance stake supply on old manager
|
2024-01-24 15:07:23 +00:00
|
|
|
* @param _epochStartTime epoch start time of old manager
|
|
|
|
*/
|
|
|
|
function migrationInitialize(
|
|
|
|
uint256 _currentEpoch,
|
2024-02-18 21:37:15 +00:00
|
|
|
uint256 _totalSupplyMP,
|
|
|
|
uint256 _totalSupplyBalance,
|
2024-01-24 15:07:23 +00:00
|
|
|
uint256 _epochStartTime
|
|
|
|
)
|
|
|
|
external
|
2024-06-19 09:26:03 +00:00
|
|
|
onlyPreviousManager
|
2024-01-24 15:07:23 +00:00
|
|
|
{
|
2024-03-01 09:56:19 +00:00
|
|
|
if (address(migration) != address(0)) {
|
|
|
|
revert StakeManager__PendingMigration();
|
|
|
|
}
|
2024-03-04 15:23:47 +00:00
|
|
|
if (currentEpoch > 0) {
|
|
|
|
revert StakeManager__AlreadyProcessedEpochs();
|
|
|
|
}
|
2024-01-24 15:07:23 +00:00
|
|
|
currentEpoch = _currentEpoch;
|
2024-02-18 21:37:15 +00:00
|
|
|
totalSupplyMP = _totalSupplyMP;
|
|
|
|
totalSupplyBalance = _totalSupplyBalance;
|
2024-01-24 15:07:23 +00:00
|
|
|
epochs[currentEpoch].startTime = _epochStartTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @notice Transfer current epoch funds for migrated manager
|
|
|
|
*/
|
2024-06-19 09:26:03 +00:00
|
|
|
function transferNonPending() external onlyPendingMigration {
|
2024-01-24 15:07:23 +00:00
|
|
|
stakedToken.transfer(address(migration), epochReward());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @notice Migrate account to new manager.
|
|
|
|
* @param _acceptMigration true if wants to migrate, false if wants to leave
|
|
|
|
*/
|
2024-02-18 21:37:15 +00:00
|
|
|
function migrateTo(bool _acceptMigration)
|
|
|
|
external
|
|
|
|
onlyVault
|
2024-06-19 09:26:03 +00:00
|
|
|
onlyAccountInitialized(msg.sender)
|
|
|
|
onlyPendingMigration
|
|
|
|
finalizeEpoch
|
2024-02-18 21:37:15 +00:00
|
|
|
returns (StakeManager newManager)
|
|
|
|
{
|
|
|
|
_processAccount(accounts[msg.sender], currentEpoch);
|
2024-01-24 15:07:23 +00:00
|
|
|
Account memory account = accounts[msg.sender];
|
2024-02-18 21:37:15 +00:00
|
|
|
totalSupplyMP -= account.currentMP;
|
|
|
|
totalSupplyBalance -= account.balance;
|
2023-06-23 23:01:59 +00:00
|
|
|
delete accounts[msg.sender];
|
2024-01-24 15:07:23 +00:00
|
|
|
migration.migrateFrom(msg.sender, _acceptMigration, account);
|
2023-06-23 23:01:59 +00:00
|
|
|
return migration;
|
2023-06-23 21:38:55 +00:00
|
|
|
}
|
|
|
|
|
2023-06-23 23:01:59 +00:00
|
|
|
/**
|
|
|
|
* @dev Only callable from old manager.
|
|
|
|
* @notice Migrate account from old manager
|
|
|
|
* @param _vault Account address
|
|
|
|
* @param _account Account data
|
2024-01-24 15:07:23 +00:00
|
|
|
* @param _acceptMigration If account should be stored or its MP/balance supply reduced
|
2023-06-23 23:01:59 +00:00
|
|
|
*/
|
2024-06-19 09:26:03 +00:00
|
|
|
function migrateFrom(address _vault, bool _acceptMigration, Account memory _account) external onlyPreviousManager {
|
2024-01-24 15:07:23 +00:00
|
|
|
if (_acceptMigration) {
|
|
|
|
accounts[_vault] = _account;
|
|
|
|
} else {
|
2024-02-18 21:37:15 +00:00
|
|
|
totalSupplyMP -= _account.currentMP;
|
|
|
|
totalSupplyBalance -= _account.balance;
|
2023-10-10 13:49:34 +00:00
|
|
|
}
|
2024-01-24 15:07:23 +00:00
|
|
|
}
|
2024-02-18 21:37:15 +00:00
|
|
|
|
2024-01-24 15:07:23 +00:00
|
|
|
/**
|
|
|
|
* @dev Only callable from old manager.
|
|
|
|
* @notice Increase total MP from old manager
|
2024-06-19 09:26:03 +00:00
|
|
|
* @param _amount amount MP increased on account after migration initialized
|
2024-01-24 15:07:23 +00:00
|
|
|
*/
|
2024-06-19 09:26:03 +00:00
|
|
|
function increaseTotalMP(uint256 _amount) external onlyPreviousManager {
|
|
|
|
totalSupplyMP += _amount;
|
2023-06-26 14:52:51 +00:00
|
|
|
}
|
|
|
|
|
2024-02-18 21:37:15 +00:00
|
|
|
/**
|
|
|
|
* @notice Process account until limit has reached
|
|
|
|
* @param account Account to process
|
|
|
|
* @param _limitEpoch Until what epoch it should be executed
|
|
|
|
*/
|
|
|
|
function _processAccount(Account storage account, uint256 _limitEpoch) private {
|
2023-10-10 13:49:34 +00:00
|
|
|
if (_limitEpoch > currentEpoch) {
|
|
|
|
revert StakeManager__InvalidLimitEpoch();
|
|
|
|
}
|
2023-06-26 14:52:51 +00:00
|
|
|
uint256 userReward;
|
|
|
|
uint256 userEpoch = account.epoch;
|
2024-02-18 21:37:15 +00:00
|
|
|
uint256 mpDifference = account.currentMP;
|
2024-01-24 15:07:23 +00:00
|
|
|
for (Epoch storage iEpoch = epochs[userEpoch]; userEpoch < _limitEpoch; userEpoch++) {
|
2024-02-18 21:37:15 +00:00
|
|
|
//mint multiplier points to that epoch
|
|
|
|
_mintMP(account, iEpoch.startTime + EPOCH_SIZE, iEpoch);
|
|
|
|
uint256 userSupply = account.balance + account.currentMP;
|
2024-02-24 19:39:38 +00:00
|
|
|
uint256 userEpochReward = Math.mulDiv(userSupply, iEpoch.epochReward, iEpoch.totalSupply);
|
|
|
|
|
2024-01-24 15:07:23 +00:00
|
|
|
userReward += userEpochReward;
|
|
|
|
iEpoch.epochReward -= userEpochReward;
|
|
|
|
iEpoch.totalSupply -= userSupply;
|
2023-06-26 14:52:51 +00:00
|
|
|
}
|
|
|
|
account.epoch = userEpoch;
|
2023-09-12 16:37:30 +00:00
|
|
|
if (userReward > 0) {
|
2023-06-26 14:52:51 +00:00
|
|
|
pendingReward -= userReward;
|
|
|
|
stakedToken.transfer(account.rewardAddress, userReward);
|
|
|
|
}
|
2024-02-18 21:37:15 +00:00
|
|
|
mpDifference = account.currentMP - mpDifference;
|
|
|
|
if (address(migration) != address(0)) {
|
2024-06-19 09:26:03 +00:00
|
|
|
migration.increaseTotalMP(mpDifference);
|
2024-02-18 21:37:15 +00:00
|
|
|
} else if (userEpoch == currentEpoch) {
|
|
|
|
_mintMP(account, block.timestamp, epochs[currentEpoch]);
|
2024-01-24 15:07:23 +00:00
|
|
|
}
|
2023-06-26 14:52:51 +00:00
|
|
|
}
|
|
|
|
|
2024-02-18 21:37:15 +00:00
|
|
|
/**
|
2024-06-19 09:26:03 +00:00
|
|
|
* @notice Mint initial multiplier points for given staking amount and time
|
|
|
|
* @dev if amount is greater 0, it increases difference of amount for current remaining lock time
|
2024-02-18 21:37:15 +00:00
|
|
|
* @dev if increased lock time, increases difference of total new balance for increased lock time
|
|
|
|
* @param account Account to mint multiplier points
|
|
|
|
* @param increasedLockTime increased lock time
|
2024-06-19 09:26:03 +00:00
|
|
|
* @param amount amount to stake
|
2024-02-18 21:37:15 +00:00
|
|
|
*/
|
2024-06-19 09:26:03 +00:00
|
|
|
function _mintInitialMP(Account storage account, uint256 increasedLockTime, uint256 amount) private {
|
|
|
|
uint256 mpToMint;
|
|
|
|
if (amount > 0) {
|
|
|
|
mpToMint += amount; //initial multiplier points
|
2024-02-18 21:37:15 +00:00
|
|
|
if (block.timestamp < account.lockUntil) {
|
|
|
|
//increasing balance on locked account?
|
|
|
|
//bonus for remaining previously locked time of new balance.
|
2024-06-19 09:26:03 +00:00
|
|
|
mpToMint += _getMPToMint(amount, account.lockUntil - block.timestamp);
|
2024-02-18 21:37:15 +00:00
|
|
|
}
|
2024-01-24 15:07:23 +00:00
|
|
|
}
|
2024-02-18 21:37:15 +00:00
|
|
|
if (increasedLockTime > 0) {
|
|
|
|
//bonus for increased lock time
|
2024-06-19 09:26:03 +00:00
|
|
|
mpToMint += _getMPToMint(account.balance + amount, increasedLockTime);
|
2024-02-18 21:37:15 +00:00
|
|
|
}
|
|
|
|
//update storage
|
2024-06-19 09:26:03 +00:00
|
|
|
totalSupplyMP += mpToMint;
|
|
|
|
account.initialMP += mpToMint;
|
|
|
|
account.currentMP += mpToMint;
|
2024-02-18 21:37:15 +00:00
|
|
|
account.lastMint = block.timestamp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @notice Mint multiplier points for given account and epoch
|
|
|
|
* @param account Account earning multiplier points
|
|
|
|
* @param processTime amount of time of multiplier points
|
|
|
|
* @param epoch Epoch to increment total supply
|
|
|
|
*/
|
|
|
|
function _mintMP(Account storage account, uint256 processTime, Epoch storage epoch) private {
|
2024-06-19 09:26:03 +00:00
|
|
|
uint256 increasedMP = _getMaxMPToMint( //check for MAX_BOOST
|
|
|
|
_getMPToMint(account.balance, processTime - account.lastMint),
|
2024-02-18 21:37:15 +00:00
|
|
|
account.balance,
|
|
|
|
account.initialMP,
|
|
|
|
account.currentMP
|
|
|
|
);
|
|
|
|
|
|
|
|
//update storage
|
|
|
|
account.lastMint = processTime;
|
|
|
|
account.currentMP += increasedMP;
|
|
|
|
totalSupplyMP += increasedMP;
|
|
|
|
epoch.totalSupply += increasedMP;
|
2023-07-06 12:12:01 +00:00
|
|
|
}
|
2023-06-26 14:52:51 +00:00
|
|
|
|
2024-02-18 21:37:15 +00:00
|
|
|
/**
|
|
|
|
* @notice Calculates maximum multiplier point increase for given balance
|
2024-06-19 09:26:03 +00:00
|
|
|
* @param _mpToMint tested value
|
2024-02-18 21:37:15 +00:00
|
|
|
* @param _balance balance of account
|
|
|
|
* @param _currentMP current multiplier point of the account
|
|
|
|
* @param _initialMP initial multiplier point of the account
|
|
|
|
* @return _maxToIncrease maximum multiplier point increase
|
|
|
|
*/
|
2024-06-19 09:26:03 +00:00
|
|
|
function _getMaxMPToMint(
|
|
|
|
uint256 _mpToMint,
|
2024-02-18 21:37:15 +00:00
|
|
|
uint256 _balance,
|
|
|
|
uint256 _initialMP,
|
|
|
|
uint256 _currentMP
|
2023-09-12 16:37:30 +00:00
|
|
|
)
|
|
|
|
private
|
2024-02-18 21:37:15 +00:00
|
|
|
pure
|
|
|
|
returns (uint256 _maxToIncrease)
|
2023-09-12 16:37:30 +00:00
|
|
|
{
|
2024-02-18 21:37:15 +00:00
|
|
|
// Maximum multiplier point for given balance
|
2024-06-19 09:26:03 +00:00
|
|
|
_maxToIncrease = _getMPToMint(_balance, MAX_BOOST * YEAR) + _initialMP;
|
|
|
|
if (_mpToMint + _currentMP > _maxToIncrease) {
|
2024-02-18 21:37:15 +00:00
|
|
|
//reached cap when increasing MP
|
|
|
|
return _maxToIncrease - _currentMP; //how much left to reach cap
|
|
|
|
} else {
|
|
|
|
//not reached capw hen increasing MP
|
2024-06-19 09:26:03 +00:00
|
|
|
return _mpToMint; //just return tested value
|
2024-02-18 21:37:15 +00:00
|
|
|
}
|
2023-06-26 14:52:51 +00:00
|
|
|
}
|
2024-02-22 22:03:02 +00:00
|
|
|
|
2024-02-18 21:37:15 +00:00
|
|
|
/**
|
2024-06-19 09:26:03 +00:00
|
|
|
* @notice Calculates multiplier points to mint for given balance and time
|
2024-02-18 21:37:15 +00:00
|
|
|
* @param _balance balance of account
|
|
|
|
* @param _deltaTime time difference
|
2024-06-19 09:26:03 +00:00
|
|
|
* @return multiplier points to mint
|
2024-02-18 21:37:15 +00:00
|
|
|
*/
|
2024-06-19 09:26:03 +00:00
|
|
|
function _getMPToMint(uint256 _balance, uint256 _deltaTime) private pure returns (uint256) {
|
2024-02-24 19:39:38 +00:00
|
|
|
return Math.mulDiv(_balance, _deltaTime, YEAR) * MP_APY;
|
2023-06-26 14:52:51 +00:00
|
|
|
}
|
|
|
|
|
2024-02-18 21:37:15 +00:00
|
|
|
/**
|
|
|
|
* @notice Returns total of multiplier points and balance
|
|
|
|
* @return _totalSupply current total supply
|
|
|
|
*/
|
|
|
|
function totalSupply() public view returns (uint256 _totalSupply) {
|
|
|
|
return totalSupplyMP + totalSupplyBalance;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @notice Returns funds available for current epoch
|
|
|
|
* @return _epochReward current epoch reward
|
|
|
|
*/
|
|
|
|
function epochReward() public view returns (uint256 _epochReward) {
|
2023-06-26 14:52:51 +00:00
|
|
|
return stakedToken.balanceOf(address(this)) - pendingReward;
|
|
|
|
}
|
|
|
|
|
2024-02-18 21:37:15 +00:00
|
|
|
/**
|
|
|
|
* @notice Returns end time of current epoch
|
|
|
|
* @return _epochEnd end time of current epoch
|
|
|
|
*/
|
|
|
|
function epochEnd() public view returns (uint256 _epochEnd) {
|
2023-06-26 14:52:51 +00:00
|
|
|
return epochs[currentEpoch].startTime + EPOCH_SIZE;
|
|
|
|
}
|
2023-09-12 16:37:30 +00:00
|
|
|
}
|