Skip to content

Commit

Permalink
SY.buyBond() returns gain
Browse files Browse the repository at this point in the history
BondModel is pure, and receives dependancies as params
YieldOracle no longer depends on currentCumulatives()
computations are now based on cToken.exchangeRateCurrent()
  • Loading branch information
popra committed Mar 3, 2021
1 parent 557395f commit 6a2b956
Show file tree
Hide file tree
Showing 14 changed files with 103 additions and 83 deletions.
4 changes: 3 additions & 1 deletion contracts/IController.sol
Expand Up @@ -4,7 +4,7 @@ pragma abicoder v2;

import "./Governed.sol";

contract IController is Governed {
abstract contract IController is Governed {

address public oracle; // IYieldOracle

Expand Down Expand Up @@ -99,4 +99,6 @@ contract IController is Governed {
{
feesOwner = newVal_;
}

function providerRatePerDay() external virtual returns (uint256);
}
2 changes: 1 addition & 1 deletion contracts/IProvider.sol
Expand Up @@ -23,6 +23,6 @@ interface IProvider {
function transferFees() external;

// current total underlying balance as measured by the provider pool
function underlyingBalance() external view returns (uint256);
function underlyingBalance() external returns (uint256);

}
16 changes: 7 additions & 9 deletions contracts/ISmartYield.sol
Expand Up @@ -36,7 +36,7 @@ interface ISmartYield {

function currentTime() external view returns(uint256);

function buyBond(uint256 principalAmount_, uint256 minGain_, uint256 deadline_, uint16 forDays_) external;
function buyBond(uint256 principalAmount_, uint256 minGain_, uint256 deadline_, uint16 forDays_) external returns (uint256);

function redeemBond(uint256 bondId_) external;

Expand All @@ -56,7 +56,7 @@ interface ISmartYield {
/**
* token purchase price
*/
function price() external view returns (uint256);
function price() external returns (uint256);

function abondPaid() external view returns (uint256);

Expand All @@ -67,18 +67,16 @@ interface ISmartYield {
/**
* @notice current total underlying balance, without accruing interest
*/
function underlyingTotal() external view returns (uint256);
function underlyingTotal() external returns (uint256);

/**
* @notice current underlying loanable, without accruing interest
*/
function underlyingLoanable() external view returns (uint256);
function underlyingLoanable() external returns (uint256);

function underlyingJuniors() external view returns (uint256);
function underlyingJuniors() external returns (uint256);

function providerRatePerDay() external view returns (uint256);
function bondGain(uint256 principalAmount_, uint16 forDays_) external returns (uint256);

function bondGain(uint256 principalAmount_, uint16 forDays_) external view returns (uint256);

function maxBondDailyRate() external view returns (uint256);
function maxBondDailyRate() external returns (uint256);
}
59 changes: 31 additions & 28 deletions contracts/SmartYield.sol
Expand Up @@ -193,13 +193,15 @@ contract SmartYield is
}

// Purchase a senior bond with principalAmount_ underlying for forDays_, buyer gets a bond with gain >= minGain_ or revert. deadline_ is timestamp before which tx is not rejected.
// returns gain
function buyBond(
uint256 principalAmount_,
uint256 minGain_,
uint256 deadline_,
uint16 forDays_
)
external override
returns (uint256)
{
_beforeProviderOp();

Expand All @@ -218,6 +220,15 @@ contract SmartYield is
"SY: buyBond forDays"
);

uint256 issuedAt = this.currentTime();

// ---

address buyer = msg.sender;

IProvider(pool)._takeUnderlying(buyer, principalAmount_);
IProvider(pool)._depositProvider(principalAmount_, 0);

uint256 gain = this.bondGain(principalAmount_, forDays_);

require(
Expand All @@ -235,15 +246,6 @@ contract SmartYield is
"SY: buyBond underlyingLoanable"
);

uint256 issuedAt = this.currentTime();

// ---

address buyer = msg.sender;

IProvider(pool)._takeUnderlying(buyer, principalAmount_);
IProvider(pool)._depositProvider(principalAmount_, 0);

SeniorBond memory b =
SeniorBond(
principalAmount_,
Expand All @@ -256,6 +258,8 @@ contract SmartYield is
_mintBond(buyer, b);

emit BuySeniorBond(buyer, seniorBondId, principalAmount_, gain, forDays_);

return gain;
}

// buy an nft with tokenAmount_ jTokens, that matures at abond maturesAt
Expand Down Expand Up @@ -371,32 +375,31 @@ contract SmartYield is
emit RedeemJuniorBond(payTo, jBondId_, payAmnt);
}


function providerRatePerDay()
external view virtual override
returns (uint256)
{
return MathUtils.min(
IController(controller).BOND_MAX_RATE_PER_DAY(),
IYieldOracle(IController(controller).oracle()).consult(1 days)
);
}

// given a principal amount and a number of days, compute the guaranteed bond gain, excluding principal
function bondGain(uint256 principalAmount_, uint16 forDays_)
external view override
external override
returns (uint256)
{
return IBondModel(IController(controller).bondModel()).gain(address(this), principalAmount_, forDays_);
return IBondModel(IController(controller).bondModel()).gain(
this.underlyingTotal(),
this.underlyingLoanable(),
IController(controller).providerRatePerDay(),
principalAmount_,
forDays_
);
}

// returns the maximum theoretically possible daily rate for senior bonds,
// in reality the actual rate given to a bond will always be lower due to slippage
function maxBondDailyRate()
external view override
external override
returns (uint256)
{
return IBondModel(IController(controller).bondModel()).maxDailyRate(address(this));
return IBondModel(IController(controller).bondModel()).maxDailyRate(
this.underlyingTotal(),
this.underlyingLoanable(),
IController(controller).providerRatePerDay()
);
}

// /externals
Expand All @@ -413,29 +416,29 @@ contract SmartYield is

// jToken price * 1e18
function price()
public view override
public override
returns (uint256)
{
uint256 ts = totalSupply();
return (ts == 0) ? 1e18 : (this.underlyingJuniors() * 1e18) / ts;
}

function underlyingTotal()
public view virtual override
public virtual override
returns(uint256)
{
return IProvider(pool).underlyingBalance() - IProvider(pool).underlyingFees() - underlyingLiquidatedJuniors;
}

function underlyingJuniors()
public view virtual override
public virtual override
returns (uint256)
{
return this.underlyingTotal() - abond.principal - this.abondPaid();
}

function underlyingLoanable()
public view virtual override
public virtual override
returns (uint256)
{
// underlyingTotal - abond.principal - abond.gain - queued withdrawls
Expand Down
1 change: 1 addition & 0 deletions contracts/external-interfaces/compound-finance/ICToken.sol
Expand Up @@ -7,6 +7,7 @@ interface ICToken {
function redeemUnderlying(uint redeemAmount) external returns (uint256);
function accrueInterest() external returns (uint256);
function exchangeRateStored() external view returns (uint256);
function exchangeRateCurrent() external returns (uint256);
function supplyRatePerBlock() external view returns (uint256);
function totalBorrows() external view returns (uint256);
function getCash() external view returns (uint256);
Expand Down
Expand Up @@ -36,7 +36,7 @@ contract SmartYieldForModelMock is HasClock, SmartYield {
}

function checkGas(uint256 principal, uint16 forDays) public {
_lastCheckGas = IBondModel(IController(controller).bondModel()).gain(address(this), principal, forDays);
//_lastCheckGas = IBondModel(IController(controller).bondModel()).gain(address(this), principal, forDays);
}

function setMockValues(uint256 underlyingLoanable_, uint256 underlyingTotal_, uint256 providerRatePerDay_) public {
Expand Down
28 changes: 12 additions & 16 deletions contracts/model/BondModelV1.sol
Expand Up @@ -12,43 +12,39 @@ contract BondModelV1 is IBondModel {
using SafeMath for uint256;

function gain(
address pool_,
uint256 total_,
uint256 loanable_,
uint256 dailyRate_,
uint256 principal_,
uint16 forDays_
)
external view override
external pure override
returns (uint256)
{
uint256 loanable = ISmartYield(pool_).underlyingLoanable();
uint256 total = ISmartYield(pool_).underlyingTotal();
uint256 dailyRate = ISmartYield(pool_).providerRatePerDay();

uint256 aproxGain = MathUtils.compound2(
principal_,
//dailyRate * (loanable * 1e18 / (total + principal)) / 1e18,
uint256(1e18).mul(dailyRate).mul(loanable) / (total + principal_) / 1e18,
uint256(1e18).mul(dailyRate_).mul(loanable_) / (total_ + principal_) / 1e18,
forDays_
).sub(principal_);

uint256 rate = uint256(1e18).mul(dailyRate).mul(loanable.sub(aproxGain, "BondModelV1: liquidity")) / (total + principal_) / 1e18;
uint256 rate = uint256(1e18).mul(dailyRate_).mul(loanable_.sub(aproxGain, "BondModelV1: liquidity")) / (total_ + principal_) / 1e18;

return MathUtils.compound2(principal_, rate, forDays_).sub(principal_);
}

function maxDailyRate(
address pool_
uint256 total_,
uint256 loanable_,
uint256 dailyRate_
)
external view override
external pure override
returns (uint256)
{
uint256 total = ISmartYield(pool_).underlyingTotal();
if (0 == total) {
if (0 == total_) {
return 0;
}

uint256 loanable = ISmartYield(pool_).underlyingLoanable();
uint256 dailyRate = ISmartYield(pool_).providerRatePerDay();
return uint256(1e18).mul(dailyRate).mul(loanable) / (total) / 1e18;
return uint256(1e18).mul(dailyRate_).mul(loanable_) / (total_) / 1e18;
}

}
4 changes: 2 additions & 2 deletions contracts/model/IBondModel.sol
Expand Up @@ -4,8 +4,8 @@ pragma abicoder v2;

interface IBondModel {

function gain(address pool_, uint256 principal_, uint16 forDays_) external view returns (uint256);
function gain(uint256 total_, uint256 loanable_, uint256 dailyRate_, uint256 principal_, uint16 forDays_) external pure returns (uint256);

function maxDailyRate(address pool_) external view returns (uint256);
function maxDailyRate(uint256 total_, uint256 loanable_, uint256 dailyRate_) external pure returns (uint256);

}
2 changes: 1 addition & 1 deletion contracts/oracle/IYieldOracle.sol
Expand Up @@ -5,5 +5,5 @@ pragma abicoder v2;
interface IYieldOracle {
function update() external;

function consult(uint256 forInterval) external view returns (uint256 amountOut);
function consult(uint256 forInterval) external returns (uint256 amountOut);
}
9 changes: 1 addition & 8 deletions contracts/oracle/IYieldOraclelizable.sol
Expand Up @@ -7,13 +7,6 @@ interface IYieldOraclelizable {
// oracle should call this when updating
function cumulatives()
external
returns(uint256 cumulativeSecondlyYield);
returns(uint256 cumulativeYield);

// returns cumulative yield up to currentTime()
function currentCumulatives()
external view
returns (uint256 cumulativeSecondlyYield);

// needs to return block.timestamp, otherwise used for mocks
function currentTime() external view returns (uint256);
}
13 changes: 6 additions & 7 deletions contracts/oracle/YieldOracle.sol
Expand Up @@ -74,7 +74,7 @@ contract YieldOracle is IYieldOracle {
view
returns (Observation storage firstObservation)
{
uint8 observationIndex = observationIndexOf(pool.currentTime());
uint8 observationIndex = observationIndexOf(block.timestamp);
// no overflow issue. if observationIndex + 1 overflows, result is still zero.
uint8 firstObservationIndex = (observationIndex + 1) % granularity;
firstObservation = yieldObservations[firstObservationIndex];
Expand All @@ -84,14 +84,14 @@ contract YieldOracle is IYieldOracle {
// once per epoch period.
function update() external virtual override {
// get the observation for the current period
uint8 observationIndex = observationIndexOf(pool.currentTime());
uint8 observationIndex = observationIndexOf(block.timestamp);
Observation storage observation = yieldObservations[observationIndex];

// we only want to commit updates once per period (i.e. windowSize / granularity)
uint256 timeElapsed = pool.currentTime() - observation.timestamp;
uint256 timeElapsed = block.timestamp - observation.timestamp;
if (timeElapsed > periodSize) {
(uint256 yieldCumulative) = pool.cumulatives();
observation.timestamp = pool.currentTime();
observation.timestamp = block.timestamp;
observation.yieldCumulative = yieldCumulative;
}
}
Expand All @@ -113,14 +113,13 @@ contract YieldOracle is IYieldOracle {
// update must have been called for the bucket corresponding to timestamp `now - windowSize`
function consult(uint256 forInterval)
external
view
override
virtual
returns (uint256 yieldForInterval)
{
Observation storage firstObservation = getFirstObservationInWindow();

uint256 timeElapsed = pool.currentTime() - firstObservation.timestamp;
uint256 timeElapsed = block.timestamp - firstObservation.timestamp;

if (!(timeElapsed <= windowSize)) {
// originally:
Expand All @@ -143,7 +142,7 @@ contract YieldOracle is IYieldOracle {
return 0;
}

(uint256 yieldCumulative) = pool.currentCumulatives();
(uint256 yieldCumulative) = pool.cumulatives();

return
computeAmountOut(
Expand Down

0 comments on commit 6a2b956

Please sign in to comment.