-
Notifications
You must be signed in to change notification settings - Fork 0
Ribbon fuse pool #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: fuse-final
Are you sure you want to change the base?
Changes from all commits
23922b7
3c5bfe4
0b20ee0
89fe5c3
c1ed18e
05e9e12
3cede01
cd396bc
99db425
89b6d6f
688aac2
4684929
b753fc8
3fe0665
f45c116
31b9857
a9926a6
378a91f
55b258b
5645f70
7f63e3d
3ddbab1
a0640a2
50beda2
eedf42c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,35 @@ | ||
| pragma solidity ^0.5.16; | ||
|
|
||
| import "./CToken.sol"; | ||
| import "./EIP20Interface.sol"; | ||
|
|
||
| interface CompLike { | ||
| function delegate(address delegatee) external; | ||
| } | ||
|
|
||
| interface RibbonMinter { | ||
| function mint(address gauge_addr) external; | ||
| } | ||
|
|
||
| interface RewardsDistributor { | ||
| function burn(address cToken, uint256 amount, bool burnStables) external; | ||
| } | ||
|
|
||
| /** | ||
| * @title Compound's CErc20 Contract | ||
| * @notice CTokens which wrap an EIP-20 underlying | ||
| * @dev This contract should not to be deployed on its own; instead, deploy `CErc20Delegator` (proxy contract) and `CErc20Delegate` (logic/implementation contract). | ||
| * @author Compound | ||
| */ | ||
| contract CErc20 is CToken, CErc20Interface { | ||
| // Minter contract for rbn gauge emissions | ||
| RibbonMinter public constant RBN_MINTER = RibbonMinter(0x5B0655F938A72052c46d2e94D206ccB6FF625A3A); | ||
| // RBN token | ||
| EIP20Interface public constant RBN = EIP20Interface(0x6123B0049F904d730dB3C36a31167D9d4121fA6B); | ||
| // Rewards distributor | ||
| // https://github.com/Rari-Capital/compound-protocol/blob/fuse-final/contracts/RewardsDistributorDelegator.sol | ||
| RewardsDistributor public rewardsDistributor; | ||
|
|
||
| /** | ||
| * @notice Initialize the new money market | ||
| * @param underlying_ The address of the underlying asset | ||
|
|
@@ -178,4 +195,45 @@ contract CErc20 is CToken, CErc20Interface { | |
| require(hasAdminRights(), "only the admin may set the comp-like delegate"); | ||
| CompLike(underlying).delegate(compLikeDelegatee); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Admin call to set rewards distributor | ||
| * @param _rewardsDistributor The rewards contract | ||
| */ | ||
| function _setRewardsDistributor(address _rewardsDistributor) external { | ||
| require(hasAdminRights(), "only the admin may set the rewards distributor delegate"); | ||
| require(_rewardsDistributor != address(0), "rewards distributor must be set"); | ||
|
|
||
| rewardsDistributor = RewardsDistributor(_rewardsDistributor); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Anyone can claim gauge rewards for collateralized gauge tokens. | ||
| */ | ||
| function claimGaugeRewards() external { | ||
| require(address(rewardsDistributor) != address(0), "rewards distributor must be set"); | ||
|
|
||
| // Underlying is the gauge token like rETH-THETA-gauge | ||
| RBN_MINTER.mint(underlying); | ||
|
|
||
| uint256 toDistribute = RBN.balanceOf(address(this)); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can add these lines to return early if (toDistribute == 0) {
return;
} |
||
|
|
||
| if (toDistribute == 0) { | ||
| return; | ||
| } | ||
|
|
||
| RBN.approve(address(rewardsDistributor), toDistribute); | ||
|
|
||
| /* | ||
| * Transfer rewards to reward distributor which will distribute rewards | ||
| * to those who supply / borrow. The reason we need to do this way is | ||
| * once individuals transfer the collateral (gauge tokens) to the cToken | ||
| * contract, they forfeit their rewards and now the cToken starts accumulating | ||
| * rewards. We want to redistribute some of it back to those supplying | ||
| * gauge tokens as collateral who 'should' be getting those rewards, and some | ||
| * to DAI / USDC suppliers | ||
| */ | ||
|
|
||
| rewardsDistributor.burn(address(this), toDistribute, true); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,15 +4,43 @@ import "./CToken.sol"; | |
| import "./ExponentialNoError.sol"; | ||
| import "./Comptroller.sol"; | ||
| import "./RewardsDistributorStorage.sol"; | ||
| import "./SafeMath.sol"; | ||
| import "./EIP20Interface.sol"; | ||
|
|
||
| /** | ||
| * @title RewardsDistributorDelegate (COMP distribution logic extracted from `Comptroller`) | ||
| * @author Compound | ||
| */ | ||
| contract RewardsDistributorDelegate is RewardsDistributorDelegateStorageV1, ExponentialNoError { | ||
| using SafeMath for uint; | ||
|
|
||
| /// @dev Notice that this contract is a RewardsDistributor | ||
| bool public constant isRewardsDistributor = true; | ||
|
|
||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tests for this |
||
| /// @dev WEEK | ||
| uint256 public constant WEEK = 604800; | ||
|
|
||
| /// @dev 100% | ||
| uint256 public constant TOTAL_PCT = 10000; | ||
|
|
||
| /// @dev Start of rewards epoch | ||
| uint256 public start; | ||
|
|
||
| /// @dev AVG blocks per week. Each block is on avg 13s = WEEK / 13 | ||
| uint256 public avgBlocksPerWeek; | ||
|
|
||
| /// @dev total RBN minted | ||
| mapping(address => uint256) public totalMint; | ||
| /// @dev total RBN minted from last epoch | ||
| mapping(address => uint256) public lastEpochTotalMint; | ||
| /// @dev Borrow reward % | ||
| mapping(address => uint256) public borrowerPCT; | ||
| /// @dev Supply reward % | ||
| mapping(address => uint256) public supplierPCT; | ||
|
|
||
| mapping(address => uint256) public startTime; | ||
| CToken[] public stables; | ||
|
|
||
| /// @notice Emitted when pendingAdmin is changed | ||
| event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin); | ||
|
|
||
|
|
@@ -37,15 +65,38 @@ contract RewardsDistributorDelegate is RewardsDistributorDelegateStorageV1, Expo | |
| /// @notice Emitted when COMP is granted by admin | ||
| event CompGranted(address recipient, uint amount); | ||
|
|
||
| /// @notice Emitted when new borrow pct set for cToken | ||
| event NewBorrowerPCT(CToken indexed cToken, uint256 newPCT); | ||
|
|
||
| /// @notice Emitted when new supply pct set for cToken | ||
| event NewSupplierPCT(CToken indexed cToken, uint256 newPCT); | ||
|
|
||
| /// @notice Emitted when average blocks per week updated | ||
| event NewAverageBlocksPerWeek(uint256 newBlocksPerWeek); | ||
|
|
||
| /// @notice Emitted when new stables reward asset added | ||
| event NewStableCToken(CToken indexed cToken); | ||
|
|
||
| /// @notice Emitted when asset recovered | ||
| event RecoverAsset(address asset, uint256 amount); | ||
|
|
||
| /// @notice Emitted when RBN sent to contract for rewards on behalf of cToken | ||
| event Burn(CToken indexed cToken, uint256 amount); | ||
|
|
||
|
|
||
| /// @notice The initial COMP index for a market | ||
| uint224 public constant compInitialIndex = 1e36; | ||
|
|
||
| /// @dev Intitializer to set admin to caller and set reward token | ||
| function initialize(address _rewardToken) external { | ||
| /// @dev Intitializer to set admin to caller and set reward token and start time of rewards | ||
| function initialize(address _rewardToken, uint256 _start) external { | ||
| require(msg.sender == admin, "Only admin can initialize."); | ||
| require(rewardToken == address(0), "Already initialized."); | ||
| require(_rewardToken != address(0), "Cannot initialize reward token to the zero address."); | ||
| require(_start != 0, "Cannot initialize start time to 0."); | ||
|
|
||
| rewardToken = _rewardToken; | ||
| start = _start; | ||
| avgBlocksPerWeek = WEEK.div(13); | ||
| } | ||
|
|
||
| /*** Set Admin ***/ | ||
|
|
@@ -106,7 +157,7 @@ contract RewardsDistributorDelegate is RewardsDistributorDelegateStorageV1, Expo | |
| // Make sure distributor is added | ||
| bool distributorAdded = false; | ||
| address[] memory distributors = comptroller.getRewardsDistributors(); | ||
| for (uint256 i = 0; i < distributors.length; i++) if (distributors[i] == address(this)) distributorAdded = true; | ||
| for (uint256 i = 0; i < distributors.length; i++) if (distributors[i] == address(this)) distributorAdded = true; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Gas optimization for loop:
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is distributors.length really calculated multiple times? this code was not changed its from original comp |
||
| require(distributorAdded == true, "distributor not added"); | ||
| } | ||
|
|
||
|
|
@@ -435,6 +486,136 @@ contract RewardsDistributorDelegate is RewardsDistributorDelegateStorageV1, Expo | |
| setCompBorrowSpeedInternal(cToken, compSpeed); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Set borrower PCT | ||
| * @param cToken The market whose borrower PCT to update | ||
| * @param _borrowerPCT Borrower PCT | ||
| */ | ||
| function _setBorrowerPCT(CToken cToken, uint256 _borrowerPCT) public { | ||
| if(startTime[address(cToken)] == 0){ | ||
| startTime[address(cToken)] = start; | ||
| } | ||
| require(msg.sender == admin, "only admin can set borrower percent"); | ||
| require(_borrowerPCT.add(supplierPCT[address(cToken)]) <= TOTAL_PCT, "Borrow + Supply PCT > 100%"); | ||
| borrowerPCT[address(cToken)] = _borrowerPCT; | ||
| emit NewBorrowerPCT(cToken, _borrowerPCT); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Set supply PCT | ||
| * @param cToken The market whose borrower PCT to update | ||
| * @param _supplierPCT Supplier PCT | ||
| */ | ||
| function _setSupplierPCT(CToken cToken, uint256 _supplierPCT) public { | ||
| if(startTime[address(cToken)] == 0){ | ||
| startTime[address(cToken)] = start; | ||
| } | ||
| require(msg.sender == admin, "only admin can set supplier percent"); | ||
| require(borrowerPCT[address(cToken)].add(_supplierPCT) <= TOTAL_PCT, "Borrow + Supply PCT > 100%"); | ||
| supplierPCT[address(cToken)] = _supplierPCT; | ||
| emit NewSupplierPCT(cToken, _supplierPCT); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Set average block time. Each block will be exactly 12 seconds after merge | ||
| */ | ||
| function _setAvgBlocksPerWeek(uint256 _avgBlocksPerWeek) public { | ||
| require(msg.sender == admin, "only admin can set avg blocks per week"); | ||
| avgBlocksPerWeek = _avgBlocksPerWeek; | ||
| emit NewAverageBlocksPerWeek(_avgBlocksPerWeek); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Add Stables Asset | ||
| */ | ||
| function _addStable(CToken cToken) public { | ||
| require(msg.sender == admin, "only admin can add stable asset for rewards"); | ||
| uint256 len = stables.length; | ||
| for (uint256 i; i < len; i++) { | ||
| if(stables[i] == cToken){ | ||
| return; | ||
| } | ||
| } | ||
| stables.push(cToken); | ||
| emit NewStableCToken(cToken); | ||
| } | ||
|
|
||
| /** | ||
| * @notice | ||
| * recover specific asset | ||
| * @param asset asset to recover | ||
| * @param amount amount to recover | ||
| */ | ||
| function _recoverAsset(address asset, uint256 amount) public { | ||
| require(asset != address(0), "!asset"); | ||
| require(msg.sender == admin, "only admin can recover asset"); | ||
| EIP20Interface(asset).transfer(admin, amount); | ||
| emit RecoverAsset(asset, amount); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Set new borrow / supply speed | ||
| * @param cToken The market whose COMP speed to update | ||
| */ | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. _updateSpeedWithNewEpoch needs to be called from cron job weekly |
||
| function updateSpeedWithNewEpoch(CToken cToken) external { | ||
| require( | ||
| block.timestamp.sub(startTime[address(cToken)]) >= WEEK, | ||
| "Must be at least week since latest epoch" | ||
| ); | ||
| uint256 totalToDistribute = totalMint[address(cToken)] | ||
| .sub(lastEpochTotalMint[address(cToken)]) | ||
| .div(avgBlocksPerWeek); | ||
| uint256 toDistributeToBorrower = totalToDistribute | ||
| .mul(borrowerPCT[address(cToken)]) | ||
| .div(TOTAL_PCT); | ||
| uint256 toDistributeToSupplier = totalToDistribute | ||
| .mul(supplierPCT[address(cToken)]) | ||
| .div(TOTAL_PCT); | ||
| lastEpochTotalMint[address(cToken)] = totalMint[address(cToken)]; | ||
| startTime[address(cToken)] = startTime[address(cToken)].add(WEEK); | ||
| setCompBorrowSpeedInternal(cToken, toDistributeToBorrower); | ||
| setCompSupplySpeedInternal(cToken, toDistributeToSupplier); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Burn | ||
| * @param cToken cToken to burn for | ||
| * @param amount Amount of RBN tokens | ||
| * @param burnStables Are we burning some RBN for stables rewards | ||
| */ | ||
| function burn(CToken cToken, uint256 amount, bool burnStables) external { | ||
| require(amount > 0, "!amount > 0"); | ||
|
|
||
| EIP20Interface(rewardToken).transferFrom(msg.sender, address(this), amount); | ||
|
|
||
| totalMint[address(cToken)] = totalMint[address(cToken)].add(amount); | ||
|
|
||
| if(burnStables){ | ||
| _burnStables(cToken, amount); | ||
| } | ||
|
|
||
| emit Burn(cToken, amount); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Burn Stables | ||
| * @param cToken cToken that sources rewards | ||
| * @param amount Amount of RBN tokens | ||
| */ | ||
| function _burnStables(CToken cToken, uint256 amount) internal { | ||
| uint256 len = stables.length; | ||
| uint256 amountForStablesTotal = amount.mul(TOTAL_PCT.sub(supplierPCT[address(cToken)]).sub(borrowerPCT[address(cToken)])).div(TOTAL_PCT); | ||
|
|
||
| if(len > 0 && amountForStablesTotal > 0){ | ||
| uint256 amountForStable = amountForStablesTotal.div(len); | ||
| for (uint256 i; i < len; i++) { | ||
| CToken stable = stables[i]; | ||
| totalMint[address(stable)] = totalMint[address(stable)].add(amountForStable); | ||
| emit Burn(stable, amountForStable); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @notice Set COMP borrow and supply speeds for the specified markets. | ||
| * @param cTokens The markets whose COMP speed to update. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tests for this