// SPDX-License-Identifier: MIT
pragma experimental ABIEncoderV2;
pragma solidity 0.6.12;
//
/**
* @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://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* 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);
}
//
/**
* @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) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
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;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
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;
}
}
//
/**
* @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) {
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
// for accounts without code, i.e. `keccak256('')`
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
// solhint-disable-next-line no-inline-assembly
assembly { codehash := extcodehash(account) }
return (codehash != accountHash && codehash != 0x0);
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* 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://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(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://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* 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");
return _functionCallWithValue(target, data, value, errorMessage);
}
function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
require(isContract(target), "Address: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
//
/**
* @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 {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
// solhint-disable-next-line max-line-length
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 {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) { // Return data is optional
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
//
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor () internal {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
//
/******************
@title WadRayMath library
@author Aave
@dev Provides mul and div function for wads (decimal numbers with 18 digits precision) and rays (decimals with 27 digits)
*/
library WadRayMath {
using SafeMath for uint256;
uint256 internal constant WAD = 1e18;
uint256 internal constant halfWAD = WAD / 2;
uint256 internal constant RAY = 1e27;
uint256 internal constant halfRAY = RAY / 2;
uint256 internal constant WAD_RAY_RATIO = 1e9;
function ray() internal pure returns (uint256) {
return RAY;
}
function wad() internal pure returns (uint256) {
return WAD;
}
function halfRay() internal pure returns (uint256) {
return halfRAY;
}
function halfWad() internal pure returns (uint256) {
return halfWAD;
}
function wadMul(uint256 a, uint256 b) internal pure returns (uint256) {
return halfWAD.add(a.mul(b)).div(WAD);
}
function wadDiv(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 halfB = b / 2;
return halfB.add(a.mul(WAD)).div(b);
}
function rayMul(uint256 a, uint256 b) internal pure returns (uint256) {
return halfRAY.add(a.mul(b)).div(RAY);
}
function rayDiv(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 halfB = b / 2;
return halfB.add(a.mul(RAY)).div(b);
}
function rayToWad(uint256 a) internal pure returns (uint256) {
uint256 halfRatio = WAD_RAY_RATIO / 2;
return halfRatio.add(a).div(WAD_RAY_RATIO);
}
function wadToRay(uint256 a) internal pure returns (uint256) {
return a.mul(WAD_RAY_RATIO);
}
/**
* @dev calculates x^n, in ray. The code uses the ModExp precompile
* @param x base
* @param n exponent
* @return z = x^n, in ray
*/
function rayPow(uint256 x, uint256 n) internal pure returns (uint256 z) {
z = n % 2 != 0 ? x : RAY;
for (n /= 2; n != 0; n /= 2) {
x = rayMul(x, x);
if (n % 2 != 0) {
z = rayMul(z, x);
}
}
}
}
//
interface IAccessController {
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
function MANAGER_ROLE() external view returns (bytes32);
function MINTER_ROLE() external view returns (bytes32);
function hasRole(bytes32 role, address account) external view returns (bool);
function getRoleMemberCount(bytes32 role) external view returns (uint256);
function getRoleMember(bytes32 role, uint256 index) external view returns (address);
function getRoleAdmin(bytes32 role) external view returns (bytes32);
function grantRole(bytes32 role, address account) external;
function revokeRole(bytes32 role, address account) external;
function renounceRole(bytes32 role, address account) external;
}
//
interface IConfigProvider {
struct CollateralConfig {
address collateralType;
uint256 debtLimit;
uint256 minCollateralRatio;
uint256 borrowRate;
uint256 originationFee;
}
function a() external view returns (IAddressProvider);
function collateralConfigs(uint256 _id) external view returns (CollateralConfig memory);
function collateralIds(address _collateralType) external view returns (uint256);
function numCollateralConfigs() external view returns (uint256);
function liquidationBonus() external view returns (uint256);
event CollateralUpdated(
address indexed collateralType,
uint256 debtLimit,
uint256 minCollateralRatio,
uint256 borrowRate,
uint256 originationFee
);
event CollateralRemoved(address indexed collateralType);
function setCollateralConfig(
address _collateralType,
uint256 _debtLimit,
uint256 _minCollateralRatio,
uint256 _borrowRate,
uint256 _originationFee
) external;
function removeCollateral(address _collateralType) external;
function setCollateralDebtLimit(address _collateralType, uint256 _debtLimit) external;
function setCollateralMinCollateralRatio(address _collateralType, uint256 _minCollateralRatio) external;
function setCollateralBorrowRate(address _collateralType, uint256 _borrowRate) external;
function setCollateralOriginationFee(address _collateralType, uint256 _originationFee) external;
function setLiquidationBonus(uint256 _bonus) external;
function collateralDebtLimit(address _collateralType) external view returns (uint256);
function collateralMinCollateralRatio(address _collateralType) external view returns (uint256);
function collateralBorrowRate(address _collateralType) external view returns (uint256);
function collateralOriginationFee(address _collateralType) external view returns (uint256);
}
//
interface ISTABLEX is IERC20 {
function a() external view returns (IAddressProvider);
function mint(address account, uint256 amount) external;
function burn(address account, uint256 amount) external;
}
//
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(uint80 _roundId)
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}
//
interface IPriceFeed {
event OracleUpdated(address indexed asset, address oracle);
function a() external view returns (IAddressProvider);
function setAssetOracle(address _asset, address _oracle) external;
function assetOracles(address _asset) external view returns (AggregatorV3Interface);
function getAssetPrice(address _asset) external view returns (uint256);
function convertFrom(address _asset, uint256 _balance) external view returns (uint256);
function convertTo(address _asset, uint256 _balance) external view returns (uint256);
}
//
interface IRatesManager {
function a() external view returns (IAddressProvider);
//current annualized borrow rate
function annualizedBorrowRate(uint256 _currentBorrowRate) external pure returns (uint256);
//uses current cumulative rate to calculate totalDebt based on baseDebt at time T0
function calculateDebt(uint256 _baseDebt, uint256 _cumulativeRate) external pure returns (uint256);
//uses current cumulative rate to calculate baseDebt at time T0
function calculateBaseDebt(uint256 _debt, uint256 _cumulativeRate) external pure returns (uint256);
//calculate a new cumulative rate
function calculateCumulativeRate(
uint256 _borrowRate,
uint256 _cumulativeRate,
uint256 _timeElapsed
) external view returns (uint256);
}
//
interface ILiquidationManager {
function a() external view returns (IAddressProvider);
function calculateHealthFactor(
address _collateralType,
uint256 _collateralValue,
uint256 _vaultDebt
) external view returns (uint256 healthFactor);
function liquidationBonus(uint256 _amount) external view returns (uint256 bonus);
function applyLiquidationDiscount(uint256 _amount) external view returns (uint256 discountedAmount);
function isHealthy(
address _collateralType,
uint256 _collateralValue,
uint256 _vaultDebt
) external view returns (bool);
}
//
interface IVaultsDataProvider {
struct Vault {
// borrowedType support USDX / PAR
address collateralType;
address owner;
uint256 collateralBalance;
uint256 baseDebt;
uint256 createdAt;
}
function a() external view returns (IAddressProvider);
// Read
function baseDebt(address _collateralType) external view returns (uint256);
function vaultCount() external view returns (uint256);
function vaults(uint256 _id) external view returns (Vault memory);
function vaultOwner(uint256 _id) external view returns (address);
function vaultCollateralType(uint256 _id) external view returns (address);
function vaultCollateralBalance(uint256 _id) external view returns (uint256);
function vaultBaseDebt(uint256 _id) external view returns (uint256);
function vaultId(address _collateralType, address _owner) external view returns (uint256);
function vaultExists(uint256 _id) external view returns (bool);
function vaultDebt(uint256 _vaultId) external view returns (uint256);
function debt() external view returns (uint256);
function collateralDebt(address _collateralType) external view returns (uint256);
//Write
function createVault(address _collateralType, address _owner) external returns (uint256);
function setCollateralBalance(uint256 _id, uint256 _balance) external;
function setBaseDebt(uint256 _id, uint256 _newBaseDebt) external;
}
//
interface IFeeDistributor {
event PayeeAdded(address indexed account, uint256 shares);
event FeeReleased(uint256 income, uint256 releasedAt);
function a() external view returns (IAddressProvider);
function lastReleasedAt() external view returns (uint256);
function getPayees() external view returns (address[] memory);
function totalShares() external view returns (uint256);
function shares(address payee) external view returns (uint256);
function release() external;
function changePayees(address[] memory _payees, uint256[] memory _shares) external;
}
//
interface IAddressProvider {
function controller() external view returns (IAccessController);
function config() external view returns (IConfigProvider);
function core() external view returns (IVaultsCore);
function stablex() external view returns (ISTABLEX);
function ratesManager() external view returns (IRatesManager);
function priceFeed() external view returns (IPriceFeed);
function liquidationManager() external view returns (ILiquidationManager);
function vaultsData() external view returns (IVaultsDataProvider);
function feeDistributor() external view returns (IFeeDistributor);
function setAccessController(IAccessController _controller) external;
function setConfigProvider(IConfigProvider _config) external;
function setVaultsCore(IVaultsCore _core) external;
function setStableX(ISTABLEX _stablex) external;
function setRatesManager(IRatesManager _ratesManager) external;
function setPriceFeed(IPriceFeed _priceFeed) external;
function setLiquidationManager(ILiquidationManager _liquidationManager) external;
function setVaultsDataProvider(IVaultsDataProvider _vaultsData) external;
function setFeeDistributor(IFeeDistributor _feeDistributor) external;
}
//
interface IVaultsCore {
event Opened(uint256 indexed vaultId, address indexed collateralType, address indexed owner);
event Deposited(uint256 indexed vaultId, uint256 amount, address indexed sender);
event Withdrawn(uint256 indexed vaultId, uint256 amount, address indexed sender);
event Borrowed(uint256 indexed vaultId, uint256 amount, address indexed sender);
event Repaid(uint256 indexed vaultId, uint256 amount, address indexed sender);
event Liquidated(
uint256 indexed vaultId,
uint256 debtRepaid,
uint256 collateralLiquidated,
address indexed owner,
address indexed sender
);
event CumulativeRateUpdated(
address indexed collateralType,
uint256 elapsedTime,
uint256 newCumulativeRate
); //cumulative interest rate from deployment time T0
event InsurancePaid(uint256 indexed vaultId, uint256 insuranceAmount, address indexed sender);
function a() external view returns (IAddressProvider);
function deposit(address _collateralType, uint256 _amount) external;
function withdraw(uint256 _vaultId, uint256 _amount) external;
function withdrawAll(uint256 _vaultId) external;
function borrow(uint256 _vaultId, uint256 _amount) external;
function repayAll(uint256 _vaultId) external;
function repay(uint256 _vaultId, uint256 _amount) external;
function liquidate(uint256 _vaultId) external;
//Read only
function availableIncome() external view returns (uint256);
function cumulativeRates(address _collateralType) external view returns (uint256);
function lastRefresh(address _collateralType) external view returns (uint256);
//Refresh
function initializeRates(address _collateralType) external;
function refresh() external;
function refreshCollateral(address collateralType) external;
//upgrade
function upgrade(address _newVaultsCore) external;
}
//
contract VaultsCore is IVaultsCore, ReentrancyGuard {
using SafeERC20 for IERC20;
using SafeMath for uint256;
using WadRayMath for uint256;
uint256 MAX_INT = 2**256 - 1;
mapping(address => uint256) public override cumulativeRates;
mapping(address => uint256) public override lastRefresh;
IAddressProvider public override a;
modifier onlyManager() {
require(a.controller().hasRole(a.controller().MANAGER_ROLE(), msg.sender));
_;
}
modifier onlyVaultOwner(uint256 _vaultId) {
require(a.vaultsData().vaultOwner(_vaultId) == msg.sender);
_;
}
modifier onlyConfig() {
require(msg.sender == address(a.config()));
_;
}
constructor(IAddressProvider _addresses) public {
require(address(_addresses) != address(0));
a = _addresses;
}
/*
Allow smooth upgrading of the vaultscore.
@dev this function approves token transfers to the new vaultscore of
both stablex and all configured collateral types
@param _newVaultsCore address of the new vaultscore
*/
function upgrade(address _newVaultsCore) public override onlyManager{
require(address(_newVaultsCore) != address(0));
require(a.stablex().approve(_newVaultsCore, MAX_INT));
for (uint256 i = 1; i <= a.config().numCollateralConfigs(); i++) {
address collateralType = a.config().collateralConfigs(i).collateralType;
IERC20 asset = IERC20(collateralType);
asset.safeApprove(_newVaultsCore, MAX_INT);
}
}
/**
Calculate the available income
@return available income that has not been minted yet.
**/
function availableIncome() public override view returns (uint256) {
return a.vaultsData().debt().sub(a.stablex().totalSupply());
}
/**
Refresh the cumulative rates and debts of all vaults and all collateral types.
**/
function refresh() public override {
for (uint256 i = 1; i <= a.config().numCollateralConfigs(); i++) {
address collateralType = a.config().collateralConfigs(i).collateralType;
refreshCollateral(collateralType);
}
}
/**
Initialize the cumulative rates to 1 for a new collateral type.
@param _collateralType the address of the new collateral type to be initialized
**/
function initializeRates(address _collateralType) public override onlyConfig {
require(_collateralType != address(0));
lastRefresh[_collateralType] = now;
cumulativeRates[_collateralType] = WadRayMath.ray();
}
/**
Refresh the cumulative rate of a collateraltype.
@dev this updates the debt for all vaults with the specified collateral type.
@param _collateralType the address of the collateral type to be refreshed.
**/
function refreshCollateral(address _collateralType) public override {
require(_collateralType != address(0));
require(a.config().collateralIds(_collateralType) != 0);
uint256 timestamp = now;
uint256 timeElapsed = timestamp.sub(lastRefresh[_collateralType]);
_refreshCumulativeRate(_collateralType, timeElapsed);
lastRefresh[_collateralType] = timestamp;
}
/**
Internal function to increase the cumulative rate over a specified time period
@dev this updates the debt for all vaults with the specified collateral type.
@param _collateralType the address of the collateral type to be updated
@param _timeElapsed the amount of time in seconds to add to the cumulative rate
**/
function _refreshCumulativeRate(address _collateralType, uint256 _timeElapsed) internal {
uint256 borrowRate = a.config().collateralBorrowRate(_collateralType);
uint256 oldCumulativeRate = cumulativeRates[_collateralType];
cumulativeRates[_collateralType] = a.ratesManager().calculateCumulativeRate(
borrowRate,
oldCumulativeRate,
_timeElapsed
);
emit CumulativeRateUpdated(_collateralType, _timeElapsed, cumulativeRates[_collateralType]);
}
/**
Deposit an ERC20 token into the vault of the msg.sender as collateral
@dev A new vault is created if no vault exists for the `msg.sender` with the specified collateral type.
this function used `transferFrom()` and requires pre-approval via `approve()` on the ERC20.
@param _collateralType the address of the collateral type to be deposited
@param _amount the amount of tokens to be deposited in WEI.
**/
function deposit(address _collateralType, uint256 _amount) public override {
require(a.config().collateralIds(_collateralType) != 0);
uint256 vaultId = a.vaultsData().vaultId(_collateralType, msg.sender);
if (vaultId == 0) {
vaultId = a.vaultsData().createVault(_collateralType, msg.sender);
}
IVaultsDataProvider.Vault memory v = a.vaultsData().vaults(vaultId);
a.vaultsData().setCollateralBalance(vaultId, v.collateralBalance.add(_amount));
IERC20 asset = IERC20(v.collateralType);
asset.safeTransferFrom(msg.sender, address(this), _amount);
emit Deposited(vaultId, _amount, msg.sender);
}
/**
Withdraws ERC20 tokens from a vault.
@dev Only te owner of a vault can withdraw collateral from it.
`withdraw()` will fail if it would bring the vault below the liquidation treshold.
@param _vaultId the ID of the vault from which to withdraw the collateral.
@param _amount the amount of ERC20 tokens to be withdrawn in WEI.
**/
function withdraw(uint256 _vaultId, uint256 _amount) public override onlyVaultOwner(_vaultId) nonReentrant {
IVaultsDataProvider.Vault memory v = a.vaultsData().vaults(_vaultId);
require(_amount <= v.collateralBalance);
uint256 newCollateralBalance = v.collateralBalance.sub(_amount);
a.vaultsData().setCollateralBalance(_vaultId, newCollateralBalance);
if (v.baseDebt > 0) {
//save gas cost when withdrawing from 0 debt vault
refreshCollateral(v.collateralType);
uint256 newCollateralValue = a.priceFeed().convertFrom(v.collateralType, newCollateralBalance);
bool _isHealthy = a.liquidationManager().isHealthy(
v.collateralType,
newCollateralValue,
a.vaultsData().vaultDebt(_vaultId)
);
require(_isHealthy);
}
IERC20 asset = IERC20(v.collateralType);
asset.safeTransfer(msg.sender, _amount);
emit Withdrawn(_vaultId, _amount, msg.sender);
}
/**
Convenience function to withdraw all collateral of a vault
@dev Only te owner of a vault can withdraw collateral from it.
`withdrawAll()` will fail if the vault has any outstanding debt attached to it.
@param _vaultId the ID of the vault from which to withdraw the collateral.
**/
function withdrawAll(uint256 _vaultId) public override onlyVaultOwner(_vaultId) {
uint256 collateralBalance = a.vaultsData().vaultCollateralBalance(_vaultId);
withdraw(_vaultId, collateralBalance);
}
/**
Borrow new StableX (Eg: PAR) tokens from a vault.
@dev Only te owner of a vault can borrow from it.
`borrow()` will update the outstanding vault debt to the current time before attempting the withdrawal.
and will fail if it would bring the vault below the liquidation treshold.
@param _vaultId the ID of the vault from which to borrow.
@param _amount the amount of borrowed StableX tokens in WEI.
**/
function borrow(uint256 _vaultId, uint256 _amount) public override onlyVaultOwner(_vaultId) nonReentrant {
IVaultsDataProvider.Vault memory v = a.vaultsData().vaults(_vaultId);
//make sure current rate is up to date
refreshCollateral(v.collateralType);
uint256 originationFeePercentage = a.config().collateralOriginationFee(v.collateralType);
uint256 newDebt = _amount;
if (originationFeePercentage > 0) {
newDebt = newDebt.add(_amount.wadMul(originationFeePercentage));
}
// Increment vault borrow balance
uint256 newBaseDebt = a.ratesManager().calculateBaseDebt(newDebt, cumulativeRates[v.collateralType]);
a.vaultsData().setBaseDebt(_vaultId, v.baseDebt.add(newBaseDebt));
uint256 collateralValue = a.priceFeed().convertFrom(v.collateralType, v.collateralBalance);
uint256 newVaultDebt = a.vaultsData().vaultDebt(_vaultId);
require(a.vaultsData().collateralDebt(v.collateralType) <= a.config().collateralDebtLimit(v.collateralType));
bool isHealthy = a.liquidationManager().isHealthy(v.collateralType, collateralValue, newVaultDebt);
require(isHealthy);
a.stablex().mint(msg.sender, _amount);
emit Borrowed(_vaultId, _amount, msg.sender);
}
/**
Convenience function to repay all debt of a vault
@dev `repayAll()` will update the outstanding vault debt to the current time.
@param _vaultId the ID of the vault for which to repay the debt.
**/
function repayAll(uint256 _vaultId) public override {
repay(_vaultId, 2**256 - 1);
}
/**
Repay an outstanding StableX balance to a vault.
@dev `repay()` will update the outstanding vault debt to the current time.
@param _vaultId the ID of the vault for which to repay the outstanding debt balance.
@param _amount the amount of StableX tokens in WEI to be repaid.
**/
function repay(uint256 _vaultId, uint256 _amount) public override nonReentrant {
address collateralType = a.vaultsData().vaultCollateralType(_vaultId);
// Make sure current rate is up to date
refreshCollateral(collateralType);
uint256 currentVaultDebt = a.vaultsData().vaultDebt(_vaultId);
// Decrement vault borrow balance
if (_amount >= currentVaultDebt) {
//full repayment
_amount = currentVaultDebt; //only pay back what's outstanding
}
_reduceVaultDebt(_vaultId, _amount);
a.stablex().burn(msg.sender, _amount);
emit Repaid(_vaultId, _amount, msg.sender);
}
/**
Internal helper function to reduce the debt of a vault.
@dev assumes cumulative rates for the vault's collateral type are up to date.
please call `refreshCollateral()` before calling this function.
@param _vaultId the ID of the vault for which to reduce the debt.
@param _amount the amount of debt to be reduced.
**/
function _reduceVaultDebt(uint256 _vaultId, uint256 _amount) internal {
address collateralType = a.vaultsData().vaultCollateralType(_vaultId);
uint256 currentVaultDebt = a.vaultsData().vaultDebt(_vaultId);
uint256 remainder = currentVaultDebt.sub(_amount);
uint256 cumulativeRate = cumulativeRates[collateralType];
if (remainder == 0) {
a.vaultsData().setBaseDebt(_vaultId, 0);
} else {
uint256 newBaseDebt = a.ratesManager().calculateBaseDebt(remainder, cumulativeRate);
a.vaultsData().setBaseDebt(_vaultId, newBaseDebt);
}
}
/**
Liquidate a vault that is below the liquidation treshold by repaying it's outstanding debt.
@dev `liquidate()` will update the outstanding vault debt to the current time and pay a `liquidationBonus`
to the liquidator. `liquidate()` can be called by anyone.
@param _vaultId the ID of the vault to be liquidated.
**/
function liquidate(uint256 _vaultId) public override nonReentrant {
IVaultsDataProvider.Vault memory v = a.vaultsData().vaults(_vaultId);
refreshCollateral(v.collateralType);
uint256 collateralValue = a.priceFeed().convertFrom(v.collateralType, v.collateralBalance);
uint256 currentVaultDebt = a.vaultsData().vaultDebt(_vaultId);
require(!a.liquidationManager().isHealthy(v.collateralType, collateralValue, currentVaultDebt));
uint256 discountedValue = a.liquidationManager().applyLiquidationDiscount(collateralValue);
uint256 collateralToReceive;
uint256 stableXToPay = currentVaultDebt;
if (discountedValue < currentVaultDebt) {
//Insurance Case
uint256 insuranceAmount = currentVaultDebt.sub(discountedValue);
require(a.stablex().balanceOf(address(this)) >= insuranceAmount);
a.stablex().burn(address(this), insuranceAmount);
emit InsurancePaid(_vaultId, insuranceAmount, msg.sender);
collateralToReceive = v.collateralBalance;
stableXToPay = currentVaultDebt.sub(insuranceAmount);
} else {
collateralToReceive = a.priceFeed().convertTo(v.collateralType, currentVaultDebt);
collateralToReceive = collateralToReceive.add(a.liquidationManager().liquidationBonus(collateralToReceive));
}
// reduce the vault debt to 0
_reduceVaultDebt(_vaultId, currentVaultDebt);
a.stablex().burn(msg.sender, stableXToPay);
// send the collateral to the liquidator
a.vaultsData().setCollateralBalance(_vaultId, v.collateralBalance.sub(collateralToReceive));
IERC20 asset = IERC20(v.collateralType);
asset.safeTransfer(msg.sender, collateralToReceive);
emit Liquidated(_vaultId, stableXToPay, collateralToReceive, v.owner, msg.sender);
}
}
{
"compilationTarget": {
"VaultsCore.sol": "VaultsCore"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"contract IAddressProvider","name":"_addresses","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"vaultId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"Borrowed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"collateralType","type":"address"},{"indexed":false,"internalType":"uint256","name":"elapsedTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newCumulativeRate","type":"uint256"}],"name":"CumulativeRateUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"vaultId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"vaultId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"insuranceAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"InsurancePaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"vaultId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"debtRepaid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralLiquidated","type":"uint256"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"Liquidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"vaultId","type":"uint256"},{"indexed":true,"internalType":"address","name":"collateralType","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"Opened","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"vaultId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"Repaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"vaultId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"Withdrawn","type":"event"},{"inputs":[],"name":"a","outputs":[{"internalType":"contract IAddressProvider","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"availableIncome","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_vaultId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"borrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"cumulativeRates","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_collateralType","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_collateralType","type":"address"}],"name":"initializeRates","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lastRefresh","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_vaultId","type":"uint256"}],"name":"liquidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"refresh","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_collateralType","type":"address"}],"name":"refreshCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_vaultId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"repay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_vaultId","type":"uint256"}],"name":"repayAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newVaultsCore","type":"address"}],"name":"upgrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_vaultId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_vaultId","type":"uint256"}],"name":"withdrawAll","outputs":[],"stateMutability":"nonpayable","type":"function"}]