Skip to content

Commit

Permalink
Merge branch 'save-v2'
Browse files Browse the repository at this point in the history
  • Loading branch information
alsco77 committed Jan 18, 2021
2 parents f0dfa3d + d890f05 commit 19a3a2e
Show file tree
Hide file tree
Showing 43 changed files with 6,983 additions and 513 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ module.exports = {
}
},
"rules": {
"@typescript-eslint/no-use-before-define": 1
"@typescript-eslint/no-use-before-define": 1,
"no-nested-ternary": 0
},
"overrides": [
{
Expand Down
100 changes: 100 additions & 0 deletions contracts/interfaces/IBoostedVaultWithLockup.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
pragma solidity 0.5.16;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IBoostedVaultWithLockup {

/**
* @dev Stakes a given amount of the StakingToken for the sender
* @param _amount Units of StakingToken
*/
function stake(uint256 _amount) external;

/**
* @dev Stakes a given amount of the StakingToken for a given beneficiary
* @param _beneficiary Staked tokens are credited to this address
* @param _amount Units of StakingToken
*/
function stake(address _beneficiary, uint256 _amount) external;

/**
* @dev Withdraws stake from pool and claims any unlocked rewards.
* Note, this function is costly - the args for _claimRewards
* should be determined off chain and then passed to other fn
*/
function exit() external;

/**
* @dev Withdraws stake from pool and claims any unlocked rewards.
* @param _first Index of the first array element to claim
* @param _last Index of the last array element to claim
*/
function exit(uint256 _first, uint256 _last) external;

/**
* @dev Withdraws given stake amount from the pool
* @param _amount Units of the staked token to withdraw
*/
function withdraw(uint256 _amount) external;

/**
* @dev Claims only the tokens that have been immediately unlocked, not including
* those that are in the lockers.
*/
function claimReward() external;

/**
* @dev Claims all unlocked rewards for sender.
* Note, this function is costly - the args for _claimRewards
* should be determined off chain and then passed to other fn
*/
function claimRewards() external;

/**
* @dev Claims all unlocked rewards for sender. Both immediately unlocked
* rewards and also locked rewards past their time lock.
* @param _first Index of the first array element to claim
* @param _last Index of the last array element to claim
*/
function claimRewards(uint256 _first, uint256 _last) external;

/**
* @dev Pokes a given account to reset the boost
*/
function pokeBoost(address _account) external;

/**
* @dev Gets the RewardsToken
*/
function getRewardToken() external view returns (IERC20);

/**
* @dev Gets the last applicable timestamp for this reward period
*/
function lastTimeRewardApplicable() external view returns (uint256);

/**
* @dev Calculates the amount of unclaimed rewards per token since last update,
* and sums with stored to give the new cumulative reward per token
* @return 'Reward' per staked token
*/
function rewardPerToken() external view returns (uint256);

/**
* @dev Returned the units of IMMEDIATELY claimable rewards a user has to receive. Note - this
* does NOT include the majority of rewards which will be locked up.
* @param _account User address
* @return Total reward amount earned
*/
function earned(address _account) external view returns (uint256);

/**
* @dev Calculates all unclaimed reward data, finding both immediately unlocked rewards
* and those that have passed their time lock.
* @param _account User address
* @return amount Total units of unclaimed rewards
* @return first Index of the first userReward that has unlocked
* @return last Index of the last userReward that has unlocked
*/
function unclaimedRewards(address _account) external view returns (uint256 amount, uint256 first, uint256 last);
}
6 changes: 3 additions & 3 deletions contracts/interfaces/IMStableHelper.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pragma solidity 0.5.16;

import { ISavingsContract } from "./ISavingsContract.sol";
import { ISavingsContractV1 } from "./ISavingsContract.sol";

interface IMStableHelper {

Expand Down Expand Up @@ -96,7 +96,7 @@ interface IMStableHelper {
* @return balance in Masset units
*/
function getSaveBalance(
ISavingsContract _save,
ISavingsContractV1 _save,
address _user
)
external
Expand All @@ -113,7 +113,7 @@ interface IMStableHelper {
* @return input for the redeem function (ie. credit units to redeem)
*/
function getSaveRedeemInput(
ISavingsContract _save,
ISavingsContractV1 _save,
uint256 _amount
)
external
Expand Down
33 changes: 26 additions & 7 deletions contracts/interfaces/ISavingsContract.sol
Original file line number Diff line number Diff line change
@@ -1,18 +1,37 @@
pragma solidity 0.5.16;

/**
* @title ISavingsContract
*/
interface ISavingsContract {

/** @dev Manager privs */
interface ISavingsContractV1 {
function depositInterest(uint256 _amount) external;

/** @dev Saver privs */
function depositSavings(uint256 _amount) external returns (uint256 creditsIssued);
function redeem(uint256 _amount) external returns (uint256 massetReturned);

/** @dev Getters */
function exchangeRate() external view returns (uint256);
function creditBalances(address) external view returns (uint256);
}

interface ISavingsContractV2 {

// DEPRECATED but still backwards compatible
function redeem(uint256 _amount) external returns (uint256 massetReturned);
function creditBalances(address) external view returns (uint256); // V1 & V2 (use balanceOf)

// --------------------------------------------

function depositInterest(uint256 _amount) external; // V1 & V2

function depositSavings(uint256 _amount) external returns (uint256 creditsIssued); // V1 & V2
function depositSavings(uint256 _amount, address _beneficiary) external returns (uint256 creditsIssued); // V2

function redeemCredits(uint256 _amount) external returns (uint256 underlyingReturned); // V2
function redeemUnderlying(uint256 _amount) external returns (uint256 creditsBurned); // V2

function exchangeRate() external view returns (uint256); // V1 & V2

function balanceOfUnderlying(address _user) external view returns (uint256 balance); // V2

function underlyingToCredits(uint256 _credits) external view returns (uint256 underlying); // V2
function creditsToUnderlying(uint256 _underlying) external view returns (uint256 credits); // V2

}
1 change: 1 addition & 0 deletions contracts/masset/liquidator/ICurveMetaPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ pragma solidity 0.5.16;

interface ICurveMetaPool {
function exchange_underlying(int128 i, int128 j, uint256 dx, uint256 min_dy) external returns (uint256);
function get_dy(int128 i, int128 j, uint256 dx) external view returns (uint256);
}
10 changes: 8 additions & 2 deletions contracts/masset/liquidator/IUniswapV2Router02.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@ pragma solidity ^0.5.16;
interface IUniswapV2Router02 {
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
uint amountOutMin, // calculated off chain
address[] calldata path, // also worked out off chain
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapExactETHForTokens(
uint amountOutMin,
address[] calldata path,
address to, uint deadline
) external payable returns (uint[] memory amounts);
function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
}
6 changes: 3 additions & 3 deletions contracts/masset/shared/MStableHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma experimental ABIEncoderV2;
import { IMasset } from "../../interfaces/IMasset.sol";
import { IBasketManager } from "../../interfaces/IBasketManager.sol";
import { IMStableHelper } from "../../interfaces/IMStableHelper.sol";
import { ISavingsContract } from "../../interfaces/ISavingsContract.sol";
import { ISavingsContractV1 } from "../../interfaces/ISavingsContract.sol";
import { IForgeValidator } from "../forge-validator/IForgeValidator.sol";

import { MassetStructs } from "./MassetStructs.sol";
Expand Down Expand Up @@ -279,7 +279,7 @@ contract MStableHelper is IMStableHelper, MassetStructs {
* @return balance in Masset units
*/
function getSaveBalance(
ISavingsContract _save,
ISavingsContractV1 _save,
address _user
)
external
Expand All @@ -306,7 +306,7 @@ contract MStableHelper is IMStableHelper, MassetStructs {
* @return input for the redeem function (ie. credit units to redeem)
*/
function getSaveRedeemInput(
ISavingsContract _save,
ISavingsContractV1 _save,
uint256 _mAssetUnits
)
external
Expand Down
47 changes: 47 additions & 0 deletions contracts/rewards/InitializableRewardsDistributionRecipient.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
pragma solidity 0.5.16;

import { InitializableModule2 } from "../shared/InitializableModule2.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IRewardsDistributionRecipient } from "../interfaces/IRewardsDistributionRecipient.sol";

/**
* @title RewardsDistributionRecipient
* @author Originally: Synthetix (forked from /Synthetixio/synthetix/contracts/RewardsDistributionRecipient.sol)
* Changes by: Stability Labs Pty. Ltd.
* @notice RewardsDistributionRecipient gets notified of additional rewards by the rewardsDistributor
* @dev Changes: Addition of Module and abstract `getRewardToken` func + cosmetic
*/
contract InitializableRewardsDistributionRecipient is IRewardsDistributionRecipient, InitializableModule2 {

// @abstract
function notifyRewardAmount(uint256 reward) external;
function getRewardToken() external view returns (IERC20);

// This address has the ability to distribute the rewards
address public rewardsDistributor;

/** @dev Recipient is a module, governed by mStable governance */
function _initialize(address _nexus, address _rewardsDistributor) internal {
InitializableModule2._initialize(_nexus);
rewardsDistributor = _rewardsDistributor;
}

/**
* @dev Only the rewards distributor can notify about rewards
*/
modifier onlyRewardsDistributor() {
require(msg.sender == rewardsDistributor, "Caller is not reward distributor");
_;
}

/**
* @dev Change the rewardsDistributor - only called by mStable governor
* @param _rewardsDistributor Address of the new distributor
*/
function setRewardsDistribution(address _rewardsDistributor)
external
onlyGovernor
{
rewardsDistributor = _rewardsDistributor;
}
}
52 changes: 40 additions & 12 deletions contracts/rewards/staking/StakingRewards.sol
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,14 @@ contract StakingRewards is StakingTokenWrapper, RewardsDistributionRecipient {
/** @dev Updates the reward for a given address, before executing function */
modifier updateReward(address _account) {
// Setting of global vars
uint256 newRewardPerToken = rewardPerToken();
(uint256 newRewardPerToken, uint256 lastApplicableTime) = _rewardPerToken();
// If statement protects against loss in initialisation case
if(newRewardPerToken > 0) {
rewardPerTokenStored = newRewardPerToken;
lastUpdateTime = lastTimeRewardApplicable();
lastUpdateTime = lastApplicableTime;
// Setting of personal vars based on new globals
if (_account != address(0)) {
rewards[_account] = earned(_account);
rewards[_account] = _earned(_account, newRewardPerToken);
userRewardPerTokenPaid[_account] = newRewardPerToken;
}
}
Expand Down Expand Up @@ -192,17 +192,33 @@ contract StakingRewards is StakingTokenWrapper, RewardsDistributionRecipient {
view
returns (uint256)
{
// If there is no StakingToken liquidity, avoid div(0)
uint256 stakedTokens = totalSupply();
if (stakedTokens == 0) {
return rewardPerTokenStored;
(uint256 rewardPerToken_, ) = _rewardPerToken();
return rewardPerToken_;
}

function _rewardPerToken()
internal
view
returns (uint256 rewardPerToken_, uint256 lastTimeRewardApplicable_)
{
uint256 lastApplicableTime = lastTimeRewardApplicable(); // + 1 SLOAD
uint256 timeDelta = lastApplicableTime.sub(lastUpdateTime); // + 1 SLOAD
// If this has been called twice in the same block, shortcircuit to reduce gas
if(timeDelta == 0) {
return (rewardPerTokenStored, lastApplicableTime);
}
// new reward units to distribute = rewardRate * timeSinceLastUpdate
uint256 rewardUnitsToDistribute = rewardRate.mul(lastTimeRewardApplicable().sub(lastUpdateTime));
uint256 rewardUnitsToDistribute = rewardRate.mul(timeDelta); // + 1 SLOAD
uint256 supply = totalSupply(); // + 1 SLOAD
// If there is no StakingToken liquidity, avoid div(0)
// If there is nothing to distribute, short circuit
if (supply == 0 || rewardUnitsToDistribute == 0) {
return (rewardPerTokenStored, lastApplicableTime);
}
// new reward units per token = (rewardUnitsToDistribute * 1e18) / totalTokens
uint256 unitsToDistributePerToken = rewardUnitsToDistribute.divPrecisely(stakedTokens);
uint256 unitsToDistributePerToken = rewardUnitsToDistribute.divPrecisely(supply);
// return summed rate
return rewardPerTokenStored.add(unitsToDistributePerToken);
return (rewardPerTokenStored.add(unitsToDistributePerToken), lastApplicableTime); // + 1 SLOAD
}

/**
Expand All @@ -214,11 +230,23 @@ contract StakingRewards is StakingTokenWrapper, RewardsDistributionRecipient {
public
view
returns (uint256)
{
return _earned(_account, rewardPerToken());
}

function _earned(address _account, uint256 _currentRewardPerToken)
internal
view
returns (uint256)
{
// current rate per token - rate user previously received
uint256 userRewardDelta = rewardPerToken().sub(userRewardPerTokenPaid[_account]);
uint256 userRewardDelta = _currentRewardPerToken.sub(userRewardPerTokenPaid[_account]); // + 1 SLOAD
// Short circuit if there is nothing new to distribute
if(userRewardDelta == 0){
return rewards[_account];
}
// new reward = staked tokens * difference in rate
uint256 userNewReward = balanceOf(_account).mulTruncate(userRewardDelta);
uint256 userNewReward = balanceOf(_account).mulTruncate(userRewardDelta); // + 1 SLOAD
// add to previous rewards
return rewards[_account].add(userNewReward);
}
Expand Down
Loading

0 comments on commit 19a3a2e

Please sign in to comment.