Skip to content

Synthetix staking rewards#68

Merged
Joeysantoro merged 7 commits intoReleasefrom
POZ-Synthetix-StakingRewards
Feb 18, 2021
Merged

Synthetix staking rewards#68
Joeysantoro merged 7 commits intoReleasefrom
POZ-Synthetix-StakingRewards

Conversation

@Joeysantoro
Copy link
Contributor

@Joeysantoro Joeysantoro commented Feb 16, 2021

Update the Pool contracts to use Synthetix StakingRewards instead.

Basically split out the pool logic related to stake, claim, withdraw and instead use FeiStakingRewards which wraps StakingRewardsV2. We can assume this contract works as intended.

The risk is in FeiRewardsDistributor which inherits the distribution schedule logic from the old FeiPool + Pool contracts. The primary risk of the change is in the drip() function of FeiRewardsDistributor as well as double checking all of the wiring.

@Joeysantoro Joeysantoro force-pushed the POZ-Synthetix-StakingRewards branch from 1ebf180 to 4276123 Compare February 16, 2021 07:11
@Joeysantoro Joeysantoro force-pushed the POZ-Synthetix-StakingRewards branch from 2946aca to f14ffae Compare February 16, 2021 22:16
@@ -0,0 +1,12 @@
pragma solidity ^0.6.6;

abstract contract RewardsDistributionRecipient {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of the box contract


// forked from https://github.com/SetProtocol/index-coop-contracts/blob/master/contracts/staking/StakingRewardsV2.sol
// NOTE: V2 allows setting of rewardsDuration in constructor
contract StakingRewardsV2 is RewardsDistributionRecipient, ReentrancyGuard {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of the box contract with a minor change

Comment on lines +135 to +142
// Added to support recovering LP Rewards from other systems such as BAL to be distributed to holders
function recoverERC20(address tokenAddress, address to, uint256 tokenAmount) external onlyRewardsDistribution {
require(tokenAddress != address(stakingToken), "Cannot withdraw the staking token");
require(tokenAddress != address(rewardsToken), "Cannot withdraw the rewards token");

IERC20(tokenAddress).safeTransfer(to, tokenAmount);
emit Recovered(tokenAddress, to, tokenAmount);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this method from StakingRewards v1

import "./IDOInterface.sol";
import "../utils/Timed.sol";
import "../refs/CoreRef.sol";
import "../pool/IPool.sol";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed Pool init logic because now we can simply drip publicly from the FeiRewardsDistributor post the first dripFrequency window. This removes the risk of the new stakingRewards contract bricking launch


uint256 public constant POOL_DURATION = 2 * 365 days;
uint256 public constant THAWING_DURATION = 4 weeks;
uint256 public constant DRIP_FREQUENCY = 1 weeks;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is both the frequency with which FeiRewardsDistributor can drip and the expected duration of FeiStakingRewards

Comment on lines +289 to +292
core.grantMinter(feiRewardsDistributor);

IRewardsDistributor(feiRewardsDistributor).setStakingContract(feiStakingRewards);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set the distributor as a minter to incentivize drips, and complete the wiring

pragma solidity ^0.6.0;

import "@openzeppelin/contracts/access/Ownable.sol";
import "../pool/FeiPool.sol";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove pool from this orchestrator to make a separate one

/// @title A StakingRewards contract for earning TRIBE with staked FEI/TRIBE LP tokens
/// @author Fei Protocol
/// @notice deposited LP tokens will earn TRIBE over time at a linearly decreasing rate
contract FeiStakingRewards is StakingRewardsV2 {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

simple wrapper around StakingRewardsV2, not really necessary

Comment on lines +109 to +156
/// @notice the total amount of rewards owned by contract and unlocked for release
function releasedReward() public view override returns (uint256) {
uint256 total = rewardBalance();
uint256 unreleased = unreleasedReward();
return total.sub(unreleased, "Pool: Released Reward underflow");
}

/// @notice the total amount of rewards distributed by the contract over entire period
function totalReward() public view override returns (uint256) {
return rewardBalance().add(distributedRewards);
}

/// @notice the total balance of rewards owned by contract, locked or unlocked
function rewardBalance() public view override returns (uint256) {
return tribeBalance();
}

/// @notice the total amount of rewards owned by contract and locked
function unreleasedReward() public view override returns (uint256) {
if (isTimeEnded()) {
return 0;
}

return
_unreleasedReward(
totalReward(),
duration,
timeSinceStart()
);
}

// Represents the integral of 2R/d - 2R/d^2 x dx from t to d
// Integral equals 2Rx/d - Rx^2/d^2
// Evaluated at t = 2R*t/d (start) - R*t^2/d^2 (end)
// Evaluated at d = 2R - R = R
// Solution = R - (start - end) or equivalently end + R - start (latter more convenient to code)
function _unreleasedReward(
uint256 _totalReward,
uint256 _duration,
uint256 _time
) internal pure returns (uint256) {
// 2R*t/d
Decimal.D256 memory start =
Decimal.ratio(_totalReward, _duration).mul(2).mul(_time);

// R*t^2/d^2
Decimal.D256 memory end =
Decimal.ratio(_totalReward, _duration).div(_duration).mul(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

audited logic from Pool.sol and FeiPool.sol for calculating the amount distributed over a window

Comment on lines +51 to +70
function drip() public override postGenesis returns(uint256) {
require(!killSwitch, "FeiRewardsDistributor: drip disabled");

// solhint-disable-next-line not-rely-on-time
require(block.timestamp >= nextDripAvailable(), "FeiRewardsDistributor: Not passed drip frequency");
// solhint-disable-next-line not-rely-on-time
lastDistributionTime = block.timestamp;

uint amount = releasedReward();
require(amount != 0, "FeiRewardsDistributor: no rewards");
distributedRewards = distributedRewards.add(amount);

tribe().transfer(address(stakingContract), amount);
stakingContract.notifyRewardAmount(amount);

_incentivize();

emit Drip(msg.sender, amount);
return amount;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main risk in the change is this method

@Joeysantoro Joeysantoro changed the base branch from POZ-Fixes to Release February 18, 2021 20:38
@Joeysantoro Joeysantoro merged commit 1bc5edf into Release Feb 18, 2021
@xklob xklob deleted the POZ-Synthetix-StakingRewards branch September 19, 2021 18:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments