Contract Source Code:
File 1 of 1 : Controller
// SPDX-License-Identifier: AGPLv3
pragma solidity >=0.6.0 <0.8.0;
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
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;
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(_owner == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
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.8.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
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;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be 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.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https:
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
pragma solidity >=0.6.2 <0.8.0;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(account) }
return size > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https:
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https:
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https:
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain`call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https:
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
pragma solidity >=0.6.0 <0.8.0;
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
constructor () internal {
_paused = false;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view returns (bool) {
return _paused;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
require(!_paused, "Pausable: paused");
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
require(_paused, "Pausable: not paused");
_;
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
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.7.0;
interface IToken {
function factor() external view returns (uint256);
function factor(uint256 totalAssets) external view returns (uint256);
function mint(
address account,
uint256 _factor,
uint256 amount
) external;
function burn(
address account,
uint256 _factor,
uint256 amount
) external;
function burnAll(address account) external;
function totalAssets() external view returns (uint256);
function getPricePerShare() external view returns (uint256);
function getShareAssets(uint256 shares) external view returns (uint256);
function getAssets(address account) external view returns (uint256);
}
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;
contract FixedStablecoins is Constants {
address public immutable DAI;
address public immutable USDC;
address public immutable USDT;
uint256 public immutable DAI_DECIMALS;
uint256 public immutable USDC_DECIMALS;
uint256 public immutable USDT_DECIMALS;
constructor(address[N_COINS] memory _tokens, uint256[N_COINS] memory _decimals) public {
DAI = _tokens[0];
USDC = _tokens[1];
USDT = _tokens[2];
DAI_DECIMALS = _decimals[0];
USDC_DECIMALS = _decimals[1];
USDT_DECIMALS = _decimals[2];
}
function underlyingTokens() internal view returns (address[N_COINS] memory tokens) {
tokens[0] = DAI;
tokens[1] = USDC;
tokens[2] = USDT;
}
function getToken(uint256 index) internal view returns (address) {
if (index == 0) {
return DAI;
} else if (index == 1) {
return USDC;
} else {
return USDT;
}
}
function decimals() internal view returns (uint256[N_COINS] memory _decimals) {
_decimals[0] = DAI_DECIMALS;
_decimals[1] = USDC_DECIMALS;
_decimals[2] = USDT_DECIMALS;
}
function getDecimal(uint256 index) internal view returns (uint256) {
if (index == 0) {
return DAI_DECIMALS;
} else if (index == 1) {
return USDC_DECIMALS;
} else {
return USDT_DECIMALS;
}
}
}
contract FixedGTokens {
IToken public immutable pwrd;
IToken public immutable gvt;
constructor(address _pwrd, address _gvt) public {
pwrd = IToken(_pwrd);
gvt = IToken(_gvt);
}
function gTokens(bool _pwrd) internal view returns (IToken) {
if (_pwrd) {
return pwrd;
} else {
return gvt;
}
}
}
contract FixedVaults is Constants {
address public immutable DAI_VAULT;
address public immutable USDC_VAULT;
address public immutable USDT_VAULT;
constructor(address[N_COINS] memory _vaults) public {
DAI_VAULT = _vaults[0];
USDC_VAULT = _vaults[1];
USDT_VAULT = _vaults[2];
}
function getVault(uint256 index) internal view returns (address) {
if (index == 0) {
return DAI_VAULT;
} else if (index == 1) {
return USDC_VAULT;
} else {
return USDT_VAULT;
}
}
function vaults() internal view returns (address[N_COINS] memory _vaults) {
_vaults[0] = DAI_VAULT;
_vaults[1] = USDC_VAULT;
_vaults[2] = USDT_VAULT;
}
}
pragma solidity >=0.6.0 <0.7.0;
contract Whitelist is Ownable {
mapping(address => bool) public whitelist;
event LogAddToWhitelist(address indexed user);
event LogRemoveFromWhitelist(address indexed user);
modifier onlyWhitelist() {
require(whitelist[msg.sender], "only whitelist");
_;
}
function addToWhitelist(address user) external onlyOwner {
require(user != address(0), "WhiteList: 0x");
whitelist[user] = true;
emit LogAddToWhitelist(user);
}
function removeFromWhitelist(address user) external onlyOwner {
require(user != address(0), "WhiteList: 0x");
whitelist[user] = false;
emit LogRemoveFromWhitelist(user);
}
}
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;
interface IChainPrice {
function getPriceFeed(uint256 i) external view returns (uint256 _price);
}
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 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 IInsurance {
function calculateDepositDeltasOnAllVaults() external view returns (uint256[3] memory);
function rebalanceTrigger() external view returns (bool sysNeedRebalance);
function rebalance() external;
function calcSkim() external view returns (uint256);
function rebalanceForWithdraw(uint256 withdrawUsd, bool pwrd) external returns (bool);
function getDelta(uint256 withdrawUsd) external view returns (uint256[3] memory delta);
function getVaultDeltaForDeposit(uint256 amount) external view returns (uint256[3] memory, uint256);
function sortVaultsByDelta(bool bigFirst) external view returns (uint256[3] memory vaultIndexes);
function getStrategiesTargetRatio(uint256 utilRatio) external view returns (uint256[] memory);
function setUnderlyingTokenPercents(uint256[3] calldata percents) external;
}
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;
interface IPnL {
function calcPnL() external view returns (uint256, uint256);
function increaseGTokenLastAmount(bool pwrd, uint256 dollarAmount) external;
function decreaseGTokenLastAmount(
bool pwrd,
uint256 dollarAmount,
uint256 bonus
) external;
function lastGvtAssets() external view returns (uint256);
function lastPwrdAssets() external view returns (uint256);
function utilisationRatio() external view returns (uint256);
function emergencyPnL() external;
function recover() external;
function distributeStrategyGainLoss(
uint256 gain,
uint256 loss,
address reward
) external;
function distributePriceChange(uint256 currentTotalAssets) external;
}
pragma solidity >=0.6.0 <0.7.0;
contract Controller is Pausable, Ownable, Whitelist, FixedStablecoins, FixedGTokens, IController {
using SafeERC20 for IERC20;
using SafeMath for uint256;
address public override curveVault;
bool public preventSmartContracts;
bool public override emergencyState;
bool public pendingOwnershipRemoval;
address public override insurance;
address public override pnl;
address public override lifeGuard;
address public override buoy;
address public override depositHandler;
address public override withdrawHandler;
address public override emergencyHandler;
uint256 public override deadCoin = N_COINS;
uint256 public utilisationRatioLimitGvt;
uint256 public utilisationRatioLimitPwrd;
uint256 public bigFishThreshold = 100;
uint256 public bigFishAbsoluteThreshold = 0;
address public override reward;
address public pendingOwner;
mapping(address => bool) public safeAddresses;
mapping(uint256 => address) public override underlyingVaults;
mapping(address => uint256) public vaultIndexes;
mapping(address => address) public override referrals;
mapping(bool => uint256) public override withdrawalFee;
event LogNewWithdrawHandler(address withdrawHandler, address emergencyHandler);
event LogNewDepositHandler(address tokens);
event LogNewVault(uint256 index, address vault);
event LogNewCurveVault(address curveVault);
event LogNewLifeguard(address lifeguard);
event LogNewInsurance(address insurance);
event LogNewPnl(address pnl);
event LogNewBigFishThreshold(uint256 percent, uint256 absolute);
event LogEoaOnly(bool status);
event LogUpdateSafeAddress(address account, bool status);
event LogNewRewardsContract(address reward);
event LogNewUtilLimit(bool indexed pwrd, uint256 limit);
event LogNewCurveToStableDistribution(uint256 amount, uint256[N_COINS] amounts, uint256[N_COINS] delta);
event LogNewWithdrawalFee(address user, bool pwrd, uint256 newFee);
event LogNewPendingOwnershipRemoval(bool status);
event LogNewPendingOwnershipChange(address pendingOwner);
event LogContractRetired(address owner);
constructor(
address pwrd,
address gvt,
address[N_COINS] memory _tokens,
uint256[N_COINS] memory _decimals
) public FixedStablecoins(_tokens, _decimals) FixedGTokens(pwrd, gvt) {}
function pause() external onlyWhitelist {
_pause();
}
function unpause() external onlyOwner {
_unpause();
}
function prepareRenounceOwnership(bool status) external onlyOwner {
require(pendingOwnershipRemoval != status, "prepareRenounceOwnership: status");
emit LogNewPendingOwnershipRemoval(status);
pendingOwnershipRemoval = status;
}
function renounceOwnership() public override onlyOwner {
require(pendingOwnershipRemoval, "renounceOwnership: !pendingOwnershipRemoval");
insurance = address(0);
pnl = address(0);
lifeGuard = address(0);
buoy = address(0);
depositHandler = address(0);
withdrawHandler = address(0);
emergencyHandler = address(0);
pendingOwnershipRemoval = false;
address lastOwner = owner();
super.renounceOwnership();
emit LogContractRetired(lastOwner);
}
function prepareTransferOwnership(address newOwner) external onlyOwner {
emit LogNewPendingOwnershipChange(newOwner);
pendingOwner = newOwner;
}
function transferOwnership(address newOwner) public override onlyOwner {
require(pendingOwner != address(0), "transferOwnership: 0x");
super.transferOwnership(pendingOwner);
pendingOwner = address(0);
}
function setWithdrawHandler(address _withdrawHandler, address _emergencyHandler) external onlyOwner {
require(_withdrawHandler != address(0) && _emergencyHandler != address(0), "setWithdrawHandler: 0x");
withdrawHandler = _withdrawHandler;
emergencyHandler = _emergencyHandler;
emit LogNewWithdrawHandler(_withdrawHandler, _emergencyHandler);
}
function setDepositHandler(address _depositHandler) external onlyOwner {
require(_depositHandler != address(0), "setDepositHandler: 0x");
depositHandler = _depositHandler;
emit LogNewDepositHandler(_depositHandler);
}
function stablecoins() external view override returns (address[N_COINS] memory) {
return underlyingTokens();
}
function getSkimPercent() external view override returns (uint256) {
return IInsurance(insurance).calcSkim();
}
function vaults() external view override returns (address[N_COINS] memory) {
address[N_COINS] memory result;
for (uint256 i = 0; i < N_COINS; i++) {
result[i] = underlyingVaults[i];
}
return result;
}
function setVault(uint256 index, address vault) external onlyOwner {
require(vault != address(0), "setVault: 0x");
require(index < N_COINS, "setVault: !index");
underlyingVaults[index] = vault;
vaultIndexes[vault] = index + 1;
emit LogNewVault(index, vault);
}
function setCurveVault(address _curveVault) external onlyOwner {
require(_curveVault != address(0), "setCurveVault: 0x");
curveVault = _curveVault;
vaultIndexes[_curveVault] = N_COINS + 1;
emit LogNewCurveVault(_curveVault);
}
function setLifeGuard(address _lifeGuard) external onlyOwner {
require(_lifeGuard != address(0), "setLifeGuard: 0x");
lifeGuard = _lifeGuard;
buoy = ILifeGuard(_lifeGuard).getBuoy();
emit LogNewLifeguard(_lifeGuard);
}
function setInsurance(address _insurance) external onlyOwner {
require(_insurance != address(0), "setInsurance: 0x");
insurance = _insurance;
emit LogNewInsurance(_insurance);
}
function setPnL(address _pnl) external onlyOwner {
require(_pnl != address(0), "setPnl: 0x");
pnl = _pnl;
emit LogNewPnl(_pnl);
}
function updateSafeAddress(address account, bool status) external onlyOwner {
safeAddresses[account] = status;
emit LogUpdateSafeAddress(account, status);
}
function switchEoaOnly(bool check) external onlyOwner {
preventSmartContracts = check;
emit LogEoaOnly(check);
}
function setBigFishThreshold(uint256 _percent, uint256 _absolute) external onlyOwner {
require(_percent > 0, "_whaleLimit is 0");
bigFishThreshold = _percent;
bigFishAbsoluteThreshold = _absolute;
emit LogNewBigFishThreshold(_percent, _absolute);
}
function setReward(address _reward) external onlyOwner {
require(_reward != address(0), "setReward: 0x");
reward = _reward;
emit LogNewRewardsContract(_reward);
}
function addReferral(address account, address referral) external override {
require(msg.sender == depositHandler, "!depositHandler");
if (referral != address(0) && referrals[account] == address(0)) {
referrals[account] = referral;
}
}
function setWithdrawalFee(bool pwrd, uint256 newFee) external onlyOwner {
withdrawalFee[pwrd] = newFee;
emit LogNewWithdrawalFee(msg.sender, pwrd, newFee);
}
function totalAssets() external view override returns (uint256) {
return emergencyState ? _totalAssetsEmergency() : _totalAssets();
}
function gTokenTotalAssets() public view override returns (uint256) {
(uint256 gvtAssets, uint256 pwrdAssets) = IPnL(pnl).calcPnL();
if (msg.sender == address(gvt)) {
return gvtAssets;
}
if (msg.sender == address(pwrd)) {
return pwrdAssets;
}
return 0;
}
function gToken(bool isPWRD) external view override returns (address) {
return isPWRD ? address(pwrd) : address(gvt);
}
function isValidBigFish(
bool pwrd,
bool deposit,
uint256 amount
) external view override returns (bool) {
if (deposit && pwrd) {
require(validGTokenIncrease(amount), "isBigFish: !validGTokenIncrease");
} else if (!pwrd && !deposit) {
require(_validGTokenDecrease(amount), "isBigFish: !validGTokenDecrease");
}
if (amount < bigFishAbsoluteThreshold) {
return false;
} else {
(uint256 gvtAssets, uint256 pwrdAssets) = IPnL(pnl).calcPnL();
uint256 assets = pwrdAssets.add(gvtAssets);
if (amount > assets) {
return true;
} else {
return amount > assets.mul(bigFishThreshold).div(PERCENTAGE_DECIMAL_FACTOR);
}
}
}
function distributeCurveAssets(uint256 amount, uint256[N_COINS] memory delta) external onlyWhitelist {
uint256[N_COINS] memory amounts = ILifeGuard(lifeGuard).distributeCurveVault(amount, delta);
emit LogNewCurveToStableDistribution(amount, amounts, delta);
}
function eoaOnly(address sender) external override {
if (preventSmartContracts && !safeAddresses[tx.origin]) {
require(sender == tx.origin, "EOA only");
}
}
function _totalAssets() private view returns (uint256) {
require(IBuoy(buoy).safetyCheck(), "!buoy.safetyCheck");
uint256[N_COINS] memory lgAssets = ILifeGuard(lifeGuard).getAssets();
uint256[N_COINS] memory vaultAssets;
for (uint256 i = 0; i < N_COINS; i++) {
vaultAssets[i] = lgAssets[i].add(IVault(underlyingVaults[i]).totalAssets());
}
uint256 totalLp = IVault(curveVault).totalAssets();
totalLp = totalLp.add(IBuoy(buoy).stableToLp(vaultAssets, true));
uint256 vp = IBuoy(buoy).getVirtualPrice();
return totalLp.mul(vp).div(DEFAULT_DECIMALS_FACTOR);
}
function _totalAssetsEmergency() private view returns (uint256) {
IChainPrice chainPrice = IChainPrice(buoy);
uint256 total;
for (uint256 i = 0; i < N_COINS; i++) {
if (i != deadCoin) {
address tokenAddress = getToken(i);
uint256 decimals = getDecimal(i);
IERC20 token = IERC20(tokenAddress);
uint256 price = chainPrice.getPriceFeed(i);
uint256 assets = IVault(underlyingVaults[i]).totalAssets().add(token.balanceOf(lifeGuard));
assets = assets.mul(price).div(CHAINLINK_PRICE_DECIMAL_FACTOR);
assets = assets.mul(DEFAULT_DECIMALS_FACTOR).div(decimals);
total = total.add(assets);
}
}
return total;
}
function emergency(uint256 coin) external onlyWhitelist {
require(coin <= N_COINS, "invalid coin");
if (!paused()) {
_pause();
}
emergencyState = true;
if (coin < N_COINS) {
uint256[N_COINS] memory percents;
percents[coin] = PERCENTAGE_DECIMAL_FACTOR;
IInsurance(insurance).setUnderlyingTokenPercents(percents);
deadCoin = coin;
}
IPnL(pnl).emergencyPnL();
}
function restart(uint256[N_COINS] calldata allocations) external onlyOwner whenPaused {
_unpause();
deadCoin = N_COINS;
emergencyState = false;
IInsurance(insurance).setUnderlyingTokenPercents(allocations);
IPnL(pnl).recover();
}
function distributeStrategyGainLoss(uint256 gain, uint256 loss) external override {
uint256 index = vaultIndexes[msg.sender];
require(index > 0 && index <= N_COINS + 1, "!VaultAdaptor");
IPnL ipnl = IPnL(pnl);
IBuoy ibuoy = IBuoy(buoy);
uint256 gainUsd;
uint256 lossUsd;
index = index - 1;
if (index < N_COINS) {
if (gain > 0) {
gainUsd = ibuoy.singleStableToUsd(gain, index);
} else if (loss > 0) {
lossUsd = ibuoy.singleStableToUsd(loss, index);
}
} else {
if (gain > 0) {
gainUsd = ibuoy.lpToUsd(gain);
} else if (loss > 0) {
lossUsd = ibuoy.lpToUsd(loss);
}
}
ipnl.distributeStrategyGainLoss(gainUsd, lossUsd, reward);
if (ibuoy.updateRatios()) {
ipnl.distributePriceChange(_totalAssets());
}
}
function realizePriceChange(uint256 tolerance) external onlyOwner {
IPnL ipnl = IPnL(pnl);
IBuoy ibuoy = IBuoy(buoy);
if (emergencyState) {
ipnl.distributePriceChange(_totalAssetsEmergency());
} else {
if (ibuoy.updateRatiosWithTolerance(tolerance)) {
ipnl.distributePriceChange(_totalAssets());
}
}
}
function burnGToken(
bool pwrd,
bool all,
address account,
uint256 amount,
uint256 bonus
) external override {
require(
msg.sender == withdrawHandler || msg.sender == emergencyHandler,
"burnGToken: !withdrawHandler|emergencyHandler"
);
IToken gt = gTokens(pwrd);
if (!all) {
gt.burn(account, gt.factor(), amount);
} else {
gt.burnAll(account);
}
IPnL(pnl).decreaseGTokenLastAmount(pwrd, amount, bonus);
}
function mintGToken(
bool pwrd,
address account,
uint256 amount
) external override {
require(msg.sender == depositHandler, "burnGToken: !depositHandler");
IToken gt = gTokens(pwrd);
gt.mint(account, gt.factor(), amount);
IPnL(pnl).increaseGTokenLastAmount(pwrd, amount);
}
function getUserAssets(bool pwrd, address account) external view override returns (uint256 deductUsd) {
IToken gt = gTokens(pwrd);
deductUsd = gt.getAssets(account);
require(deductUsd > 0, "!minAmount");
}
function validGTokenIncrease(uint256 amount) private view returns (bool) {
return
gTokens(false).totalAssets().mul(utilisationRatioLimitPwrd).div(PERCENTAGE_DECIMAL_FACTOR) >=
amount.add(gTokens(true).totalAssets());
}
function _validGTokenDecrease(uint256 amount) private view returns (bool) {
return
gTokens(false).totalAssets().sub(amount).mul(utilisationRatioLimitGvt).div(PERCENTAGE_DECIMAL_FACTOR) >=
gTokens(true).totalAssets();
}
function validGTokenDecrease(uint256 amount) external view override returns (bool) {
return _validGTokenDecrease(amount);
}
function setUtilisationRatioLimitPwrd(uint256 _utilisationRatioLimitPwrd) external onlyOwner {
utilisationRatioLimitPwrd = _utilisationRatioLimitPwrd;
emit LogNewUtilLimit(true, _utilisationRatioLimitPwrd);
}
function setUtilisationRatioLimitGvt(uint256 _utilisationRatioLimitGvt) external onlyOwner {
utilisationRatioLimitGvt = _utilisationRatioLimitGvt;
emit LogNewUtilLimit(false, _utilisationRatioLimitGvt);
}
function getStrategiesTargetRatio() external view override returns (uint256[] memory) {
uint256 utilRatio = IPnL(pnl).utilisationRatio();
return IInsurance(insurance).getStrategiesTargetRatio(utilRatio);
}
}