mirror of https://github.com/logos-co/staking.git
update math
This commit is contained in:
parent
4669a4fcf3
commit
9be84c707a
|
@ -1,51 +1,135 @@
|
|||
// SPDX-License-Identifier: MIT-1.0
|
||||
pragma solidity ^0.8.18;
|
||||
pragma solidity ^0.8.26;
|
||||
|
||||
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
|
||||
|
||||
abstract contract MultiplierPointMath {
|
||||
uint256 public constant YEAR = 365 days;
|
||||
uint256 public constant MP_APY = 1;
|
||||
/// @notice One (mean) tropical year, in seconds.
|
||||
uint256 public constant YEAR = 365 days + 5 hours + 48 minutes + 45 seconds;
|
||||
/// @notice Multiplier points annual percentage yield.
|
||||
uint256 public constant MP_APY = 100;
|
||||
/// @notice Accrued multiplier points maximum multiplier.
|
||||
uint256 public constant MAX_MULTIPLIER = 4;
|
||||
/// @notice The accrue rate period of time over which multiplier points are calculated.
|
||||
uint256 public constant ACCURE_RATE = 1 weeks;
|
||||
/// @notice Minimal value to generate 1 multiplier point in the accrue rate period (rounded up).
|
||||
uint256 public constant MIN_BALANCE = (((YEAR * 100) - 1) / (MP_APY * ACCURE_RATE)) + 1;
|
||||
/// @notice Multiplier points absolute maximum multiplier
|
||||
uint256 public constant MAX_MULTIPLIER_ABSOLUTE = 1 + (2 * (MAX_MULTIPLIER * MP_APY) / 100);
|
||||
/// @notice Maximum lockup period
|
||||
uint256 public constant MAX_LOCKUP_PERIOD = MAX_MULTIPLIER * YEAR;
|
||||
|
||||
/**
|
||||
* @notice Calculates multiplier points accurred for given `_amount` and `_seconds` time passed
|
||||
* @param _amount quantity of tokens
|
||||
* @param _seconds time in seconds
|
||||
* @notice Calculates the accrued multiplier points (MPs) over a time period Δt, based on the account balance
|
||||
* @param _balance Represents the current account balance
|
||||
* @param _deltaTime The time difference or the duration over which the multiplier points are accrued, expressed in
|
||||
* seconds
|
||||
* @return _accuredMP points accured for given `_amount` and `_seconds`
|
||||
* 51584438
|
||||
* 10000000
|
||||
*/
|
||||
function _calculateAccuredMP(uint256 _amount, uint256 _seconds) internal pure returns (uint256 _accuredMP) {
|
||||
return Math.mulDiv(_amount, _seconds, YEAR) * MP_APY;
|
||||
function _calculateAccuredMP(uint256 _balance, uint256 _deltaTime) public pure returns (uint256 _accuredMP) {
|
||||
return Math.mulDiv(_balance, _deltaTime * MP_APY, YEAR * 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Calculates bonus multiplier points for given `_amount` and `_lockedSeconds`
|
||||
* @notice Calculates the bonus multiplier points (MPs) earned when a balance Δa is locked for a specified duration
|
||||
* t_lock.
|
||||
* It is equivalent to the accrued multiplier points function but specifically applied in the context of a locked
|
||||
* balance.
|
||||
* @param _amount quantity of tokens
|
||||
* @param _lockedSeconds time in seconds locked
|
||||
* @return _bonusMP bonus multiplier points for given `_amount` and `_lockedSeconds`
|
||||
*/
|
||||
function _calculateBonusMP(uint256 _amount, uint256 _lockedSeconds) internal pure returns (uint256 _bonusMP) {
|
||||
_bonusMP = _amount;
|
||||
if (_lockedSeconds > 0) {
|
||||
_bonusMP += _calculateAccuredMP(_amount, _lockedSeconds);
|
||||
}
|
||||
function _calculateBonusMP(uint256 _amount, uint256 _lockedSeconds) public pure returns (uint256 _bonusMP) {
|
||||
return _calculateAccuredMP(_amount, _lockedSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Calculates minimum stake to genarate 1 multiplier points for given `_seconds`
|
||||
* @param _seconds time in seconds
|
||||
* @return _minimumStake minimum quantity of tokens
|
||||
* @notice Calculates the initial multiplier points (MPs) based on the balance change Δa. The result is equal to
|
||||
* the amount of balance added.
|
||||
* @param _amount Represents the change in balance.
|
||||
*/
|
||||
function _calculateMinimumStake(uint256 _seconds) internal pure returns (uint256 _minimumStake) {
|
||||
return YEAR / (_seconds * MP_APY);
|
||||
function _calculateInitialMP(uint256 _amount) public pure returns (uint256 _initialMP) {
|
||||
return _amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Calculates the reduction in multiplier points (MPs) when a portion of the balance Δa `_reducedAmount` is
|
||||
* removed from the total balance a_bal `_currentBalance`.
|
||||
* The reduction is proportional to the ratio of the removed balance to the total balance, applied to the current
|
||||
* multiplier points $mp$.
|
||||
* @param _mp Represents the current multiplier points
|
||||
* @param _currentBalance The total account balance before the removal of Δa `_reducedBalance`
|
||||
* @param _reducedAmount reduced balance
|
||||
* @return _reducedMP Multiplier points to reduce from `_mp`
|
||||
*/
|
||||
function _calculateReducedMP(
|
||||
uint256 _mp,
|
||||
uint256 _currentBalance,
|
||||
uint256 _reducedAmount
|
||||
)
|
||||
public
|
||||
pure
|
||||
returns (uint256 _reducedMP)
|
||||
{
|
||||
return Math.mulDiv(_mp, _currentBalance, _reducedAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Calculates maximum stake a given `_amount` can be generated with `MAX_MULTIPLIER`
|
||||
* @param _amount quantity of tokens
|
||||
* @param _balance quantity of tokens
|
||||
* @return _maxMPAccured maximum quantity of muliplier points that can be generated for given `_amount`
|
||||
*/
|
||||
function _calculateMaxAccuredMP(uint256 _amount) internal pure returns (uint256 _maxMPAccured) {
|
||||
return _calculateAccuredMP(_amount, MAX_MULTIPLIER * YEAR);
|
||||
function _calculateMaxAccuredMP(uint256 _balance) public pure returns (uint256 _maxMPAccured) {
|
||||
return Math.mulDiv(_balance, MAX_MULTIPLIER * MP_APY, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice The maximum total multiplier points that can be generated for a determined amount of balance and lock
|
||||
* duration.
|
||||
* @param _balance Represents the current account balance
|
||||
* @param _lockTime The time duration for which the balance is locked
|
||||
* @return _maxMP Maximum multiplier points that can be generated for given `_balance` and `_lockTime`
|
||||
*/
|
||||
function _calculateMaxMP(uint256 _balance, uint256 _lockTime) public pure returns (uint256 _maxMP) {
|
||||
return _balance + Math.mulDiv(_balance * MP_APY, (MAX_MULTIPLIER * YEAR) + _lockTime, YEAR * 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Caution: This value is estimated and can be incorrect due precision loss.
|
||||
* @notice Estimates the time an account set as locked time.
|
||||
* @param _mpMax Maximum multiplier points calculated from the current balance.
|
||||
* @param _currentBalance Current balance used to calculate the maximum multiplier points.
|
||||
*/
|
||||
function _estimateLockTime(uint256 _mpMax, uint256 _currentBalance) public pure returns (uint256 _lockTime) {
|
||||
return Math.mulDiv((_mpMax - _currentBalance) * 100, YEAR, _currentBalance * MP_APY, Math.Rounding.Ceil)
|
||||
- MAX_LOCKUP_PERIOD;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Caution: This value is estimated and can be incorrect due precision loss.
|
||||
* @notice Calculates the remaining lock time available for a given `_mpMax` and `_currentBalance`
|
||||
* @param _mpMax Maximum multiplier points calculated from the current balance.
|
||||
* @param _currentBalance Current balance used to calculate the maximum multiplier points.
|
||||
*/
|
||||
function _remainingLockTimeAvailable(
|
||||
uint256 _mpMax,
|
||||
uint256 _currentBalance
|
||||
)
|
||||
public
|
||||
pure
|
||||
returns (uint256 _lockTime)
|
||||
{
|
||||
return Math.mulDiv((_currentBalance * MAX_MULTIPLIER_ABSOLUTE) - _mpMax, YEAR, _currentBalance);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Calculates the lock time for a given bonus multiplier points and current balance.
|
||||
* @param _bonusMP bonus multiplier points intended to be generated
|
||||
* @param _currentBalance current balance
|
||||
*/
|
||||
function _calculateLockTime(uint256 _bonusMP, uint256 _currentBalance) public pure returns (uint256 _lockTime) {
|
||||
return Math.mulDiv(_bonusMP * 100, YEAR, _currentBalance * MP_APY);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
// SPDX-License-Identifier: MIT-1.0
|
||||
pragma solidity ^0.8.26;
|
||||
|
||||
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
|
||||
import { MultiplierPointMath } from "./MultiplierPointMath.sol";
|
||||
|
||||
abstract contract StakeMath is MultiplierPointMath {
|
||||
/// @notice Minimal lockup time
|
||||
uint256 public constant MIN_LOCKUP_TIME = 1 weeks;
|
||||
|
||||
/**
|
||||
* @notice Calculates the bonus multiplier points earned when a balance Δa is increased an optionally locked for a
|
||||
* specified duration
|
||||
* @param _balance Account current balance
|
||||
* @param _maxMP Account current max multiplier points
|
||||
* @param _lockEndTime Account current lock end timestamp
|
||||
* @param _processTime Process current timestamp
|
||||
* @param _increasedAmount Increased amount of balance
|
||||
* @param _increasedLockSeconds Increased amount of seconds to lock
|
||||
* @return _deltaMpTotal Increased amount of total multiplier points
|
||||
* @return _newMaxMP Account new max multiplier points
|
||||
* @return _newLockEnd Account new lock end timestamp
|
||||
*/
|
||||
function _calculateStake(
|
||||
uint256 _balance,
|
||||
uint256 _maxMP,
|
||||
uint256 _lockEndTime,
|
||||
uint256 _processTime,
|
||||
uint256 _increasedAmount,
|
||||
uint256 _increasedLockSeconds
|
||||
)
|
||||
public
|
||||
pure
|
||||
returns (uint256 _deltaMpTotal, uint256 _newMaxMP, uint256 _newLockEnd)
|
||||
{
|
||||
uint256 newBalance = _balance + _increasedAmount;
|
||||
require(newBalance >= MIN_BALANCE, "StakeMath: balance too low");
|
||||
_newLockEnd = Math.max(_lockEndTime, _processTime) + _increasedLockSeconds;
|
||||
uint256 dt_lock = _newLockEnd - _processTime;
|
||||
require(dt_lock == 0 || dt_lock >= MIN_LOCKUP_TIME, "StakeMath: lockup time too low");
|
||||
require(dt_lock <= MAX_LOCKUP_PERIOD, "StakeMath: lockup time too high");
|
||||
|
||||
uint256 deltaMpBonus;
|
||||
if (dt_lock > 0) {
|
||||
deltaMpBonus = _calculateBonusMP(_increasedAmount, dt_lock);
|
||||
}
|
||||
|
||||
if (_balance > 0 && _increasedLockSeconds > 0) {
|
||||
deltaMpBonus += _calculateBonusMP(_balance, _increasedLockSeconds);
|
||||
}
|
||||
|
||||
_deltaMpTotal = _calculateInitialMP(_increasedAmount) + deltaMpBonus;
|
||||
_newMaxMP = _maxMP + _deltaMpTotal + _calculateAccuredMP(_balance, MAX_MULTIPLIER * YEAR);
|
||||
|
||||
require(
|
||||
_newMaxMP <= MAX_MULTIPLIER_ABSOLUTE * (_balance + _increasedAmount), "StakeMath: max multiplier exceeded"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Calculates the bonus multiplier points earned when a balance Δa is locked for a specified duration
|
||||
* @param _balance Account current balance
|
||||
* @param _maxMP Account current max multiplier points
|
||||
* @param _lockEndTime Account current lock end timestamp
|
||||
* @param _processTime Process current timestamp
|
||||
* @param _increasedLockSeconds Increased amount of seconds to lock
|
||||
* @return _deltaMpTotal Increased amount of total multiplier points
|
||||
* @return _newMaxMP Account new max multiplier points
|
||||
* @return _newLockEnd Account new lock end timestamp
|
||||
*/
|
||||
function calculateLock(
|
||||
uint256 _balance,
|
||||
uint256 _maxMP,
|
||||
uint256 _lockEndTime,
|
||||
uint256 _processTime,
|
||||
uint256 _increasedLockSeconds
|
||||
)
|
||||
public
|
||||
pure
|
||||
returns (uint256 _deltaMpTotal, uint256 _newMaxMP, uint256 _newLockEnd)
|
||||
{
|
||||
require(_balance > 0);
|
||||
require(_increasedLockSeconds > 0);
|
||||
|
||||
_newLockEnd = Math.max(_lockEndTime, _processTime) + _increasedLockSeconds;
|
||||
uint256 dt_lock = _newLockEnd - _processTime;
|
||||
require(dt_lock == 0 || dt_lock >= MIN_LOCKUP_TIME, "StakeMath: lockup time too low");
|
||||
require(dt_lock <= MAX_LOCKUP_PERIOD, "StakeMath: lockup time too high");
|
||||
|
||||
_deltaMpTotal += _calculateBonusMP(_balance, _increasedLockSeconds);
|
||||
_newMaxMP = _maxMP + _deltaMpTotal;
|
||||
|
||||
require(_newMaxMP <= MAX_MULTIPLIER_ABSOLUTE * (_balance), "StakeMath: max multiplier exceeded");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param _balance Account current balance
|
||||
* @param _lockEndTime Account current lock end timestamp
|
||||
* @param _processTime Process current timestamp
|
||||
* @param _totalMP Account current total multiplier points
|
||||
* @param _maxMP Account current max multiplier points
|
||||
* @param _reducedAmount Reduced amount of balance
|
||||
* @return _deltaMpTotal Increased amount of total multiplier points
|
||||
* @return _deltaMpMax Increased amount of max multiplier points
|
||||
*/
|
||||
function _calculateUnstake(
|
||||
uint256 _balance,
|
||||
uint256 _lockEndTime,
|
||||
uint256 _processTime,
|
||||
uint256 _totalMP,
|
||||
uint256 _maxMP,
|
||||
uint256 _reducedAmount
|
||||
)
|
||||
public
|
||||
pure
|
||||
returns (uint256 _deltaMpTotal, uint256 _deltaMpMax)
|
||||
{
|
||||
require(_lockEndTime <= _processTime, "StakeMath: lockup not ended");
|
||||
require(_balance >= _reducedAmount, "StakeMath: balance too low");
|
||||
uint256 newBalance = _balance - _reducedAmount;
|
||||
require(newBalance == 0 || newBalance >= MIN_BALANCE, "StakeMath: balance too low");
|
||||
_deltaMpTotal = _calculateReducedMP(_totalMP, _balance, _reducedAmount);
|
||||
_deltaMpMax = _calculateReducedMP(_maxMP, _balance, _reducedAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Calculates the accrued multiplier points for a given balance and seconds passed since last accrual
|
||||
* @param _balance Account current balance
|
||||
* @param _totalMP Account current total multiplier points
|
||||
* @param _maxMP Account current max multiplier points
|
||||
* @param _lastAccrualTime Account current last accrual timestamp
|
||||
* @param _processTime Process current timestamp
|
||||
* @return _deltaMpTotal Increased amount of total multiplier points
|
||||
*/
|
||||
function _calculateAccrual(
|
||||
uint256 _balance,
|
||||
uint256 _totalMP,
|
||||
uint256 _maxMP,
|
||||
uint256 _lastAccrualTime,
|
||||
uint256 _processTime
|
||||
)
|
||||
public
|
||||
pure
|
||||
returns (uint256 _deltaMpTotal)
|
||||
{
|
||||
uint256 dt = _processTime - _lastAccrualTime;
|
||||
require(dt >= ACCURE_RATE, "StakeMath: no enough time passed");
|
||||
if (_totalMP <= _maxMP) {
|
||||
_deltaMpTotal = Math.min(_calculateAccuredMP(_balance, dt), _maxMP - _totalMP);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue