Skip to content

Commit

Permalink
Merge pull request #495 from NexusMutual/test/staking-pool-deposit-to…
Browse files Browse the repository at this point in the history
…-unit-tests

Test: Add stakingPool depositTo unit tests
  • Loading branch information
roxdanila committed Nov 22, 2022
2 parents 5259de2 + 04bd647 commit 882cc42
Show file tree
Hide file tree
Showing 8 changed files with 864 additions and 21 deletions.
4 changes: 4 additions & 0 deletions contracts/interfaces/IStakingPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,8 @@ interface IStakingPool {
uint lastBasePrice,
uint targetPrice
);

/* ========== EVENTS ========== */

event StakeDeposited(address indexed user, uint256 amount, uint256 trancheId, uint256 tokenId);
}
2 changes: 2 additions & 0 deletions contracts/mocks/TokenControllerMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ contract TokenControllerMock is MasterAware {
token.operatorTransfer(from, amount);
}

function withdrawNXMStakeAndRewards(address to, uint stakeToWithdraw, uint rewardsToWithdraw, uint poolId) external {}

/* unused functions */

modifier unused {
Expand Down
44 changes: 29 additions & 15 deletions contracts/modules/staking/StakingPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,13 @@ contract StakingPool is IStakingPool, ERC721 {
uint public constant INITIAL_PRICE_DENOMINATOR = 100_00;
uint public constant TARGET_PRICE_DENOMINATOR = 100_00;

// base price bump is +0.2% for each 1% of capacity used, ie +20% for 100%
// 20% = 0.2
uint public constant PRICE_BUMP_RATIO = 0.2 ether;
// base price bump
// +0.2% for each 1% of capacity used, ie +20% for 100%
uint public constant PRICE_BUMP_RATIO = 20_00; // 20%

// next price smoothing
// 0.005 ether = 0.5% out of 1e18
uint public constant PRICE_CHANGE_PER_DAY = 0.005 ether;
// 0.5% per day
uint public constant PRICE_CHANGE_PER_DAY = 50; // 0.5%

// +2% for every 1%, ie +200% for 100%
uint public constant SURGE_PRICE_RATIO = 2 ether;
Expand Down Expand Up @@ -279,7 +279,10 @@ contract StakingPool is IStakingPool, ERC721 {
uint bucketStartTime = _firstActiveBucketId * BUCKET_DURATION;
uint elapsed = bucketStartTime - _lastAccNxmUpdate;

uint newAccNxmPerRewardsShare = elapsed * _rewardPerSecond / _rewardsSharesSupply;
uint newAccNxmPerRewardsShare = _rewardsSharesSupply != 0
? elapsed * _rewardPerSecond / _rewardsSharesSupply
: 0;

_accNxmPerRewardsShare = _accNxmPerRewardsShare.uncheckedAdd(newAccNxmPerRewardsShare);

_rewardPerSecond -= rewardBuckets[_firstActiveBucketId].rewardPerSecondCut;
Expand All @@ -294,7 +297,9 @@ contract StakingPool is IStakingPool, ERC721 {
{
uint trancheEndTime = (_firstActiveTrancheId + 1) * TRANCHE_DURATION;
uint elapsed = trancheEndTime - _lastAccNxmUpdate;
uint newAccNxmPerRewardsShare = elapsed * _rewardPerSecond / _rewardsSharesSupply;
uint newAccNxmPerRewardsShare = _rewardsSharesSupply != 0
? elapsed * _rewardPerSecond / _rewardsSharesSupply
: 0;
_accNxmPerRewardsShare = _accNxmPerRewardsShare.uncheckedAdd(newAccNxmPerRewardsShare);
_lastAccNxmUpdate = trancheEndTime;

Expand Down Expand Up @@ -324,7 +329,9 @@ contract StakingPool is IStakingPool, ERC721 {

if (updateUntilCurrentTimestamp) {
uint elapsed = block.timestamp - _lastAccNxmUpdate;
uint newAccNxmPerRewardsShare = elapsed * _rewardPerSecond / _rewardsSharesSupply;
uint newAccNxmPerRewardsShare = _rewardsSharesSupply != 0
? elapsed * _rewardPerSecond / _rewardsSharesSupply
: 0;
_accNxmPerRewardsShare = _accNxmPerRewardsShare.uncheckedAdd(newAccNxmPerRewardsShare);
_lastAccNxmUpdate = block.timestamp;
}
Expand Down Expand Up @@ -380,7 +387,8 @@ contract StakingPool is IStakingPool, ERC721 {
address to = request.destination == address(0) ? msg.sender : request.destination;
_mint(to, tokenIds[i]);
} else {
require(ownerOf(request.tokenId) != address(0), "StakingPool: Token does not exist");
// validate token id exists. ownerOf() reverts if owner is address 0
ownerOf(request.tokenId);
tokenIds[i] = request.tokenId;
}

Expand All @@ -406,7 +414,7 @@ contract StakingPool is IStakingPool, ERC721 {
);

// if we're increasing an existing deposit
if (deposit.lastAccNxmPerRewardShare != 0) {
if (deposit.rewardsShares != 0) {
uint newEarningsPerShare = _accNxmPerRewardsShare.uncheckedSub(deposit.lastAccNxmPerRewardShare);
deposit.pendingRewards += newEarningsPerShare * deposit.rewardsShares;
}
Expand Down Expand Up @@ -450,6 +458,8 @@ contract StakingPool is IStakingPool, ERC721 {
_activeStake += request.amount;
_stakeSharesSupply += newStakeShares;
_rewardsSharesSupply += newRewardsShares;

emit StakeDeposited(msg.sender, request.amount, request.trancheId, tokenIds[i]);
}
address source = msg.sender == coverContract ? manager() : msg.sender;
// transfer nxm from the staker and update the pool deposit balance
Expand Down Expand Up @@ -667,11 +677,17 @@ contract StakingPool is IStakingPool, ERC721 {

uint rewards = premium * request.rewardRatio / REWARDS_DENOMINATOR;
uint expireAtBucket = Math.divCeil(block.timestamp + request.period, BUCKET_DURATION);
uint _rewardPerSecond = rewards / (expireAtBucket * BUCKET_DURATION - block.timestamp);
uint rewardStreamPeriod = expireAtBucket * BUCKET_DURATION - block.timestamp;
uint _rewardPerSecond = rewards / rewardStreamPeriod;

// recalculating rewards to avoid minting dust that will not be streamed
rewards = _rewardPerSecond * rewardStreamPeriod;

// 1 SLOAD + 1 SSTORE
rewardBuckets[expireAtBucket].rewardPerSecondCut += _rewardPerSecond;

rewardPerSecond += _rewardPerSecond;

// scale back from 2 to 18 decimals
allocatedCoverAmount *= NXM_PER_ALLOCATION_UNIT;

Expand Down Expand Up @@ -1544,11 +1560,9 @@ function setProducts(StakedProductParam[] memory params) external onlyManager {
uint initialCapacityUsed,
uint totalCapacity
) public pure returns (uint) {

// base price has 18 decimals
// cover amount has 2 decimals (100 = 1 unit)
// dividing by ALLOCATION_UNITS_PER_NXM (=100) to get the right amount of decimals
uint basePremium = basePrice * coverAmount / ALLOCATION_UNITS_PER_NXM;
// scale coverAmount to 18 decimals and apply price percentage
uint basePremium = (coverAmount * NXM_PER_ALLOCATION_UNIT) * basePrice / TARGET_PRICE_DENOMINATOR;
uint finalCapacityUsed = initialCapacityUsed + coverAmount;

// surge price is applied for the capacity used above SURGE_THRESHOLD_RATIO.
Expand Down
7 changes: 1 addition & 6 deletions test/unit/StakingPool/calculateNewRewardShares.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@ const {
utils: { parseUnits },
},
} = require('hardhat');

const TRANCHE_DURATION =
91 * // days
24 * // hourss
60 * // minutes
60; // seconds
const { TRANCHE_DURATION } = require('./helpers');

describe('calculateNewRewardShares', function () {
it('grants bonus shares proportionally to the time left of the first active tranche', async function () {
Expand Down

0 comments on commit 882cc42

Please sign in to comment.