文件 1 的 1:Exposure.sol
pragma solidity >=0.6.0 <0.8.0;
library SafeMath {
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) {
return sub(a, b, "SafeMath: subtraction overflow");
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
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) {
return div(a, b, "SafeMath: division by zero");
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
pragma solidity >=0.6.0 <0.7.0;
contract Constants {
uint8 public constant N_COINS = 3;
uint8 public constant DEFAULT_DECIMALS = 18;
uint256 public constant DEFAULT_DECIMALS_FACTOR = uint256(10)**DEFAULT_DECIMALS;
uint8 public constant CHAINLINK_PRICE_DECIMALS = 8;
uint256 public constant CHAINLINK_PRICE_DECIMAL_FACTOR = uint256(10)**CHAINLINK_PRICE_DECIMALS;
uint8 public constant PERCENTAGE_DECIMALS = 4;
uint256 public constant PERCENTAGE_DECIMAL_FACTOR = uint256(10)**PERCENTAGE_DECIMALS;
uint256 public constant CURVE_RATIO_DECIMALS = 6;
uint256 public constant CURVE_RATIO_DECIMALS_FACTOR = uint256(10)**CURVE_RATIO_DECIMALS;
}
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;
}
}
pragma solidity >=0.6.0 <0.8.0;
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor () internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
function owner() public view returns (address) {
return _owner;
}
modifier onlyOwner() {
require(_owner == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
pragma solidity >=0.6.0 <0.7.0;
interface IController {
function stablecoins() external view returns (address[3] memory);
function vaults() external view returns (address[3] memory);
function underlyingVaults(uint256 i) external view returns (address vault);
function curveVault() external view returns (address);
function pnl() external view returns (address);
function insurance() external view returns (address);
function lifeGuard() external view returns (address);
function buoy() external view returns (address);
function reward() external view returns (address);
function isValidBigFish(
bool pwrd,
bool deposit,
uint256 amount
) external view returns (bool);
function withdrawHandler() external view returns (address);
function emergencyHandler() external view returns (address);
function depositHandler() external view returns (address);
function totalAssets() external view returns (uint256);
function gTokenTotalAssets() external view returns (uint256);
function eoaOnly(address sender) external;
function getSkimPercent() external view returns (uint256);
function gToken(bool _pwrd) external view returns (address);
function emergencyState() external view returns (bool);
function deadCoin() external view returns (uint256);
function distributeStrategyGainLoss(uint256 gain, uint256 loss) external;
function burnGToken(
bool pwrd,
bool all,
address account,
uint256 amount,
uint256 bonus
) external;
function mintGToken(
bool pwrd,
address account,
uint256 amount
) external;
function getUserAssets(bool pwrd, address account) external view returns (uint256 deductUsd);
function referrals(address account) external view returns (address);
function addReferral(address account, address referral) external;
function getStrategiesTargetRatio() external view returns (uint256[] memory);
function withdrawalFee(bool pwrd) external view returns (uint256);
function validGTokenDecrease(uint256 amount) external view returns (bool);
}
pragma solidity >=0.6.0 <0.7.0;
interface IPausable {
function paused() external view returns (bool);
}
pragma solidity >=0.6.0 <0.7.0;
contract Controllable is Ownable {
address public controller;
event ChangeController(address indexed oldController, address indexed newController);
modifier whenNotPaused() {
require(!_pausable().paused(), "Pausable: paused");
_;
}
modifier whenPaused() {
require(_pausable().paused(), "Pausable: not paused");
_;
}
function ctrlPaused() public view returns (bool) {
return _pausable().paused();
}
function setController(address newController) external onlyOwner {
require(newController != address(0), "setController: !0x");
address oldController = controller;
controller = newController;
emit ChangeController(oldController, newController);
}
function _controller() internal view returns (IController) {
require(controller != address(0), "Controller not set");
return IController(controller);
}
function _pausable() internal view returns (IPausable) {
require(controller != address(0), "Controller not set");
return IPausable(controller);
}
}
pragma solidity >=0.6.0 <0.7.0;
interface IERC20Detailed {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
}
pragma solidity >=0.6.0 <0.7.0;
interface ILifeGuard {
function assets(uint256 i) external view returns (uint256);
function totalAssets() external view returns (uint256);
function getAssets() external view returns (uint256[3] memory);
function totalAssetsUsd() external view returns (uint256);
function availableUsd() external view returns (uint256 dollar);
function availableLP() external view returns (uint256);
function depositStable(bool rebalance) external returns (uint256);
function investToCurveVault() external;
function distributeCurveVault(uint256 amount, uint256[3] memory delta) external returns (uint256[3] memory);
function deposit() external returns (uint256 usdAmount);
function withdrawSingleByLiquidity(
uint256 i,
uint256 minAmount,
address recipient
) external returns (uint256 usdAmount, uint256 amount);
function withdrawSingleByExchange(
uint256 i,
uint256 minAmount,
address recipient
) external returns (uint256 usdAmount, uint256 amount);
function invest(uint256 whaleDepositAmount, uint256[3] calldata delta) external returns (uint256 dollarAmount);
function getBuoy() external view returns (address);
function investSingle(
uint256[3] calldata inAmounts,
uint256 i,
uint256 j
) external returns (uint256 dollarAmount);
function investToCurveVaultTrigger() external view returns (bool _invest);
}
pragma solidity >=0.6.0 <0.7.0;
pragma experimental ABIEncoderV2;
struct SystemState {
uint256 totalCurrentAssetsUsd;
uint256 curveCurrentAssetsUsd;
uint256 lifeguardCurrentAssetsUsd;
uint256[3] vaultCurrentAssets;
uint256[3] vaultCurrentAssetsUsd;
uint256 rebalanceThreshold;
uint256 utilisationRatio;
uint256 targetBuffer;
uint256[3] stablePercents;
uint256 curvePercent;
}
struct ExposureState {
uint256[3] stablecoinExposure;
uint256[] protocolExposure;
uint256 curveExposure;
bool stablecoinExposed;
bool protocolExposed;
}
struct AllocationState {
uint256[] strategyTargetRatio;
bool needProtocolWithdrawal;
uint256 protocolExposedIndex;
uint256[3] protocolWithdrawalUsd;
StablecoinAllocationState stableState;
}
struct StablecoinAllocationState {
uint256 swapInTotalAmountUsd;
uint256[3] swapInAmounts;
uint256[3] swapInAmountsUsd;
uint256[3] swapOutPercents;
uint256[3] vaultsTargetUsd;
uint256 curveTargetUsd;
uint256 curveTargetDeltaUsd;
}
pragma solidity >=0.6.0 <0.7.0;
interface IExposure {
function calcRiskExposure(SystemState calldata sysState) external view returns (ExposureState memory expState);
function getExactRiskExposure(SystemState calldata sysState) external view returns (ExposureState memory expState);
function getUnifiedAssets(address[3] calldata vaults)
external
view
returns (uint256 unifiedTotalAssets, uint256[3] memory unifiedAssets);
function sortVaultsByDelta(
bool bigFirst,
uint256 unifiedTotalAssets,
uint256[3] calldata unifiedAssets,
uint256[3] calldata targetPercents
) external pure returns (uint256[3] memory vaultIndexes);
function calcRoughDelta(
uint256[3] calldata targets,
address[3] calldata vaults,
uint256 withdrawUsd
) external view returns (uint256[3] memory);
}
pragma solidity >=0.6.0 <0.7.0;
interface IVault {
function withdraw(uint256 amount) external;
function withdraw(uint256 amount, address recipient) external;
function withdrawByStrategyOrder(
uint256 amount,
address recipient,
bool reversed
) external;
function withdrawByStrategyIndex(
uint256 amount,
address recipient,
uint256 strategyIndex
) external;
function deposit(uint256 amount) external;
function updateStrategyRatio(uint256[] calldata strategyRetios) external;
function totalAssets() external view returns (uint256);
function getStrategiesLength() external view returns (uint256);
function strategyHarvestTrigger(uint256 index, uint256 callCost) external view returns (bool);
function strategyHarvest(uint256 index) external returns (bool);
function getStrategyAssets(uint256 index) external view returns (uint256);
function token() external view returns (address);
function vault() external view returns (address);
function investTrigger() external view returns (bool);
function invest() external;
}
pragma solidity >=0.6.0 <0.7.0;
interface IBuoy {
function safetyCheck() external view returns (bool);
function updateRatios() external returns (bool);
function updateRatiosWithTolerance(uint256 tolerance) external returns (bool);
function lpToUsd(uint256 inAmount) external view returns (uint256);
function usdToLp(uint256 inAmount) external view returns (uint256);
function stableToUsd(uint256[3] calldata inAmount, bool deposit) external view returns (uint256);
function stableToLp(uint256[3] calldata inAmount, bool deposit) external view returns (uint256);
function singleStableFromLp(uint256 inAmount, int128 i) external view returns (uint256);
function getVirtualPrice() external view returns (uint256);
function singleStableFromUsd(uint256 inAmount, int128 i) external view returns (uint256);
function singleStableToUsd(uint256 inAmount, uint256 i) external view returns (uint256);
}
pragma solidity >=0.6.0 <0.7.0;
contract Exposure is Constants, Controllable, IExposure {
using SafeMath for uint256;
uint256 public protocolCount;
uint256 public makerUSDCExposure;
event LogNewProtocolCount(uint256 count);
event LogNewMakerExposure(uint256 exposure);
function setProtocolCount(uint256 _protocolCount) external onlyOwner {
protocolCount = _protocolCount;
emit LogNewProtocolCount(_protocolCount);
}
function setMakerUSDCExposure(uint256 _makerUSDCExposure) external onlyOwner {
makerUSDCExposure = _makerUSDCExposure;
emit LogNewMakerExposure(_makerUSDCExposure);
}
function getExactRiskExposure(SystemState calldata sysState)
external
view
override
returns (ExposureState memory expState)
{
expState = _calcRiskExposure(sysState, false);
ILifeGuard lifeguard = ILifeGuard(_controller().lifeGuard());
IBuoy buoy = IBuoy(_controller().buoy());
for (uint256 i = 0; i < N_COINS; i++) {
uint256 assets = lifeguard.assets(i);
uint256 assetsUsd = buoy.singleStableToUsd(assets, i);
expState.stablecoinExposure[i] = expState.stablecoinExposure[i].add(
assetsUsd.mul(PERCENTAGE_DECIMAL_FACTOR).div(sysState.totalCurrentAssetsUsd)
);
}
}
function calcRiskExposure(SystemState calldata sysState)
external
view
override
returns (ExposureState memory expState)
{
expState = _calcRiskExposure(sysState, true);
(expState.stablecoinExposed, expState.protocolExposed) = isExposed(
sysState.rebalanceThreshold,
expState.stablecoinExposure,
expState.protocolExposure,
expState.curveExposure
);
}
function getUnifiedAssets(address[N_COINS] calldata vaults)
public
view
override
returns (uint256 unifiedTotalAssets, uint256[N_COINS] memory unifiedAssets)
{
for (uint256 i = 0; i < N_COINS; i++) {
uint256 assets = IVault(vaults[i]).totalAssets();
unifiedAssets[i] = assets.mul(DEFAULT_DECIMALS_FACTOR).div(
uint256(10)**IERC20Detailed(IVault(vaults[i]).token()).decimals()
);
unifiedTotalAssets = unifiedTotalAssets.add(unifiedAssets[i]);
}
}
function calcRoughDelta(
uint256[N_COINS] calldata targets,
address[N_COINS] calldata vaults,
uint256 withdrawUsd
) external view override returns (uint256[N_COINS] memory delta) {
(uint256 totalAssets, uint256[N_COINS] memory vaultTotalAssets) = getUnifiedAssets(vaults);
require(totalAssets > withdrawUsd, "totalAssets < withdrawalUsd");
totalAssets = totalAssets.sub(withdrawUsd);
uint256 totalDelta;
for (uint256 i; i < N_COINS; i++) {
uint256 target = totalAssets.mul(targets[i]).div(PERCENTAGE_DECIMAL_FACTOR);
if (vaultTotalAssets[i] > target) {
delta[i] = vaultTotalAssets[i].sub(target);
totalDelta = totalDelta.add(delta[i]);
}
}
uint256 percent = PERCENTAGE_DECIMAL_FACTOR;
for (uint256 i; i < N_COINS - 1; i++) {
if (delta[i] > 0) {
delta[i] = delta[i].mul(PERCENTAGE_DECIMAL_FACTOR).div(totalDelta);
percent = percent.sub(delta[i]);
}
}
delta[N_COINS - 1] = percent;
return delta;
}
function sortVaultsByDelta(
bool bigFirst,
uint256 unifiedTotalAssets,
uint256[N_COINS] calldata unifiedAssets,
uint256[N_COINS] calldata targetPercents
) external pure override returns (uint256[N_COINS] memory vaultIndexes) {
uint256 maxIndex;
uint256 minIndex;
int256 maxDelta = type(int256).min;
int256 minDelta = type(int256).max;
for (uint256 i = 0; i < N_COINS; i++) {
int256 delta = int256(unifiedAssets[i]) -
int256(unifiedTotalAssets.mul(targetPercents[i]).div(PERCENTAGE_DECIMAL_FACTOR));
if (delta > maxDelta) {
maxDelta = delta;
maxIndex = i;
}
if (delta < minDelta) {
minDelta = delta;
minIndex = i;
}
}
if (bigFirst) {
vaultIndexes[0] = maxIndex;
vaultIndexes[2] = minIndex;
} else {
vaultIndexes[0] = minIndex;
vaultIndexes[2] = maxIndex;
}
vaultIndexes[1] = N_COINS - maxIndex - minIndex;
}
function calculatePercentOfSystem(
address vault,
uint256 index,
uint256 vaultAssetsPercent,
uint256 vaultAssets
) private view returns (uint256 percentOfSystem) {
if (vaultAssets == 0) return 0;
uint256 strategyAssetsPercent = IVault(vault).getStrategyAssets(index).mul(PERCENTAGE_DECIMAL_FACTOR).div(
vaultAssets
);
percentOfSystem = vaultAssetsPercent.mul(strategyAssetsPercent).div(PERCENTAGE_DECIMAL_FACTOR);
}
function calculateStableCoinExposure(uint256[N_COINS] memory directlyExposure, uint256 curveExposure)
private
view
returns (uint256[N_COINS] memory stableCoinExposure)
{
uint256 maker = directlyExposure[0].mul(makerUSDCExposure).div(PERCENTAGE_DECIMAL_FACTOR);
for (uint256 i = 0; i < N_COINS; i++) {
uint256 indirectExposure = curveExposure;
if (i == 1) {
indirectExposure = indirectExposure.add(maker);
}
stableCoinExposure[i] = directlyExposure[i].add(indirectExposure);
}
}
function isExposed(
uint256 rebalanceThreshold,
uint256[N_COINS] memory stableCoinExposure,
uint256[] memory protocolExposure,
uint256 curveExposure
) private pure returns (bool stablecoinExposed, bool protocolExposed) {
for (uint256 i = 0; i < N_COINS; i++) {
if (stableCoinExposure[i] > rebalanceThreshold) {
stablecoinExposed = true;
break;
}
}
for (uint256 i = 0; i < protocolExposure.length; i++) {
if (protocolExposure[i] > rebalanceThreshold) {
protocolExposed = true;
break;
}
}
if (!protocolExposed && curveExposure > rebalanceThreshold) protocolExposed = true;
return (stablecoinExposed, protocolExposed);
}
function _calcRiskExposure(SystemState memory sysState, bool treatLifeguardAsCurve)
private
view
returns (ExposureState memory expState)
{
address[N_COINS] memory vaults = _controller().vaults();
uint256 pCount = protocolCount;
expState.protocolExposure = new uint256[](pCount);
if (sysState.totalCurrentAssetsUsd == 0) {
return expState;
}
for (uint256 i = 0; i < N_COINS; i++) {
uint256 vaultAssetsPercent = sysState.vaultCurrentAssetsUsd[i].mul(PERCENTAGE_DECIMAL_FACTOR).div(
sysState.totalCurrentAssetsUsd
);
expState.stablecoinExposure[i] = vaultAssetsPercent;
for (uint256 j = 0; j < pCount; j++) {
uint256 percentOfSystem = calculatePercentOfSystem(
vaults[i],
j,
vaultAssetsPercent,
sysState.vaultCurrentAssets[i]
);
expState.protocolExposure[j] = expState.protocolExposure[j].add(percentOfSystem);
}
}
if (treatLifeguardAsCurve) {
expState.curveExposure = sysState.curveCurrentAssetsUsd.add(sysState.lifeguardCurrentAssetsUsd);
} else {
expState.curveExposure = sysState.curveCurrentAssetsUsd;
}
expState.curveExposure = expState.curveExposure.mul(PERCENTAGE_DECIMAL_FACTOR).div(
sysState.totalCurrentAssetsUsd
);
expState.stablecoinExposure = calculateStableCoinExposure(expState.stablecoinExposure, expState.curveExposure);
}
}