文件 1 的 15:Context.sol
pragma solidity >=0.6.0 <0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this;
return msg.data;
}
}
文件 2 的 15:ERC20.sol
pragma solidity >=0.6.0 <0.8.0;
import "../../utils/Context.sol";
import "./IERC20.sol";
import "../../math/SafeMath.sol";
contract ERC20 is Context, IERC20 {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
uint8 private _decimals;
constructor (string memory name_, string memory symbol_) public {
_name = name_;
_symbol = symbol_;
_decimals = 18;
}
function name() public view virtual returns (string memory) {
return _name;
}
function symbol() public view virtual returns (string memory) {
return _symbol;
}
function decimals() public view virtual returns (uint8) {
return _decimals;
}
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 amount) public virtual override returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
return true;
}
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
return true;
}
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
return true;
}
function _transfer(address sender, address recipient, uint256 amount) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
function _setupDecimals(uint8 decimals_) internal virtual {
_decimals = decimals_;
}
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
}
文件 3 的 15:Governed.sol
pragma solidity ^0.7.6;
pragma abicoder v2;
abstract contract Governed {
address public dao;
address public guardian;
modifier onlyDao {
require(
dao == msg.sender,
"GOV: not dao"
);
_;
}
modifier onlyDaoOrGuardian {
require(
msg.sender == dao || msg.sender == guardian,
"GOV: not dao/guardian"
);
_;
}
constructor()
{
dao = msg.sender;
guardian = msg.sender;
}
function setDao(address dao_)
external
onlyDao
{
dao = dao_;
}
function setGuardian(address guardian_)
external
onlyDao
{
guardian = guardian_;
}
}
文件 4 的 15:IBond.sol
pragma solidity ^0.7.6;
pragma abicoder v2;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
interface IBond is IERC721 {
function smartYield() external view returns (address);
function mint(address to, uint256 tokenId) external;
function burn(uint256 tokenId) external;
}
文件 5 的 15:IBondModel.sol
pragma solidity ^0.7.6;
pragma abicoder v2;
interface IBondModel {
function gain(uint256 total_, uint256 loanable_, uint256 dailyRate_, uint256 principal_, uint16 forDays_) external pure returns (uint256);
function maxDailyRate(uint256 total_, uint256 loanable_, uint256 dailyRate_) external pure returns (uint256);
}
文件 6 的 15:IController.sol
pragma solidity ^0.7.6;
pragma abicoder v2;
import "./Governed.sol";
import "./IProvider.sol";
import "./ISmartYield.sol";
abstract contract IController is Governed {
uint256 public constant EXP_SCALE = 1e18;
address public pool;
address public smartYield;
address public oracle;
address public bondModel;
address public feesOwner;
uint256 public HARVEST_COST = 40 * 1e15;
uint256 public FEE_BUY_JUNIOR_TOKEN = 3 * 1e15;
uint256 public FEE_REDEEM_SENIOR_BOND = 100 * 1e15;
uint256 public BOND_MAX_RATE_PER_DAY = 719065000000000;
uint16 public BOND_LIFE_MAX = 90;
bool public PAUSED_BUY_JUNIOR_TOKEN = false;
bool public PAUSED_BUY_SENIOR_BOND = false;
function setHarvestCost(uint256 newValue_)
public
onlyDao
{
require(
HARVEST_COST < EXP_SCALE,
"IController: HARVEST_COST too large"
);
HARVEST_COST = newValue_;
}
function setBondMaxRatePerDay(uint256 newVal_)
public
onlyDao
{
BOND_MAX_RATE_PER_DAY = newVal_;
}
function setBondLifeMax(uint16 newVal_)
public
onlyDao
{
BOND_LIFE_MAX = newVal_;
}
function setFeeBuyJuniorToken(uint256 newVal_)
public
onlyDao
{
FEE_BUY_JUNIOR_TOKEN = newVal_;
}
function setFeeRedeemSeniorBond(uint256 newVal_)
public
onlyDao
{
FEE_REDEEM_SENIOR_BOND = newVal_;
}
function setPaused(bool buyJToken_, bool buySBond_)
public
onlyDaoOrGuardian
{
PAUSED_BUY_JUNIOR_TOKEN = buyJToken_;
PAUSED_BUY_SENIOR_BOND = buySBond_;
}
function setOracle(address newVal_)
public
onlyDao
{
oracle = newVal_;
}
function setBondModel(address newVal_)
public
onlyDao
{
bondModel = newVal_;
}
function setFeesOwner(address newVal_)
public
onlyDao
{
feesOwner = newVal_;
}
function yieldControllTo(address newController_)
public
onlyDao
{
IProvider(pool).setController(newController_);
ISmartYield(smartYield).setController(newController_);
}
function providerRatePerDay() external virtual returns (uint256);
}
文件 7 的 15:IERC165.sol
pragma solidity >=0.6.0 <0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 8 的 15:IERC20.sol
pragma solidity >=0.6.0 <0.8.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 9 的 15:IERC721.sol
pragma solidity >=0.6.2 <0.8.0;
import "../../introspection/IERC165.sol";
interface IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function transferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function setApprovalForAll(address operator, bool _approved) external;
function isApprovedForAll(address owner, address operator) external view returns (bool);
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
}
文件 10 的 15:IProvider.sol
pragma solidity ^0.7.6;
pragma abicoder v2;
interface IProvider {
function smartYield() external view returns (address);
function controller() external view returns (address);
function underlyingFees() external view returns (uint256);
function _depositProvider(uint256 underlyingAmount_, uint256 takeFees_) external;
function _withdrawProvider(uint256 underlyingAmount_, uint256 takeFees_) external;
function _takeUnderlying(address from_, uint256 amount_) external;
function _sendUnderlying(address to_, uint256 amount_) external;
function transferFees() external;
function underlyingBalance() external returns (uint256);
function setController(address newController_) external;
}
文件 11 的 15:ISmartYield.sol
pragma solidity ^0.7.6;
pragma abicoder v2;
interface ISmartYield {
struct SeniorBond {
uint256 principal;
uint256 gain;
uint256 issuedAt;
uint256 maturesAt;
bool liquidated;
}
struct JuniorBond {
uint256 tokens;
uint256 maturesAt;
}
struct JuniorBondsAt {
uint256 tokens;
uint256 price;
}
function controller() external view returns (address);
function buyBond(uint256 principalAmount_, uint256 minGain_, uint256 deadline_, uint16 forDays_) external returns (uint256);
function redeemBond(uint256 bondId_) external;
function unaccountBonds(uint256[] memory bondIds_) external;
function buyTokens(uint256 underlyingAmount_, uint256 minTokens_, uint256 deadline_) external;
function sellTokens(uint256 tokens_, uint256 minUnderlying_, uint256 deadline_) external;
function buyJuniorBond(uint256 tokenAmount_, uint256 maxMaturesAt_, uint256 deadline_) external;
function redeemJuniorBond(uint256 jBondId_) external;
function liquidateJuniorBonds(uint256 upUntilTimestamp_) external;
function price() external returns (uint256);
function abondPaid() external view returns (uint256);
function abondDebt() external view returns (uint256);
function abondGain() external view returns (uint256);
function underlyingTotal() external returns (uint256);
function underlyingLoanable() external returns (uint256);
function underlyingJuniors() external returns (uint256);
function bondGain(uint256 principalAmount_, uint16 forDays_) external returns (uint256);
function maxBondDailyRate() external returns (uint256);
function setController(address newController_) external;
}
文件 12 的 15:JuniorToken.sol
pragma solidity ^0.7.6;
pragma abicoder v2;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
abstract contract JuniorToken is ERC20 {
constructor(
string memory name_,
string memory symbol_,
uint8 decimals_
)
ERC20(name_, symbol_)
{
_setupDecimals(decimals_);
}
}
文件 13 的 15:MathUtils.sol
pragma solidity ^0.7.6;
import "@openzeppelin/contracts/math/SafeMath.sol";
library MathUtils {
using SafeMath for uint256;
uint256 public constant EXP_SCALE = 1e18;
function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = x < y ? x : y;
}
function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = x > y ? x : y;
}
function compound(
uint256 principal,
uint256 ratePerPeriod,
uint16 periods
) internal pure returns (uint256) {
if (0 == ratePerPeriod) {
return principal;
}
while (periods > 0) {
principal = principal.add(principal.mul(ratePerPeriod).div(EXP_SCALE));
periods -= 1;
}
return principal;
}
function compound2(
uint256 principal,
uint256 ratePerPeriod,
uint16 periods
) internal pure returns (uint256) {
if (0 == ratePerPeriod) {
return principal;
}
while (periods > 0) {
if (periods % 2 == 1) {
principal = principal.add(principal.mul(ratePerPeriod).div(EXP_SCALE));
periods -= 1;
} else {
ratePerPeriod = ((uint256(2).mul(ratePerPeriod).mul(EXP_SCALE)).add(ratePerPeriod.mul(ratePerPeriod))).div(EXP_SCALE);
periods /= 2;
}
}
return principal;
}
function linearGain(
uint256 principal,
uint256 ratePerPeriod,
uint16 periods
) internal pure returns (uint256) {
return principal.add(
fractionOf(principal, ratePerPeriod.mul(periods))
);
}
function fractionOf(uint256 a, uint256 f) internal pure returns (uint256) {
return a.mul(f).div(EXP_SCALE);
}
}
文件 14 的 15:SafeMath.sol
pragma solidity >=0.6.0 <0.8.0;
library SafeMath {
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a / b);
}
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a % b;
}
}
文件 15 的 15:SmartYield.sol
pragma solidity ^0.7.6;
pragma abicoder v2;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./lib/math/MathUtils.sol";
import "./IController.sol";
import "./ISmartYield.sol";
import "./IProvider.sol";
import "./model/IBondModel.sol";
import "./IBond.sol";
import "./JuniorToken.sol";
contract SmartYield is
JuniorToken,
ISmartYield
{
using SafeMath for uint256;
uint256 public constant MAX_UINT256 = uint256(-1);
uint256 public constant EXP_SCALE = 1e18;
address public override controller;
address public pool;
address public seniorBond;
address public juniorBond;
uint256 public underlyingLiquidatedJuniors;
uint256 public tokensInJuniorBonds;
uint256 public seniorBondId;
uint256 public juniorBondId;
uint256 public juniorBondsMaturitiesPrev;
uint256[] public juniorBondsMaturities;
mapping(uint256 => JuniorBondsAt) public juniorBondsMaturingAt;
mapping(uint256 => SeniorBond) public seniorBonds;
mapping(uint256 => JuniorBond) public juniorBonds;
SeniorBond public abond;
bool public _setup;
event BuyTokens(address indexed buyer, uint256 underlyingIn, uint256 tokensOut, uint256 fee);
event SellTokens(address indexed seller, uint256 tokensIn, uint256 underlyingOut, uint256 forfeits);
event BuySeniorBond(address indexed buyer, uint256 indexed seniorBondId, uint256 underlyingIn, uint256 gain, uint256 forDays);
event RedeemSeniorBond(address indexed owner, uint256 indexed seniorBondId, uint256 fee);
event BuyJuniorBond(address indexed buyer, uint256 indexed juniorBondId, uint256 tokensIn, uint256 maturesAt);
event RedeemJuniorBond(address indexed owner, uint256 indexed juniorBondId, uint256 underlyingOut);
modifier onlyControllerOrDao {
require(
msg.sender == controller || msg.sender == IController(controller).dao(),
"PPC: only controller/DAO"
);
_;
}
constructor(
string memory name_,
string memory symbol_,
uint8 decimals_
)
JuniorToken(name_, symbol_, decimals_)
{}
function setup(
address controller_,
address pool_,
address seniorBond_,
address juniorBond_
)
external
{
require(
false == _setup,
"SY: already setup"
);
controller = controller_;
pool = pool_;
seniorBond = seniorBond_;
juniorBond = juniorBond_;
_setup = true;
}
function setController(address newController_)
external override
onlyControllerOrDao
{
controller = newController_;
}
function buyTokens(
uint256 underlyingAmount_,
uint256 minTokens_,
uint256 deadline_
)
external override
{
_beforeProviderOp(block.timestamp);
require(
false == IController(controller).PAUSED_BUY_JUNIOR_TOKEN(),
"SY: buyTokens paused"
);
require(
block.timestamp <= deadline_,
"SY: buyTokens deadline"
);
uint256 fee = MathUtils.fractionOf(underlyingAmount_, IController(controller).FEE_BUY_JUNIOR_TOKEN());
uint256 getsTokens = (underlyingAmount_.sub(fee)).mul(EXP_SCALE).div(price());
require(
getsTokens >= minTokens_,
"SY: buyTokens minTokens"
);
address buyer = msg.sender;
IProvider(pool)._takeUnderlying(buyer, underlyingAmount_);
IProvider(pool)._depositProvider(underlyingAmount_, fee);
_mint(buyer, getsTokens);
emit BuyTokens(buyer, underlyingAmount_, getsTokens, fee);
}
function sellTokens(
uint256 tokenAmount_,
uint256 minUnderlying_,
uint256 deadline_
)
external override
{
_beforeProviderOp(block.timestamp);
require(
block.timestamp <= deadline_,
"SY: sellTokens deadline"
);
uint256 debtShare = tokenAmount_.mul(EXP_SCALE).div(totalSupply());
uint256 forfeits = abondDebt().mul(debtShare).div(EXP_SCALE);
uint256 toPay = tokenAmount_.mul(price()).div(EXP_SCALE).sub(forfeits);
require(
toPay >= minUnderlying_,
"SY: sellTokens minUnderlying"
);
address seller = msg.sender;
_burn(seller, tokenAmount_);
IProvider(pool)._withdrawProvider(toPay, 0);
IProvider(pool)._sendUnderlying(seller, toPay);
emit SellTokens(seller, tokenAmount_, toPay, forfeits);
}
function buyBond(
uint256 principalAmount_,
uint256 minGain_,
uint256 deadline_,
uint16 forDays_
)
external override
returns (uint256)
{
_beforeProviderOp(block.timestamp);
require(
false == IController(controller).PAUSED_BUY_SENIOR_BOND(),
"SY: buyBond paused"
);
require(
block.timestamp <= deadline_,
"SY: buyBond deadline"
);
require(
0 < forDays_ && forDays_ <= IController(controller).BOND_LIFE_MAX(),
"SY: buyBond forDays"
);
uint256 gain = bondGain(principalAmount_, forDays_);
require(
gain >= minGain_,
"SY: buyBond minGain"
);
require(
gain > 0,
"SY: buyBond gain 0"
);
require(
gain < underlyingLoanable(),
"SY: buyBond underlyingLoanable"
);
uint256 issuedAt = block.timestamp;
address buyer = msg.sender;
IProvider(pool)._takeUnderlying(buyer, principalAmount_);
IProvider(pool)._depositProvider(principalAmount_, 0);
SeniorBond memory b =
SeniorBond(
principalAmount_,
gain,
issuedAt,
uint256(1 days) * uint256(forDays_) + issuedAt,
false
);
_mintBond(buyer, b);
emit BuySeniorBond(buyer, seniorBondId, principalAmount_, gain, forDays_);
return gain;
}
function buyJuniorBond(
uint256 tokenAmount_,
uint256 maxMaturesAt_,
uint256 deadline_
)
external override
{
_beforeProviderOp(block.timestamp);
uint256 maturesAt = abond.maturesAt.div(EXP_SCALE).add(1);
require(
block.timestamp <= deadline_,
"SY: buyJuniorBond deadline"
);
require(
maturesAt <= maxMaturesAt_,
"SY: buyJuniorBond maxMaturesAt"
);
JuniorBond memory jb = JuniorBond(
tokenAmount_,
maturesAt
);
address buyer = msg.sender;
_takeTokens(buyer, tokenAmount_);
_mintJuniorBond(buyer, jb);
emit BuyJuniorBond(buyer, juniorBondId, tokenAmount_, maturesAt);
if (block.timestamp >= maturesAt) {
JuniorBondsAt memory jBondsAt = juniorBondsMaturingAt[jb.maturesAt];
if (jBondsAt.price == 0) {
_liquidateJuniorsAt(jb.maturesAt);
} else {
_burn(address(this), jb.tokens);
underlyingLiquidatedJuniors = underlyingLiquidatedJuniors.add(
jb.tokens.mul(jBondsAt.price).div(EXP_SCALE)
);
_unaccountJuniorBond(jb);
}
return this.redeemJuniorBond(juniorBondId);
}
}
function redeemBond(
uint256 bondId_
)
external override
{
_beforeProviderOp(block.timestamp);
require(
block.timestamp >= seniorBonds[bondId_].maturesAt,
"SY: redeemBond not matured"
);
address payTo = IBond(seniorBond).ownerOf(bondId_);
uint256 payAmnt = seniorBonds[bondId_].gain.add(seniorBonds[bondId_].principal);
uint256 fee = MathUtils.fractionOf(seniorBonds[bondId_].gain, IController(controller).FEE_REDEEM_SENIOR_BOND());
payAmnt = payAmnt.sub(fee);
if (seniorBonds[bondId_].liquidated == false) {
seniorBonds[bondId_].liquidated = true;
_unaccountBond(seniorBonds[bondId_]);
}
IBond(seniorBond).burn(bondId_);
IProvider(pool)._withdrawProvider(payAmnt, fee);
IProvider(pool)._sendUnderlying(payTo, payAmnt);
emit RedeemSeniorBond(payTo, bondId_, fee);
}
function redeemJuniorBond(uint256 jBondId_)
external override
{
_beforeProviderOp(block.timestamp);
JuniorBond memory jb = juniorBonds[jBondId_];
require(
jb.maturesAt <= block.timestamp,
"SY: redeemJuniorBond maturesAt"
);
JuniorBondsAt memory jBondsAt = juniorBondsMaturingAt[jb.maturesAt];
address payTo = IBond(juniorBond).ownerOf(jBondId_);
uint256 payAmnt = jBondsAt.price.mul(jb.tokens).div(EXP_SCALE);
_burnJuniorBond(jBondId_);
IProvider(pool)._withdrawProvider(payAmnt, 0);
IProvider(pool)._sendUnderlying(payTo, payAmnt);
underlyingLiquidatedJuniors = underlyingLiquidatedJuniors.sub(payAmnt);
emit RedeemJuniorBond(payTo, jBondId_, payAmnt);
}
function maxBondDailyRate()
external override
returns (uint256)
{
return IBondModel(IController(controller).bondModel()).maxDailyRate(
underlyingTotal(),
underlyingLoanable(),
IController(controller).providerRatePerDay()
);
}
function liquidateJuniorBonds(uint256 upUntilTimestamp_)
external override
{
require(
upUntilTimestamp_ <= block.timestamp,
"SY: liquidateJuniorBonds in future"
);
_beforeProviderOp(upUntilTimestamp_);
}
function bondGain(uint256 principalAmount_, uint16 forDays_)
public override
returns (uint256)
{
return IBondModel(IController(controller).bondModel()).gain(
underlyingTotal(),
underlyingLoanable(),
IController(controller).providerRatePerDay(),
principalAmount_,
forDays_
);
}
function price()
public override
returns (uint256)
{
uint256 ts = totalSupply();
return (ts == 0) ? EXP_SCALE : underlyingJuniors().mul(EXP_SCALE).div(ts);
}
function underlyingTotal()
public virtual override
returns(uint256)
{
return IProvider(pool).underlyingBalance().sub(underlyingLiquidatedJuniors);
}
function underlyingJuniors()
public virtual override
returns (uint256)
{
return underlyingTotal().sub(abond.principal).sub(abondPaid());
}
function underlyingLoanable()
public virtual override
returns (uint256)
{
uint256 _underlyingTotal = underlyingTotal();
uint256 _lockedUnderlying = abond.principal.add(abond.gain).add(
tokensInJuniorBonds.mul(price()).div(EXP_SCALE)
);
if (_lockedUnderlying > _underlyingTotal) {
return 0;
}
return _underlyingTotal.sub(_lockedUnderlying);
}
function abondGain()
public view override
returns (uint256)
{
return abond.gain;
}
function abondPaid()
public view override
returns (uint256)
{
uint256 ts = block.timestamp * EXP_SCALE;
if (ts <= abond.issuedAt || (abond.maturesAt <= abond.issuedAt)) {
return 0;
}
uint256 duration = abond.maturesAt.sub(abond.issuedAt);
uint256 paidDuration = MathUtils.min(ts.sub(abond.issuedAt), duration);
return abondGain().mul(paidDuration).div(duration);
}
function abondDebt()
public view override
returns (uint256)
{
return abondGain().sub(abondPaid());
}
function _beforeProviderOp(uint256 upUntilTimestamp_) internal {
for (uint256 i = juniorBondsMaturitiesPrev; i < juniorBondsMaturities.length; i++) {
if (upUntilTimestamp_ >= juniorBondsMaturities[i]) {
_liquidateJuniorsAt(juniorBondsMaturities[i]);
juniorBondsMaturitiesPrev = i.add(1);
} else {
break;
}
}
}
function _liquidateJuniorsAt(uint256 timestamp_)
internal
{
JuniorBondsAt storage jBondsAt = juniorBondsMaturingAt[timestamp_];
require(
jBondsAt.tokens > 0,
"SY: nothing to liquidate"
);
require(
jBondsAt.price == 0,
"SY: already liquidated"
);
jBondsAt.price = price();
underlyingLiquidatedJuniors = underlyingLiquidatedJuniors.add(
jBondsAt.tokens.mul(jBondsAt.price).div(EXP_SCALE)
);
_burn(address(this), jBondsAt.tokens);
tokensInJuniorBonds = tokensInJuniorBonds.sub(jBondsAt.tokens);
}
function unaccountBonds(uint256[] memory bondIds_)
external override
{
uint256 currentTime = block.timestamp;
for (uint256 f = 0; f < bondIds_.length; f++) {
if (
currentTime >= seniorBonds[bondIds_[f]].maturesAt &&
seniorBonds[bondIds_[f]].liquidated == false
) {
seniorBonds[bondIds_[f]].liquidated = true;
_unaccountBond(seniorBonds[bondIds_[f]]);
}
}
}
function _mintBond(address to_, SeniorBond memory bond_)
internal
{
require(
seniorBondId < MAX_UINT256,
"SY: _mintBond"
);
seniorBondId++;
seniorBonds[seniorBondId] = bond_;
_accountBond(bond_);
IBond(seniorBond).mint(to_, seniorBondId);
}
function _accountBond(SeniorBond memory b_)
internal
{
uint256 _now = block.timestamp * EXP_SCALE;
uint256 newDebt = abondDebt().add(b_.gain);
uint256 newMaturesAt = (abond.maturesAt.mul(abondDebt()).add(b_.maturesAt.mul(EXP_SCALE).mul(b_.gain))).div(newDebt);
uint256 newDuration = (abond.gain.add(b_.gain)).mul(newMaturesAt.sub(_now)).div(newDebt).add(1);
uint256 newIssuedAt = newMaturesAt.sub(newDuration, "SY: liquidate some seniorBonds");
abond = SeniorBond(
abond.principal.add(b_.principal),
abond.gain.add(b_.gain),
newIssuedAt,
newMaturesAt,
false
);
}
function _unaccountBond(SeniorBond memory b_)
internal
{
uint256 now_ = block.timestamp * EXP_SCALE;
if ((now_ >= abond.maturesAt)) {
abond = SeniorBond(
abond.principal.sub(b_.principal),
abond.gain - b_.gain,
now_.sub(abond.maturesAt.sub(abond.issuedAt)),
now_,
false
);
return;
}
uint256 newDuration = (abond.gain.sub(b_.gain)).mul(abond.maturesAt.sub(now_)).div(abondDebt()).add(1);
uint256 newIssuedAt = abond.maturesAt.sub(newDuration, "SY: liquidate some seniorBonds");
abond = SeniorBond(
abond.principal.sub(b_.principal),
abond.gain.sub(b_.gain),
newIssuedAt,
abond.maturesAt,
false
);
}
function _mintJuniorBond(address to_, JuniorBond memory jb_)
internal
{
require(
juniorBondId < MAX_UINT256,
"SY: _mintJuniorBond"
);
juniorBondId++;
juniorBonds[juniorBondId] = jb_;
_accountJuniorBond(jb_);
IBond(juniorBond).mint(to_, juniorBondId);
}
function _accountJuniorBond(JuniorBond memory jb_)
internal
{
tokensInJuniorBonds = tokensInJuniorBonds.add(jb_.tokens);
JuniorBondsAt storage jBondsAt = juniorBondsMaturingAt[jb_.maturesAt];
uint256 tmp;
if (jBondsAt.tokens == 0 && block.timestamp < jb_.maturesAt) {
juniorBondsMaturities.push(jb_.maturesAt);
for (uint256 i = juniorBondsMaturities.length - 1; i >= MathUtils.max(1, juniorBondsMaturitiesPrev); i--) {
if (juniorBondsMaturities[i] > juniorBondsMaturities[i - 1]) {
break;
}
tmp = juniorBondsMaturities[i - 1];
juniorBondsMaturities[i - 1] = juniorBondsMaturities[i];
juniorBondsMaturities[i] = tmp;
}
}
jBondsAt.tokens = jBondsAt.tokens.add(jb_.tokens);
}
function _burnJuniorBond(uint256 bondId_) internal {
IBond(juniorBond).burn(bondId_);
}
function _unaccountJuniorBond(JuniorBond memory jb_) internal {
tokensInJuniorBonds = tokensInJuniorBonds.sub(jb_.tokens);
JuniorBondsAt storage jBondsAt = juniorBondsMaturingAt[jb_.maturesAt];
jBondsAt.tokens = jBondsAt.tokens.sub(jb_.tokens);
}
function _takeTokens(address from_, uint256 amount_) internal {
_transfer(from_, address(this), amount_);
}
}
{
"compilationTarget": {
"contracts/SmartYield.sol": "SmartYield"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 9999
},
"remappings": []
}
[{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":true,"internalType":"uint256","name":"juniorBondId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokensIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maturesAt","type":"uint256"}],"name":"BuyJuniorBond","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":true,"internalType":"uint256","name":"seniorBondId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"underlyingIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gain","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"forDays","type":"uint256"}],"name":"BuySeniorBond","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"underlyingIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokensOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"BuyTokens","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"juniorBondId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"underlyingOut","type":"uint256"}],"name":"RedeemJuniorBond","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"seniorBondId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"RedeemSeniorBond","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokensIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"underlyingOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"forfeits","type":"uint256"}],"name":"SellTokens","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"EXP_SCALE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_UINT256","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_setup","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"abond","outputs":[{"internalType":"uint256","name":"principal","type":"uint256"},{"internalType":"uint256","name":"gain","type":"uint256"},{"internalType":"uint256","name":"issuedAt","type":"uint256"},{"internalType":"uint256","name":"maturesAt","type":"uint256"},{"internalType":"bool","name":"liquidated","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"abondDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"abondGain","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"abondPaid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"principalAmount_","type":"uint256"},{"internalType":"uint16","name":"forDays_","type":"uint16"}],"name":"bondGain","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"principalAmount_","type":"uint256"},{"internalType":"uint256","name":"minGain_","type":"uint256"},{"internalType":"uint256","name":"deadline_","type":"uint256"},{"internalType":"uint16","name":"forDays_","type":"uint16"}],"name":"buyBond","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenAmount_","type":"uint256"},{"internalType":"uint256","name":"maxMaturesAt_","type":"uint256"},{"internalType":"uint256","name":"deadline_","type":"uint256"}],"name":"buyJuniorBond","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"underlyingAmount_","type":"uint256"},{"internalType":"uint256","name":"minTokens_","type":"uint256"},{"internalType":"uint256","name":"deadline_","type":"uint256"}],"name":"buyTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"controller","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"juniorBond","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"juniorBondId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"juniorBonds","outputs":[{"internalType":"uint256","name":"tokens","type":"uint256"},{"internalType":"uint256","name":"maturesAt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"juniorBondsMaturingAt","outputs":[{"internalType":"uint256","name":"tokens","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"juniorBondsMaturities","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"juniorBondsMaturitiesPrev","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upUntilTimestamp_","type":"uint256"}],"name":"liquidateJuniorBonds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxBondDailyRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"price","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"bondId_","type":"uint256"}],"name":"redeemBond","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"jBondId_","type":"uint256"}],"name":"redeemJuniorBond","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenAmount_","type":"uint256"},{"internalType":"uint256","name":"minUnderlying_","type":"uint256"},{"internalType":"uint256","name":"deadline_","type":"uint256"}],"name":"sellTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"seniorBond","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"seniorBondId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"seniorBonds","outputs":[{"internalType":"uint256","name":"principal","type":"uint256"},{"internalType":"uint256","name":"gain","type":"uint256"},{"internalType":"uint256","name":"issuedAt","type":"uint256"},{"internalType":"uint256","name":"maturesAt","type":"uint256"},{"internalType":"bool","name":"liquidated","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newController_","type":"address"}],"name":"setController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"controller_","type":"address"},{"internalType":"address","name":"pool_","type":"address"},{"internalType":"address","name":"seniorBond_","type":"address"},{"internalType":"address","name":"juniorBond_","type":"address"}],"name":"setup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokensInJuniorBonds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"bondIds_","type":"uint256[]"}],"name":"unaccountBonds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"underlyingJuniors","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"underlyingLiquidatedJuniors","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"underlyingLoanable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"underlyingTotal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]