mirror of
https://github.com/vacp2p/staking-reward-streamer.git
synced 2026-01-08 23:08:19 -05:00
@@ -57,7 +57,7 @@ contract RewardsStreamerMP is ReentrancyGuard {
|
||||
}
|
||||
|
||||
_updateGlobalState();
|
||||
updateUserMP(msg.sender);
|
||||
_updateUserMP(msg.sender);
|
||||
|
||||
UserInfo storage user = users[msg.sender];
|
||||
if (user.lockUntil != 0 && user.lockUntil > block.timestamp) {
|
||||
@@ -110,7 +110,7 @@ contract RewardsStreamerMP is ReentrancyGuard {
|
||||
}
|
||||
|
||||
_updateGlobalState();
|
||||
updateUserMP(msg.sender);
|
||||
_updateUserMP(msg.sender);
|
||||
|
||||
uint256 userRewards = calculateUserRewards(msg.sender);
|
||||
if (userRewards > 0) {
|
||||
@@ -192,7 +192,7 @@ contract RewardsStreamerMP is ReentrancyGuard {
|
||||
}
|
||||
}
|
||||
|
||||
function updateUserMP(address userAddress) internal {
|
||||
function _updateUserMP(address userAddress) internal {
|
||||
UserInfo storage user = users[userAddress];
|
||||
|
||||
if (user.userPotentialMP == 0 || user.stakedBalance == 0) {
|
||||
@@ -217,6 +217,10 @@ contract RewardsStreamerMP is ReentrancyGuard {
|
||||
user.lastMPUpdateTime = block.timestamp;
|
||||
}
|
||||
|
||||
function updateUserMP(address userAddress) external {
|
||||
_updateUserMP(userAddress);
|
||||
}
|
||||
|
||||
function calculateUserRewards(address userAddress) public view returns (uint256) {
|
||||
UserInfo storage user = users[userAddress];
|
||||
uint256 userWeight = user.stakedBalance + user.userMP;
|
||||
|
||||
@@ -95,6 +95,13 @@ contract RewardsStreamerMPTest is Test {
|
||||
* (lockupTime * streamer.MAX_MULTIPLIER() * streamer.SCALE_FACTOR() / streamer.MAX_LOCKING_PERIOD())
|
||||
/ streamer.SCALE_FACTOR();
|
||||
}
|
||||
|
||||
function _calculateTimeToMPLimit(uint256 amount) public view returns (uint256) {
|
||||
uint256 maxMP = amount * streamer.MAX_MULTIPLIER();
|
||||
uint256 mpPerYear = (amount * streamer.MP_RATE_PER_YEAR()) / streamer.SCALE_FACTOR();
|
||||
uint256 timeInSeconds = (maxMP * 365 days) / mpPerYear;
|
||||
return timeInSeconds;
|
||||
}
|
||||
}
|
||||
|
||||
contract IntegrationTest is RewardsStreamerMPTest {
|
||||
@@ -577,6 +584,172 @@ contract StakeTest is RewardsStreamerMPTest {
|
||||
);
|
||||
}
|
||||
|
||||
function test_StakeOneAccountMPIncreasesPotentialMPDecreases() public {
|
||||
uint256 stakeAmount = 15e18;
|
||||
uint256 potentialMP = stakeAmount * streamer.MAX_MULTIPLIER();
|
||||
uint256 totalMP = stakeAmount;
|
||||
|
||||
_stake(alice, stakeAmount, 0);
|
||||
|
||||
checkStreamer(
|
||||
CheckStreamerParams({
|
||||
totalStaked: stakeAmount,
|
||||
totalMP: stakeAmount,
|
||||
potentialMP: potentialMP,
|
||||
stakingBalance: stakeAmount,
|
||||
rewardBalance: 0,
|
||||
rewardIndex: 0,
|
||||
accountedRewards: 0
|
||||
})
|
||||
);
|
||||
|
||||
uint256 currentTime = vm.getBlockTimestamp();
|
||||
vm.warp(currentTime + (365 days));
|
||||
|
||||
streamer.updateGlobalState();
|
||||
streamer.updateUserMP(alice);
|
||||
|
||||
uint256 expectedMPIncrease = stakeAmount; // 1 year passed, 1 MP accrued per token staked
|
||||
totalMP = totalMP + expectedMPIncrease;
|
||||
potentialMP = potentialMP - expectedMPIncrease; // we expect a decrease in potential MP as some MP have already
|
||||
// been minted
|
||||
|
||||
checkStreamer(
|
||||
CheckStreamerParams({
|
||||
totalStaked: stakeAmount,
|
||||
totalMP: totalMP,
|
||||
potentialMP: potentialMP,
|
||||
stakingBalance: stakeAmount,
|
||||
rewardBalance: 0,
|
||||
rewardIndex: 0,
|
||||
accountedRewards: 0
|
||||
})
|
||||
);
|
||||
|
||||
checkUser(
|
||||
CheckUserParams({
|
||||
user: alice,
|
||||
rewardBalance: 0,
|
||||
stakedBalance: stakeAmount,
|
||||
rewardIndex: 0,
|
||||
userMP: totalMP, // userMP == totalMP because only one user is staking
|
||||
userPotentialMP: potentialMP
|
||||
})
|
||||
);
|
||||
|
||||
currentTime = vm.getBlockTimestamp();
|
||||
vm.warp(currentTime + (365 days / 2));
|
||||
|
||||
streamer.updateGlobalState();
|
||||
streamer.updateUserMP(alice);
|
||||
|
||||
expectedMPIncrease = stakeAmount / 2; // 1/2 year passed, 1/2 MP accrued per token staked
|
||||
totalMP = totalMP + expectedMPIncrease;
|
||||
potentialMP = potentialMP - expectedMPIncrease;
|
||||
|
||||
checkStreamer(
|
||||
CheckStreamerParams({
|
||||
totalStaked: stakeAmount,
|
||||
totalMP: totalMP,
|
||||
potentialMP: potentialMP,
|
||||
stakingBalance: stakeAmount,
|
||||
rewardBalance: 0,
|
||||
rewardIndex: 0,
|
||||
accountedRewards: 0
|
||||
})
|
||||
);
|
||||
|
||||
checkUser(
|
||||
CheckUserParams({
|
||||
user: alice,
|
||||
rewardBalance: 0,
|
||||
stakedBalance: stakeAmount,
|
||||
rewardIndex: 0,
|
||||
userMP: totalMP, // userMP == totalMP because only one user is staking
|
||||
userPotentialMP: potentialMP
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function test_StakeOneAccountReachingMPLimit() public {
|
||||
uint256 stakeAmount = 15e18;
|
||||
uint256 potentialMP = stakeAmount * streamer.MAX_MULTIPLIER();
|
||||
uint256 totalMP = stakeAmount;
|
||||
|
||||
_stake(alice, stakeAmount, 0);
|
||||
|
||||
checkStreamer(
|
||||
CheckStreamerParams({
|
||||
totalStaked: stakeAmount,
|
||||
totalMP: stakeAmount,
|
||||
potentialMP: potentialMP,
|
||||
stakingBalance: stakeAmount,
|
||||
rewardBalance: 0,
|
||||
rewardIndex: 0,
|
||||
accountedRewards: 0
|
||||
})
|
||||
);
|
||||
|
||||
checkUser(
|
||||
CheckUserParams({
|
||||
user: alice,
|
||||
rewardBalance: 0,
|
||||
stakedBalance: stakeAmount,
|
||||
rewardIndex: 0,
|
||||
userMP: totalMP, // userMP == totalMP because only one user is staking
|
||||
userPotentialMP: potentialMP
|
||||
})
|
||||
);
|
||||
|
||||
uint256 currentTime = vm.getBlockTimestamp();
|
||||
uint256 timeToMaxMP = _calculateTimeToMPLimit(stakeAmount);
|
||||
vm.warp(currentTime + timeToMaxMP);
|
||||
|
||||
streamer.updateGlobalState();
|
||||
streamer.updateUserMP(alice);
|
||||
|
||||
checkStreamer(
|
||||
CheckStreamerParams({
|
||||
totalStaked: stakeAmount,
|
||||
totalMP: potentialMP + stakeAmount,
|
||||
potentialMP: 0,
|
||||
stakingBalance: stakeAmount,
|
||||
rewardBalance: 0,
|
||||
rewardIndex: 0,
|
||||
accountedRewards: 0
|
||||
})
|
||||
);
|
||||
|
||||
checkUser(
|
||||
CheckUserParams({
|
||||
user: alice,
|
||||
rewardBalance: 0,
|
||||
stakedBalance: stakeAmount,
|
||||
rewardIndex: 0,
|
||||
userMP: potentialMP + stakeAmount, // userMP == totalMP because only one user is staking
|
||||
userPotentialMP: 0
|
||||
})
|
||||
);
|
||||
|
||||
// move forward in time to check we're not producing more MP
|
||||
vm.warp(currentTime + 1);
|
||||
|
||||
streamer.updateGlobalState();
|
||||
streamer.updateUserMP(alice);
|
||||
|
||||
checkStreamer(
|
||||
CheckStreamerParams({
|
||||
totalStaked: stakeAmount,
|
||||
totalMP: potentialMP + stakeAmount,
|
||||
potentialMP: 0,
|
||||
stakingBalance: stakeAmount,
|
||||
rewardBalance: 0,
|
||||
rewardIndex: 0,
|
||||
accountedRewards: 0
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function test_StakeMultipleAccounts() public {
|
||||
// Alice stakes 10 tokens
|
||||
_stake(alice, 10e18, 0);
|
||||
@@ -736,6 +909,160 @@ contract StakeTest is RewardsStreamerMPTest {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function test_StakeMultipleAccountsMPIncreasesPotentialMPDecreases() public {
|
||||
uint256 aliceStakeAmount = 15e18;
|
||||
uint256 aliceMP = aliceStakeAmount;
|
||||
uint256 alicePotentialMP = aliceStakeAmount * streamer.MAX_MULTIPLIER();
|
||||
|
||||
uint256 bobStakeAmount = 5e18;
|
||||
uint256 bobMP = bobStakeAmount;
|
||||
uint256 bobPotentialMP = bobStakeAmount * streamer.MAX_MULTIPLIER();
|
||||
|
||||
uint256 totalMP = aliceStakeAmount + bobStakeAmount;
|
||||
uint256 totalStaked = aliceStakeAmount + bobStakeAmount;
|
||||
uint256 potentialMP = alicePotentialMP + bobPotentialMP;
|
||||
|
||||
_stake(alice, aliceStakeAmount, 0);
|
||||
_stake(bob, bobStakeAmount, 0);
|
||||
|
||||
checkStreamer(
|
||||
CheckStreamerParams({
|
||||
totalStaked: totalStaked,
|
||||
totalMP: totalMP,
|
||||
potentialMP: potentialMP,
|
||||
stakingBalance: totalStaked,
|
||||
rewardBalance: 0,
|
||||
rewardIndex: 0,
|
||||
accountedRewards: 0
|
||||
})
|
||||
);
|
||||
|
||||
checkUser(
|
||||
CheckUserParams({
|
||||
user: alice,
|
||||
rewardBalance: 0,
|
||||
stakedBalance: aliceStakeAmount,
|
||||
rewardIndex: 0,
|
||||
userMP: aliceMP,
|
||||
userPotentialMP: alicePotentialMP
|
||||
})
|
||||
);
|
||||
checkUser(
|
||||
CheckUserParams({
|
||||
user: bob,
|
||||
rewardBalance: 0,
|
||||
stakedBalance: bobStakeAmount,
|
||||
rewardIndex: 0,
|
||||
userMP: bobMP,
|
||||
userPotentialMP: bobPotentialMP
|
||||
})
|
||||
);
|
||||
|
||||
uint256 currentTime = vm.getBlockTimestamp();
|
||||
vm.warp(currentTime + (365 days));
|
||||
|
||||
streamer.updateGlobalState();
|
||||
streamer.updateUserMP(alice);
|
||||
streamer.updateUserMP(bob);
|
||||
|
||||
uint256 aliceExpectedMPIncrease = aliceStakeAmount; // 1 year passed, 1 MP accrued per token staked
|
||||
uint256 bobExpectedMPIncrease = bobStakeAmount; // 1 year passed, 1 MP accrued per token staked
|
||||
uint256 totalExpectedMPIncrease = aliceExpectedMPIncrease + bobExpectedMPIncrease;
|
||||
|
||||
aliceMP = aliceMP + aliceExpectedMPIncrease;
|
||||
bobMP = bobMP + bobExpectedMPIncrease;
|
||||
totalMP = totalMP + totalExpectedMPIncrease;
|
||||
|
||||
alicePotentialMP = alicePotentialMP - aliceExpectedMPIncrease;
|
||||
bobPotentialMP = bobPotentialMP - bobExpectedMPIncrease;
|
||||
potentialMP = potentialMP - totalExpectedMPIncrease;
|
||||
|
||||
checkStreamer(
|
||||
CheckStreamerParams({
|
||||
totalStaked: totalStaked,
|
||||
totalMP: totalMP,
|
||||
potentialMP: potentialMP,
|
||||
stakingBalance: totalStaked,
|
||||
rewardBalance: 0,
|
||||
rewardIndex: 0,
|
||||
accountedRewards: 0
|
||||
})
|
||||
);
|
||||
|
||||
checkUser(
|
||||
CheckUserParams({
|
||||
user: alice,
|
||||
rewardBalance: 0,
|
||||
stakedBalance: aliceStakeAmount,
|
||||
rewardIndex: 0,
|
||||
userMP: aliceMP,
|
||||
userPotentialMP: alicePotentialMP
|
||||
})
|
||||
);
|
||||
checkUser(
|
||||
CheckUserParams({
|
||||
user: bob,
|
||||
rewardBalance: 0,
|
||||
stakedBalance: bobStakeAmount,
|
||||
rewardIndex: 0,
|
||||
userMP: bobMP,
|
||||
userPotentialMP: bobPotentialMP
|
||||
})
|
||||
);
|
||||
|
||||
currentTime = vm.getBlockTimestamp();
|
||||
vm.warp(currentTime + (365 days / 2));
|
||||
|
||||
streamer.updateGlobalState();
|
||||
streamer.updateUserMP(alice);
|
||||
streamer.updateUserMP(bob);
|
||||
|
||||
aliceExpectedMPIncrease = aliceStakeAmount / 2;
|
||||
bobExpectedMPIncrease = bobStakeAmount / 2;
|
||||
totalExpectedMPIncrease = aliceExpectedMPIncrease + bobExpectedMPIncrease;
|
||||
|
||||
aliceMP = aliceMP + aliceExpectedMPIncrease;
|
||||
bobMP = bobMP + bobExpectedMPIncrease;
|
||||
totalMP = totalMP + totalExpectedMPIncrease;
|
||||
|
||||
alicePotentialMP = alicePotentialMP - aliceExpectedMPIncrease;
|
||||
bobPotentialMP = bobPotentialMP - bobExpectedMPIncrease;
|
||||
potentialMP = potentialMP - totalExpectedMPIncrease;
|
||||
|
||||
checkStreamer(
|
||||
CheckStreamerParams({
|
||||
totalStaked: totalStaked,
|
||||
totalMP: totalMP,
|
||||
potentialMP: potentialMP,
|
||||
stakingBalance: totalStaked,
|
||||
rewardBalance: 0,
|
||||
rewardIndex: 0,
|
||||
accountedRewards: 0
|
||||
})
|
||||
);
|
||||
|
||||
checkUser(
|
||||
CheckUserParams({
|
||||
user: alice,
|
||||
rewardBalance: 0,
|
||||
stakedBalance: aliceStakeAmount,
|
||||
rewardIndex: 0,
|
||||
userMP: aliceMP,
|
||||
userPotentialMP: alicePotentialMP
|
||||
})
|
||||
);
|
||||
checkUser(
|
||||
CheckUserParams({
|
||||
user: bob,
|
||||
rewardBalance: 0,
|
||||
stakedBalance: bobStakeAmount,
|
||||
rewardIndex: 0,
|
||||
userMP: bobMP,
|
||||
userPotentialMP: bobPotentialMP
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
contract UnstakeTest is StakeTest {
|
||||
|
||||
Reference in New Issue
Block a user