Skip to content

Commit

Permalink
checkpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
anajuliabit committed Jul 7, 2024
1 parent 31765c5 commit 98f4c90
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 35 deletions.
10 changes: 9 additions & 1 deletion src/RewardsDistributor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,15 @@ contract RewardsDistributor is Ownable2Step, IRewardsDistributor {
// difference in time since last update
uint256 timeDelta = block.timestamp - rewardConfiguration.lastUpdate;

if (rewardConfiguration.emissionRate != 0 && timeDelta != 0) {
// the contract must have funds to distribute
// we don't want to revert in case its zero to not block the staking contract
uint256 funds = rewardToken.balanceOf(address(this));

if (
rewardConfiguration.emissionRate != 0 &&
timeDelta != 0 &&
funds != 0
) {
rewards = rewardConfiguration.emissionRate * timeDelta;

// update the last update timestamp
Expand Down
18 changes: 7 additions & 11 deletions src/Staking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -374,17 +374,15 @@ contract Staking is ERC20VotesUpgradeable, Ownable2StepUpgradeable {
/// @param _rewardsDistributor The address of the rewards distributor contract
function setRewardsDistributor(
address _rewardsDistributor
) external onlyOwner updateRewards {
) external onlyOwner {
rewardsDistributor = IRewardsDistributor(_rewardsDistributor);

emit NewRewardsDistributor(_rewardsDistributor);
}

/// @notice Set the lock period
/// @param _lockPeriod The lock period in seconds
function setLockPeriod(
uint256 _lockPeriod
) external onlyOwner updateRewards {
function setLockPeriod(uint256 _lockPeriod) external onlyOwner {
lockPeriod = _lockPeriod;

emit NewLockPeriod(_lockPeriod);
Expand All @@ -401,10 +399,7 @@ contract Staking is ERC20VotesUpgradeable, Ownable2StepUpgradeable {
/// @notice Set a keyper
/// @param keyper The keyper address
/// @param isKeyper Whether the keyper is a keyper or not
function setKeyper(
address keyper,
bool isKeyper
) external onlyOwner updateRewards {
function setKeyper(address keyper, bool isKeyper) external onlyOwner {
_setKeyper(keyper, isKeyper);
}

Expand All @@ -414,7 +409,7 @@ contract Staking is ERC20VotesUpgradeable, Ownable2StepUpgradeable {
function setKeypers(
address[] memory _keypers,
bool isKeyper
) external onlyOwner updateRewards {
) external onlyOwner {
for (uint256 i = 0; i < _keypers.length; i++) {
_setKeyper(_keypers[i], isKeyper);
}
Expand Down Expand Up @@ -452,6 +447,7 @@ contract Staking is ERC20VotesUpgradeable, Ownable2StepUpgradeable {
/// @return The maximum amount of assets that a keyper can withdraw
function maxWithdraw(address keyper) public view virtual returns (uint256) {
uint256 shares = balanceOf(keyper);

require(shares > 0, KeyperHasNoShares());

uint256 assets = convertToAssets(shares);
Expand All @@ -461,7 +457,7 @@ contract Staking is ERC20VotesUpgradeable, Ownable2StepUpgradeable {
: minStake;

if (assets < compare) {
// TODO check this
// need this branch as convertToAssets rounds down
return 0;
} else {
return assets - compare;
Expand All @@ -481,7 +477,7 @@ contract Staking is ERC20VotesUpgradeable, Ownable2StepUpgradeable {
uint256 compare = locked >= minStake ? locked : minStake;

if (assets < compare) {
// TODO check this
// need this branch as convertToAssets rounds down
return 0;
} else {
return assets - compare;
Expand Down
116 changes: 93 additions & 23 deletions test/Staking.integration.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,6 @@ contract StakingIntegrationTest is Test {
(staking, rewardsDistributor) = deployScript.run();
}

function _boundUnlockedTime(uint256 _time) internal view returns (uint256) {
return
bound(
_time,
vm.getBlockTimestamp() + LOCK_PERIOD,
vm.getBlockTimestamp() + 105 weeks
);
}

function _boundRealisticTimeAhead(
uint256 _time
) internal pure returns (uint256) {
Expand All @@ -63,7 +54,7 @@ contract StakingIntegrationTest is Test {
IERC20(STAKING_TOKEN).transfer(address(rewardsDistributor), poolSize);
}

function _calculateAPR(
function _calculateReturnOverPrincipal(
uint256 _rewardsReceived,
uint256 _staked,
uint256 _days
Expand Down Expand Up @@ -124,24 +115,28 @@ contract StakingIntegrationTest is Test {

uint256 rewardsReceived = staking.claimRewards(0);

uint256 APR = _calculateAPR(rewardsReceived, staked, jump);
uint256 APR = _calculateReturnOverPrincipal(
rewardsReceived,
staked,
jump
);

// 1% error margin
assertApproxEqAbs(APR, 20e18, 1e18);
}

function testFork_FirstDepositorsAlwaysReceiveMoreRewards() public {
function testForkFuzz_MultipleDepositorsStakeMinAmountDifferentTimestamp(
uint256 _jump
) public {
uint256 depositorsCount = 400;

_setRewardAndFund();

uint256 jumpBetweenStakes = 1 hours;
_jump = bound(_jump, 1 minutes, 12 hours);

uint256[] memory timeStaked = new uint256[](depositorsCount);
uint256 previousDepositorShares;

uint256 timestampFirstStake = vm.getBlockTimestamp();

for (uint256 i = 1; i <= depositorsCount; i++) {
address participant = address(uint160(i));

Expand All @@ -163,12 +158,11 @@ contract StakingIntegrationTest is Test {

timeStaked[i - 1] = vm.getBlockTimestamp();

_jumpAhead(jumpBetweenStakes);
_jumpAhead(_jump);
}

uint256 previousRewardsReceived;

// collect rewards and calculate rewards
for (uint256 i = 1; i <= depositorsCount; i++) {
address participant = address(uint160(i));

Expand All @@ -185,16 +179,87 @@ contract StakingIntegrationTest is Test {
assertGt(rewardsReceived, previousRewardsReceived);
}

_jumpAhead(jumpBetweenStakes);

uint256 assetsAfter = staking.convertToAssets(
staking.balanceOf(participant)
);
assertApproxEqAbs(assetsAfter, MIN_STAKE, 2);
assertApproxEqAbs(assetsAfter, MIN_STAKE, 1e18);
}
}

function testFork_ClaimRewardsAtTheEndOfSemester() public {
_setRewardAndFund();

uint256 staked = (CIRCULATION_SUPPLY * 25) / 100;

deal(STAKING_TOKEN, address(this), staked);

vm.prank(CONTRACT_OWNER);
staking.setKeyper(address(this), true);

IERC20(STAKING_TOKEN).approve(address(staking), staked);
staking.stake(staked);

uint256 jump = 86 days;

_jumpAhead(jump);

vm.prank(CONTRACT_OWNER);
staking.setKeyper(address(1), true);

uint256 rewardsReceived = staking.claimRewards(0);

uint256 APR = _calculateReturnOverPrincipal(
rewardsReceived,
staked,
jump
);

// 1% error margin
assertApproxEqAbs(APR, 20e18, 1e18);
}

function testFork_ClaimRewardsEveryDayAndReestakeUntilEndSemester() public {
_setRewardAndFund();

uint256 staked = (CIRCULATION_SUPPLY * 25) / 100;

deal(STAKING_TOKEN, address(this), staked);

vm.prank(CONTRACT_OWNER);
staking.setKeyper(address(this), true);

IERC20(STAKING_TOKEN).approve(address(staking), staked);
staking.stake(staked);

uint256 previousTimestamp = vm.getBlockTimestamp();

for (uint256 i = 1; i < 2064; i++) {
_jumpAhead(1 hours);

previousTimestamp = vm.getBlockTimestamp();
uint256 rewardsReceived = staking.claimRewards(0);

IERC20(STAKING_TOKEN).approve(address(staking), rewardsReceived);
staking.stake(rewardsReceived);
}

_jumpAhead(1 hours);

uint256 assets = staking.convertToAssets(
staking.balanceOf(address(this))
);

uint256 APR = _calculateReturnOverPrincipal(
assets - staked,
staked,
86 days
);

// 1% error margin
assertApproxEqAbs(APR, 20e18, 1e18);
}

function testForkFuzz_MultipleDepositorsStakeMinStakeSameBlock(
function testForkFuzz_MultipleDepositorsStakeMinStakeSameTimestamp(
uint256 _depositorsCount,
uint256 _jump
) public {
Expand Down Expand Up @@ -226,8 +291,6 @@ contract StakingIntegrationTest is Test {
uint256 expectedRewardPerKeyper = expectedRewardsDistributed /
depositors.length;

uint256 APR = _calculateAPR(expectedRewardPerKeyper, MIN_STAKE, _jump);

_jumpAhead(_jump);

// collect rewards
Expand All @@ -240,6 +303,13 @@ contract StakingIntegrationTest is Test {
vm.stopPrank();

assertApproxEqAbs(rewards, expectedRewardPerKeyper, 0.1e18);

uint256 APR = _calculateReturnOverPrincipal(
rewards,
MIN_STAKE,
_jump
);
assertApproxEqAbs(APR, 20e18, 1e18);
}
}
}

0 comments on commit 98f4c90

Please sign in to comment.