// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;
abstract contract Manager {
function last(address) virtual public returns (uint);
function cdpCan(address, uint, address) virtual public view returns (uint);
function ilks(uint) virtual public view returns (bytes32);
function owns(uint) virtual public view returns (address);
function urns(uint) virtual public view returns (address);
function vat() virtual public view returns (address);
function open(bytes32, address) virtual public returns (uint);
function give(uint, address) virtual public;
function cdpAllow(uint, address, uint) virtual public;
function urnAllow(address, uint) virtual public;
function frob(uint, int, int) virtual public;
function flux(uint, address, uint) virtual public;
function move(uint, address, uint) virtual public;
function exit(address, uint, address, uint) virtual public;
function quit(uint, address) virtual public;
function enter(address, uint) virtual public;
function shift(uint, uint) virtual public;
}
abstract contract Vat {
struct Urn {
uint256 ink; // Locked Collateral [wad]
uint256 art; // Normalised Debt [wad]
}
struct Ilk {
uint256 Art; // Total Normalised Debt [wad]
uint256 rate; // Accumulated Rates [ray]
uint256 spot; // Price with Safety Margin [ray]
uint256 line; // Debt Ceiling [rad]
uint256 dust; // Urn Debt Floor [rad]
}
mapping (bytes32 => mapping (address => Urn )) public urns;
mapping (bytes32 => Ilk) public ilks;
mapping (bytes32 => mapping (address => uint)) public gem; // [wad]
function can(address, address) virtual public view returns (uint);
function dai(address) virtual public view returns (uint);
function frob(bytes32, address, address, address, int, int) virtual public;
function hope(address) virtual public;
function move(address, address, uint) virtual public;
function fork(bytes32, address, address, int, int) virtual public;
}
abstract contract PipInterface {
function read() public virtual returns (bytes32);
}
abstract contract Spotter {
struct Ilk {
PipInterface pip;
uint256 mat;
}
mapping (bytes32 => Ilk) public ilks;
uint256 public par;
}
contract DSMath {
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x);
}
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x);
}
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(y == 0 || (z = x * y) / y == x);
}
function div(uint256 x, uint256 y) internal pure returns (uint256 z) {
return x / y;
}
function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
return x <= y ? x : y;
}
function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
return x >= y ? x : y;
}
function imin(int256 x, int256 y) internal pure returns (int256 z) {
return x <= y ? x : y;
}
function imax(int256 x, int256 y) internal pure returns (int256 z) {
return x >= y ? x : y;
}
uint256 constant WAD = 10**18;
uint256 constant RAY = 10**27;
function wmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = add(mul(x, y), WAD / 2) / WAD;
}
function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = add(mul(x, y), RAY / 2) / RAY;
}
function wdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = add(mul(x, WAD), y / 2) / y;
}
function rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = add(mul(x, RAY), y / 2) / y;
}
// This famous algorithm is called "exponentiation by squaring"
// and calculates x^n with x as fixed-point and n as regular unsigned.
//
// It's O(log n), instead of O(n) for naive repeated multiplication.
//
// These facts are why it works:
//
// If n is even, then x^n = (x^2)^(n/2).
// If n is odd, then x^n = x * x^(n-1),
// and applying the equation for even x gives
// x^n = x * (x^2)^((n-1) / 2).
//
// Also, EVM division is flooring and
// floor[(n-1) / 2] = floor[n / 2].
//
function rpow(uint256 x, uint256 n) internal pure returns (uint256 z) {
z = n % 2 != 0 ? x : RAY;
for (n /= 2; n != 0; n /= 2) {
x = rmul(x, x);
if (n % 2 != 0) {
z = rmul(z, x);
}
}
}
}
interface ERC20 {
function totalSupply() external view returns (uint256 supply);
function balanceOf(address _owner) external view returns (uint256 balance);
function transfer(address _to, uint256 _value) external returns (bool success);
function transferFrom(address _from, address _to, uint256 _value)
external
returns (bool success);
function approve(address _spender, uint256 _value) external returns (bool success);
function allowance(address _owner, address _spender) external view returns (uint256 remaining);
function decimals() external view returns (uint256 digits);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}
library Address {
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);
}
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");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return _functionCallWithValue(target, data, 0, errorMessage);
}
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");
}
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);
}
}
}
}
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) {
// 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;
}
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;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
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;
}
}
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(ERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(ERC20 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.
*/
function safeApprove(ERC20 token, address spender, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(ERC20 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(ERC20 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));
}
function _callOptionalReturn(ERC20 token, bytes memory data) private {
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");
}
}
}
contract AdminAuth {
using SafeERC20 for ERC20;
address public owner;
address public admin;
modifier onlyOwner() {
require(owner == msg.sender);
_;
}
modifier onlyAdmin() {
require(admin == msg.sender);
_;
}
constructor() public {
owner = 0xBc841B0dE0b93205e912CFBBd1D0c160A1ec6F00;
admin = 0x25eFA336886C74eA8E282ac466BdCd0199f85BB9;
}
/// @notice Admin is set by owner first time, after that admin is super role and has permission to change owner
/// @param _admin Address of multisig that becomes admin
function setAdminByOwner(address _admin) public {
require(msg.sender == owner);
require(admin == address(0));
admin = _admin;
}
/// @notice Admin is able to set new admin
/// @param _admin Address of multisig that becomes new admin
function setAdminByAdmin(address _admin) public {
require(msg.sender == admin);
admin = _admin;
}
/// @notice Admin is able to change owner
/// @param _owner Address of new owner
function setOwnerByAdmin(address _owner) public {
require(msg.sender == admin);
owner = _owner;
}
/// @notice Destroy the contract
function kill() public onlyOwner {
selfdestruct(payable(owner));
}
/// @notice withdraw stuck funds
function withdrawStuckFunds(address _token, uint _amount) public onlyOwner {
if (_token == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) {
payable(owner).transfer(_amount);
} else {
ERC20(_token).safeTransfer(owner, _amount);
}
}
}
contract DefisaverLogger {
event LogEvent(
address indexed contractAddress,
address indexed caller,
string indexed logName,
bytes data
);
// solhint-disable-next-line func-name-mixedcase
function Log(address _contract, address _caller, string memory _logName, bytes memory _data)
public
{
emit LogEvent(_contract, _caller, _logName, _data);
}
}
contract BotRegistry is AdminAuth {
mapping (address => bool) public botList;
constructor() public {
botList[0x776B4a13093e30B05781F97F6A4565B6aa8BE330] = true;
botList[0xAED662abcC4FA3314985E67Ea993CAD064a7F5cF] = true;
botList[0xa5d330F6619d6bF892A5B87D80272e1607b3e34D] = true;
botList[0x5feB4DeE5150B589a7f567EA7CADa2759794A90A] = true;
botList[0x7ca06417c1d6f480d3bB195B80692F95A6B66158] = true;
}
function setBot(address _botAddr, bool _state) public onlyOwner {
botList[_botAddr] = _state;
}
}
contract DFSExchangeData {
// first is empty to keep the legacy order in place
enum ExchangeType { _, OASIS, KYBER, UNISWAP, ZEROX }
enum ActionType { SELL, BUY }
struct OffchainData {
address wrapper;
address exchangeAddr;
address allowanceTarget;
uint256 price;
uint256 protocolFee;
bytes callData;
}
struct ExchangeData {
address srcAddr;
address destAddr;
uint256 srcAmount;
uint256 destAmount;
uint256 minPrice;
uint256 dfsFeeDivider; // service fee divider
address user; // user to check special fee
address wrapper;
bytes wrapperData;
OffchainData offchainData;
}
function packExchangeData(ExchangeData memory _exData) public pure returns(bytes memory) {
return abi.encode(_exData);
}
function unpackExchangeData(bytes memory _data) public pure returns(ExchangeData memory _exData) {
_exData = abi.decode(_data, (ExchangeData));
}
}
/// @title Implements enum Method
abstract contract StaticV2 {
enum Method { Boost, Repay }
struct CdpHolder {
uint128 minRatio;
uint128 maxRatio;
uint128 optimalRatioBoost;
uint128 optimalRatioRepay;
address owner;
uint cdpId;
bool boostEnabled;
bool nextPriceEnabled;
}
struct SubPosition {
uint arrPos;
bool subscribed;
}
}
abstract contract ISubscriptionsV2 is StaticV2 {
function getOwner(uint _cdpId) external view virtual returns(address);
function getSubscribedInfo(uint _cdpId) public view virtual returns(bool, uint128, uint128, uint128, uint128, address, uint coll, uint debt);
function getCdpHolder(uint _cdpId) public view virtual returns (bool subscribed, CdpHolder memory);
}
abstract contract DSProxyInterface {
/// Truffle wont compile if this isn't commented
// function execute(bytes memory _code, bytes memory _data)
// public virtual
// payable
// returns (address, bytes32);
function execute(address _target, bytes memory _data) public virtual payable returns (bytes32);
function setCache(address _cacheAddr) public virtual payable returns (bool);
function owner() public virtual returns (address);
}
/// @title Implements logic for calling MCDSaverProxy always from same contract
contract MCDMonitorProxyV2 is AdminAuth {
uint public CHANGE_PERIOD;
uint public MIN_CHANGE_PERIOD = 6 * 1 hours;
address public monitor;
address public newMonitor;
address public lastMonitor;
uint public changeRequestedTimestamp;
event MonitorChangeInitiated(address oldMonitor, address newMonitor);
event MonitorChangeCanceled();
event MonitorChangeFinished(address monitor);
event MonitorChangeReverted(address monitor);
modifier onlyMonitor() {
require (msg.sender == monitor);
_;
}
constructor(uint _changePeriod) public {
CHANGE_PERIOD = _changePeriod * 1 hours;
}
/// @notice Only monitor contract is able to call execute on users proxy
/// @param _owner Address of cdp owner (users DSProxy address)
/// @param _saverProxy Address of MCDSaverProxy
/// @param _data Data to send to MCDSaverProxy
function callExecute(address _owner, address _saverProxy, bytes memory _data) public payable onlyMonitor {
// execute reverts if calling specific method fails
DSProxyInterface(_owner).execute{value: msg.value}(_saverProxy, _data);
// return if anything left
if (address(this).balance > 0) {
msg.sender.transfer(address(this).balance);
}
}
/// @notice Allowed users are able to set Monitor contract without any waiting period first time
/// @param _monitor Address of Monitor contract
function setMonitor(address _monitor) public onlyOwner {
require(monitor == address(0));
monitor = _monitor;
}
/// @notice Allowed users are able to start procedure for changing monitor
/// @dev after CHANGE_PERIOD needs to call confirmNewMonitor to actually make a change
/// @param _newMonitor address of new monitor
function changeMonitor(address _newMonitor) public onlyOwner {
require(changeRequestedTimestamp == 0);
changeRequestedTimestamp = now;
lastMonitor = monitor;
newMonitor = _newMonitor;
emit MonitorChangeInitiated(lastMonitor, newMonitor);
}
/// @notice At any point allowed users are able to cancel monitor change
function cancelMonitorChange() public onlyOwner {
require(changeRequestedTimestamp > 0);
changeRequestedTimestamp = 0;
newMonitor = address(0);
emit MonitorChangeCanceled();
}
/// @notice Anyone is able to confirm new monitor after CHANGE_PERIOD if process is started
function confirmNewMonitor() public onlyOwner {
require((changeRequestedTimestamp + CHANGE_PERIOD) < now);
require(changeRequestedTimestamp != 0);
require(newMonitor != address(0));
monitor = newMonitor;
newMonitor = address(0);
changeRequestedTimestamp = 0;
emit MonitorChangeFinished(monitor);
}
/// @notice Its possible to revert monitor to last used monitor
function revertMonitor() public onlyOwner {
require(lastMonitor != address(0));
monitor = lastMonitor;
emit MonitorChangeReverted(monitor);
}
function setChangePeriod(uint _periodInHours) public onlyOwner {
require(_periodInHours * 1 hours > MIN_CHANGE_PERIOD);
CHANGE_PERIOD = _periodInHours * 1 hours;
}
}
/**
* @title LendingPoolAddressesProvider contract
* @dev Main registry of addresses part of or connected to the protocol, including permissioned roles
* - Acting also as factory of proxies and admin of those, so with right to change its implementations
* - Owned by the Aave Governance
* @author Aave
**/
interface ILendingPoolAddressesProviderV2 {
event LendingPoolUpdated(address indexed newAddress);
event ConfigurationAdminUpdated(address indexed newAddress);
event EmergencyAdminUpdated(address indexed newAddress);
event LendingPoolConfiguratorUpdated(address indexed newAddress);
event LendingPoolCollateralManagerUpdated(address indexed newAddress);
event PriceOracleUpdated(address indexed newAddress);
event LendingRateOracleUpdated(address indexed newAddress);
event ProxyCreated(bytes32 id, address indexed newAddress);
event AddressSet(bytes32 id, address indexed newAddress, bool hasProxy);
function setAddress(bytes32 id, address newAddress) external;
function setAddressAsProxy(bytes32 id, address impl) external;
function getAddress(bytes32 id) external view returns (address);
function getLendingPool() external view returns (address);
function setLendingPoolImpl(address pool) external;
function getLendingPoolConfigurator() external view returns (address);
function setLendingPoolConfiguratorImpl(address configurator) external;
function getLendingPoolCollateralManager() external view returns (address);
function setLendingPoolCollateralManager(address manager) external;
function getPoolAdmin() external view returns (address);
function setPoolAdmin(address admin) external;
function getEmergencyAdmin() external view returns (address);
function setEmergencyAdmin(address admin) external;
function getPriceOracle() external view returns (address);
function setPriceOracle(address priceOracle) external;
function getLendingRateOracle() external view returns (address);
function setLendingRateOracle(address lendingRateOracle) external;
}
library DataTypes {
// refer to the whitepaper, section 1.1 basic concepts for a formal description of these properties.
struct ReserveData {
//stores the reserve configuration
ReserveConfigurationMap configuration;
//the liquidity index. Expressed in ray
uint128 liquidityIndex;
//variable borrow index. Expressed in ray
uint128 variableBorrowIndex;
//the current supply rate. Expressed in ray
uint128 currentLiquidityRate;
//the current variable borrow rate. Expressed in ray
uint128 currentVariableBorrowRate;
//the current stable borrow rate. Expressed in ray
uint128 currentStableBorrowRate;
uint40 lastUpdateTimestamp;
//tokens addresses
address aTokenAddress;
address stableDebtTokenAddress;
address variableDebtTokenAddress;
//address of the interest rate strategy
address interestRateStrategyAddress;
//the id of the reserve. Represents the position in the list of the active reserves
uint8 id;
}
struct ReserveConfigurationMap {
//bit 0-15: LTV
//bit 16-31: Liq. threshold
//bit 32-47: Liq. bonus
//bit 48-55: Decimals
//bit 56: Reserve is active
//bit 57: reserve is frozen
//bit 58: borrowing is enabled
//bit 59: stable rate borrowing enabled
//bit 60-63: reserved
//bit 64-79: reserve factor
uint256 data;
}
struct UserConfigurationMap {
uint256 data;
}
enum InterestRateMode {NONE, STABLE, VARIABLE}
}
interface ILendingPoolV2 {
/**
* @dev Emitted on deposit()
* @param reserve The address of the underlying asset of the reserve
* @param user The address initiating the deposit
* @param onBehalfOf The beneficiary of the deposit, receiving the aTokens
* @param amount The amount deposited
* @param referral The referral code used
**/
event Deposit(
address indexed reserve,
address user,
address indexed onBehalfOf,
uint256 amount,
uint16 indexed referral
);
/**
* @dev Emitted on withdraw()
* @param reserve The address of the underlyng asset being withdrawn
* @param user The address initiating the withdrawal, owner of aTokens
* @param to Address that will receive the underlying
* @param amount The amount to be withdrawn
**/
event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);
/**
* @dev Emitted on borrow() and flashLoan() when debt needs to be opened
* @param reserve The address of the underlying asset being borrowed
* @param user The address of the user initiating the borrow(), receiving the funds on borrow() or just
* initiator of the transaction on flashLoan()
* @param onBehalfOf The address that will be getting the debt
* @param amount The amount borrowed out
* @param borrowRateMode The rate mode: 1 for Stable, 2 for Variable
* @param borrowRate The numeric rate at which the user has borrowed
* @param referral The referral code used
**/
event Borrow(
address indexed reserve,
address user,
address indexed onBehalfOf,
uint256 amount,
uint256 borrowRateMode,
uint256 borrowRate,
uint16 indexed referral
);
/**
* @dev Emitted on repay()
* @param reserve The address of the underlying asset of the reserve
* @param user The beneficiary of the repayment, getting his debt reduced
* @param repayer The address of the user initiating the repay(), providing the funds
* @param amount The amount repaid
**/
event Repay(
address indexed reserve,
address indexed user,
address indexed repayer,
uint256 amount
);
/**
* @dev Emitted on swapBorrowRateMode()
* @param reserve The address of the underlying asset of the reserve
* @param user The address of the user swapping his rate mode
* @param rateMode The rate mode that the user wants to swap to
**/
event Swap(address indexed reserve, address indexed user, uint256 rateMode);
/**
* @dev Emitted on setUserUseReserveAsCollateral()
* @param reserve The address of the underlying asset of the reserve
* @param user The address of the user enabling the usage as collateral
**/
event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user);
/**
* @dev Emitted on setUserUseReserveAsCollateral()
* @param reserve The address of the underlying asset of the reserve
* @param user The address of the user enabling the usage as collateral
**/
event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);
/**
* @dev Emitted on rebalanceStableBorrowRate()
* @param reserve The address of the underlying asset of the reserve
* @param user The address of the user for which the rebalance has been executed
**/
event RebalanceStableBorrowRate(address indexed reserve, address indexed user);
/**
* @dev Emitted on flashLoan()
* @param target The address of the flash loan receiver contract
* @param initiator The address initiating the flash loan
* @param asset The address of the asset being flash borrowed
* @param amount The amount flash borrowed
* @param premium The fee flash borrowed
* @param referralCode The referral code used
**/
event FlashLoan(
address indexed target,
address indexed initiator,
address indexed asset,
uint256 amount,
uint256 premium,
uint16 referralCode
);
/**
* @dev Emitted when the pause is triggered.
*/
event Paused();
/**
* @dev Emitted when the pause is lifted.
*/
event Unpaused();
/**
* @dev Emitted when a borrower is liquidated. This event is emitted by the LendingPool via
* LendingPoolCollateral manager using a DELEGATECALL
* This allows to have the events in the generated ABI for LendingPool.
* @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
* @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
* @param user The address of the borrower getting liquidated
* @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
* @param liquidatedCollateralAmount The amount of collateral received by the liiquidator
* @param liquidator The address of the liquidator
* @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants
* to receive the underlying collateral asset directly
**/
event LiquidationCall(
address indexed collateralAsset,
address indexed debtAsset,
address indexed user,
uint256 debtToCover,
uint256 liquidatedCollateralAmount,
address liquidator,
bool receiveAToken
);
/**
* @dev Emitted when the state of a reserve is updated. NOTE: This event is actually declared
* in the ReserveLogic library and emitted in the updateInterestRates() function. Since the function is internal,
* the event will actually be fired by the LendingPool contract. The event is therefore replicated here so it
* gets added to the LendingPool ABI
* @param reserve The address of the underlying asset of the reserve
* @param liquidityRate The new liquidity rate
* @param stableBorrowRate The new stable borrow rate
* @param variableBorrowRate The new variable borrow rate
* @param liquidityIndex The new liquidity index
* @param variableBorrowIndex The new variable borrow index
**/
event ReserveDataUpdated(
address indexed reserve,
uint256 liquidityRate,
uint256 stableBorrowRate,
uint256 variableBorrowRate,
uint256 liquidityIndex,
uint256 variableBorrowIndex
);
/**
* @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
* - E.g. User deposits 100 USDC and gets in return 100 aUSDC
* @param asset The address of the underlying asset to deposit
* @param amount The amount to be deposited
* @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
* wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
* is a different wallet
* @param referralCode Code used to register the integrator originating the operation, for potential rewards.
* 0 if the action is executed directly by the user, without any middle-man
**/
function deposit(
address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode
) external;
/**
* @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
* E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
* @param asset The address of the underlying asset to withdraw
* @param amount The underlying amount to be withdrawn
* - Send the value type(uint256).max in order to withdraw the whole aToken balance
* @param to Address that will receive the underlying, same as msg.sender if the user
* wants to receive it on his own wallet, or a different address if the beneficiary is a
* different wallet
**/
function withdraw(
address asset,
uint256 amount,
address to
) external;
/**
* @dev Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower
* already deposited enough collateral, or he was given enough allowance by a credit delegator on the
* corresponding debt token (StableDebtToken or VariableDebtToken)
* - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet
* and 100 stable/variable debt tokens, depending on the `interestRateMode`
* @param asset The address of the underlying asset to borrow
* @param amount The amount to be borrowed
* @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable
* @param referralCode Code used to register the integrator originating the operation, for potential rewards.
* 0 if the action is executed directly by the user, without any middle-man
* @param onBehalfOf Address of the user who will receive the debt. Should be the address of the borrower itself
* calling the function if he wants to borrow against his own collateral, or the address of the credit delegator
* if he has been given credit delegation allowance
**/
function borrow(
address asset,
uint256 amount,
uint256 interestRateMode,
uint16 referralCode,
address onBehalfOf
) external;
/**
* @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned
* - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address
* @param asset The address of the borrowed underlying asset previously borrowed
* @param amount The amount to repay
* - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
* @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
* @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the
* user calling the function if he wants to reduce/remove his own debt, or the address of any other
* other borrower whose debt should be removed
**/
function repay(
address asset,
uint256 amount,
uint256 rateMode,
address onBehalfOf
) external;
/**
* @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa
* @param asset The address of the underlying asset borrowed
* @param rateMode The rate mode that the user wants to swap to
**/
function swapBorrowRateMode(address asset, uint256 rateMode) external;
/**
* @dev Rebalances the stable interest rate of a user to the current stable rate defined on the reserve.
* - Users can be rebalanced if the following conditions are satisfied:
* 1. Usage ratio is above 95%
* 2. the current deposit APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too much has been
* borrowed at a stable rate and depositors are not earning enough
* @param asset The address of the underlying asset borrowed
* @param user The address of the user to be rebalanced
**/
function rebalanceStableBorrowRate(address asset, address user) external;
/**
* @dev Allows depositors to enable/disable a specific deposited asset as collateral
* @param asset The address of the underlying asset deposited
* @param useAsCollateral `true` if the user wants to use the deposit as collateral, `false` otherwise
**/
function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external;
/**
* @dev Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1
* - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives
* a proportionally amount of the `collateralAsset` plus a bonus to cover market risk
* @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
* @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
* @param user The address of the borrower getting liquidated
* @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
* @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants
* to receive the underlying collateral asset directly
**/
function liquidationCall(
address collateralAsset,
address debtAsset,
address user,
uint256 debtToCover,
bool receiveAToken
) external;
/**
* @dev Allows smartcontracts to access the liquidity of the pool within one transaction,
* as long as the amount taken plus a fee is returned.
* IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration.
* For further details please visit https://developers.aave.com
* @param receiverAddress The address of the contract receiving the funds, implementing the IFlashLoanReceiver interface
* @param assets The addresses of the assets being flash-borrowed
* @param amounts The amounts amounts being flash-borrowed
* @param modes Types of the debt to open if the flash loan is not returned:
* 0 -> Don't open any debt, just revert if funds can't be transferred from the receiver
* 1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
* 2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
* @param onBehalfOf The address that will receive the debt in the case of using on `modes` 1 or 2
* @param params Variadic packed params to pass to the receiver as extra information
* @param referralCode Code used to register the integrator originating the operation, for potential rewards.
* 0 if the action is executed directly by the user, without any middle-man
**/
function flashLoan(
address receiverAddress,
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata modes,
address onBehalfOf,
bytes calldata params,
uint16 referralCode
) external;
/**
* @dev Returns the user account data across all the reserves
* @param user The address of the user
* @return totalCollateralETH the total collateral in ETH of the user
* @return totalDebtETH the total debt in ETH of the user
* @return availableBorrowsETH the borrowing power left of the user
* @return currentLiquidationThreshold the liquidation threshold of the user
* @return ltv the loan to value of the user
* @return healthFactor the current health factor of the user
**/
function getUserAccountData(address user)
external
view
returns (
uint256 totalCollateralETH,
uint256 totalDebtETH,
uint256 availableBorrowsETH,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
);
function initReserve(
address reserve,
address aTokenAddress,
address stableDebtAddress,
address variableDebtAddress,
address interestRateStrategyAddress
) external;
function setReserveInterestRateStrategyAddress(address reserve, address rateStrategyAddress)
external;
function setConfiguration(address reserve, uint256 configuration) external;
/**
* @dev Returns the configuration of the reserve
* @param asset The address of the underlying asset of the reserve
* @return The configuration of the reserve
**/
function getConfiguration(address asset) external view returns (DataTypes.ReserveConfigurationMap memory);
/**
* @dev Returns the configuration of the user across all the reserves
* @param user The user address
* @return The configuration of the user
**/
function getUserConfiguration(address user) external view returns (DataTypes.UserConfigurationMap memory);
/**
* @dev Returns the normalized income normalized income of the reserve
* @param asset The address of the underlying asset of the reserve
* @return The reserve's normalized income
*/
function getReserveNormalizedIncome(address asset) external view returns (uint256);
/**
* @dev Returns the normalized variable debt per unit of asset
* @param asset The address of the underlying asset of the reserve
* @return The reserve normalized variable debt
*/
function getReserveNormalizedVariableDebt(address asset) external view returns (uint256);
/**
* @dev Returns the state and configuration of the reserve
* @param asset The address of the underlying asset of the reserve
* @return The state of the reserve
**/
function getReserveData(address asset) external view returns (DataTypes.ReserveData memory);
function finalizeTransfer(
address asset,
address from,
address to,
uint256 amount,
uint256 balanceFromAfter,
uint256 balanceToBefore
) external;
function getReservesList() external view returns (address[] memory);
function getAddressesProvider() external view returns (ILendingPoolAddressesProviderV2);
function setPause(bool val) external;
function paused() external view returns (bool);
}
interface IFlashLoans {
function flashLoan(
address recipient,
address[] memory tokens,
uint256[] memory amounts,
bytes memory userData
) external;
}
abstract contract IAaveProtocolDataProviderV2 {
struct TokenData {
string symbol;
address tokenAddress;
}
function getAllReservesTokens() external virtual view returns (TokenData[] memory);
function getAllATokens() external virtual view returns (TokenData[] memory);
function getReserveConfigurationData(address asset)
external virtual
view
returns (
uint256 decimals,
uint256 ltv,
uint256 liquidationThreshold,
uint256 liquidationBonus,
uint256 reserveFactor,
bool usageAsCollateralEnabled,
bool borrowingEnabled,
bool stableBorrowRateEnabled,
bool isActive,
bool isFrozen
);
function getReserveData(address asset)
external virtual
view
returns (
uint256 availableLiquidity,
uint256 totalStableDebt,
uint256 totalVariableDebt,
uint256 liquidityRate,
uint256 variableBorrowRate,
uint256 stableBorrowRate,
uint256 averageStableBorrowRate,
uint256 liquidityIndex,
uint256 variableBorrowIndex,
uint40 lastUpdateTimestamp
);
function getUserReserveData(address asset, address user)
external virtual
view
returns (
uint256 currentATokenBalance,
uint256 currentStableDebt,
uint256 currentVariableDebt,
uint256 principalStableDebt,
uint256 scaledVariableDebt,
uint256 stableBorrowRate,
uint256 liquidityRate,
uint40 stableRateLastUpdated,
bool usageAsCollateralEnabled
);
function getReserveTokensAddresses(address asset)
external virtual
view
returns (
address aTokenAddress,
address stableDebtTokenAddress,
address variableDebtTokenAddress
);
}
/// @title Helper contract for getting AaveV2/Balancer flash loans
contract FLHelper {
address internal constant AAVE_MARKET_ADDR = 0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5;
address internal constant BALANCER_VAULT_ADDR = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;
uint16 internal constant AAVE_REFERRAL_CODE = 64;
address internal constant WETH_ADDR = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address internal constant ETH_ADDR = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
enum FLType {
AAVE_V2,
BALANCER,
NO_LOAN
}
function _getFL(
FLType _flType,
address _tokenAddr,
uint256 _flAmount,
bytes memory _callData,
address _receiverAddr
) internal {
_tokenAddr = _tokenAddr == ETH_ADDR ? WETH_ADDR : _tokenAddr;
address[] memory tokens = new address[](1);
tokens[0] = _tokenAddr;
uint256[] memory amounts = new uint256[](1);
amounts[0] = _flAmount;
if (_flType == FLType.AAVE_V2) {
address lendingPool = ILendingPoolAddressesProviderV2(AAVE_MARKET_ADDR)
.getLendingPool();
uint256[] memory modes = new uint256[](1);
modes[0] = 0;
ILendingPoolV2(lendingPool).flashLoan(
_receiverAddr,
tokens,
amounts,
modes,
address(this),
_callData,
AAVE_REFERRAL_CODE
);
} else {
IFlashLoans(BALANCER_VAULT_ADDR).flashLoan(_receiverAddr, tokens, amounts, _callData);
}
}
function getProtocolLiq(address _tokenAddr, uint256 _desiredAmount)
public
view
returns (FLType flType)
{
uint256 flLiquidity = getFLLiquidity(FLType.BALANCER, _tokenAddr);
if (flLiquidity >= _desiredAmount) return FLType.BALANCER;
flLiquidity = getFLLiquidity(FLType.AAVE_V2, _tokenAddr);
if (flLiquidity >= _desiredAmount) return FLType.AAVE_V2;
return FLType.NO_LOAN;
}
function getFLLiquidity(FLType _flType, address _tokenAddr) public view returns (uint256) {
_tokenAddr = _tokenAddr == ETH_ADDR ? WETH_ADDR : _tokenAddr;
if (_flType == FLType.AAVE_V2) {
IAaveProtocolDataProviderV2 dataProvider = getDataProvider(AAVE_MARKET_ADDR);
try dataProvider.getReserveData(_tokenAddr) returns (
uint256 liquidity,
uint256,
uint256,
uint256,
uint256,
uint256,
uint256,
uint256,
uint256,
uint40
) {
return liquidity;
} catch (bytes memory) {
return 0;
}
} else {
return ERC20(_tokenAddr).balanceOf(BALANCER_VAULT_ADDR);
}
}
function getDataProvider(address _market) internal view returns (IAaveProtocolDataProviderV2) {
return
IAaveProtocolDataProviderV2(
ILendingPoolAddressesProviderV2(_market).getAddress(
0x0100000000000000000000000000000000000000000000000000000000000000
)
);
}
}
abstract contract IMcdSubProxy {
struct McdSubData {
uint256 vaultId;
uint256 minRatio;
uint256 maxRatio;
uint128 targetRatioBoost;
uint128 targetRatioRepay;
bool boostEnabled;
}
function subToMcdAutomation(
McdSubData calldata _subData,
bool _shouldLegacyUnsub
) public virtual;
}
/// @title Implements logic that allows bots to call Boost and Repay
contract MCDMonitorV2 is DSMath, AdminAuth, StaticV2 {
uint256 public MAX_GAS_PRICE = 800 gwei; // 800 gwei
uint256 public REPAY_GAS_COST = 1_000_000;
uint256 public BOOST_GAS_COST = 1_000_000;
bytes4 public REPAY_SELECTOR = 0xee98485d; // repayWithLoan(...)
bytes4 public BOOST_SELECTOR = 0x63ed7d63; // boostWithLoan(...)
MCDMonitorProxyV2 public monitorProxyContract = MCDMonitorProxyV2(0x1816A86C4DA59395522a42b871bf11A4E96A1C7a);
ISubscriptionsV2 public subscriptionsContract = ISubscriptionsV2(0xC45d4f6B6bf41b6EdAA58B01c4298B8d9078269a);
address public mcdSaverTakerAddress;
address internal mcdSaverFlashLoan;
address public constant BOT_REGISTRY_ADDRESS = 0x637726f8b08a7ABE3aE3aCaB01A80E2d8ddeF77B;
address public constant PROXY_PERMISSION_ADDR = 0x5a4f877CA808Cca3cB7c2A194F80Ab8588FAE26B;
Manager public manager = Manager(0x5ef30b9986345249bc32d8928B7ee64DE9435E39);
Vat public vat = Vat(0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B);
Spotter public spotter = Spotter(0x65C79fcB50Ca1594B025960e539eD7A9a6D434A3);
// New strategy sub proxy contract
IMcdSubProxy mcdSubProxy = IMcdSubProxy(0xDED2752728227c502E08e51023b1cE0a37F907A2);
DefisaverLogger public constant logger =
DefisaverLogger(0x5c55B921f590a89C1Ebe84dF170E655a82b62126);
modifier onlyApproved() {
require(BotRegistry(BOT_REGISTRY_ADDRESS).botList(msg.sender), "Not auth bot");
_;
}
constructor(
address _newMcdSaverTakerAddress,
address _mcdSaverFlashLoan
) public {
mcdSaverTakerAddress = _newMcdSaverTakerAddress;
mcdSaverFlashLoan = _mcdSaverFlashLoan;
}
/// @notice Bots call this method to repay for user when conditions are met
/// @dev If the contract owns gas token it will try and use it for gas price reduction
function repayFor(
DFSExchangeData.ExchangeData memory _exchangeData,
uint256 _cdpId,
uint256 _nextPrice,
address _joinAddr,
FLHelper.FLType _flType
) public payable onlyApproved {
uint256 ratioBefore;
string memory errReason;
{ // for stack to deep, scope variables not used later on
bool isAllowed;
(isAllowed, ratioBefore, errReason) = checkPreconditions(
Method.Repay,
_cdpId,
_nextPrice
);
require(isAllowed, errReason);
}
uint256 gasCost = calcGasCost(REPAY_GAS_COST);
address usersProxy = subscriptionsContract.getOwner(_cdpId);
monitorProxyContract.callExecute{value: msg.value}(
usersProxy,
mcdSaverTakerAddress,
abi.encodeWithSelector(REPAY_SELECTOR, _exchangeData, _cdpId, gasCost, _joinAddr, 0, _flType, mcdSaverFlashLoan)
);
bool isGoodRatio;
uint256 ratioAfter;
(isGoodRatio, ratioAfter, errReason) = ratioGoodAfter(
Method.Repay,
_cdpId,
_nextPrice,
ratioBefore
);
require(isGoodRatio, errReason);
returnEth();
logger.Log(
address(this),
usersProxy,
"AutomaticMCDRepay",
abi.encode(ratioBefore, ratioAfter)
);
}
/// @notice Bots call this method to boost for user when conditions are met
/// @dev If the contract owns gas token it will try and use it for gas price reduction
function boostFor(
DFSExchangeData.ExchangeData memory _exchangeData,
uint256 _cdpId,
uint256 _nextPrice,
address _joinAddr,
FLHelper.FLType _flType
) public payable onlyApproved {
uint256 ratioBefore;
string memory errReason;
{ // for stack to deep, scope variables not used alter on
bool isAllowed;
(isAllowed, ratioBefore, errReason) = checkPreconditions(
Method.Boost,
_cdpId,
_nextPrice
);
require(isAllowed, errReason);
}
uint256 gasCost = calcGasCost(BOOST_GAS_COST);
address usersProxy = subscriptionsContract.getOwner(_cdpId);
monitorProxyContract.callExecute{value: msg.value}(
usersProxy,
mcdSaverTakerAddress,
abi.encodeWithSelector(BOOST_SELECTOR, _exchangeData, _cdpId, gasCost, _joinAddr, 0, _flType, mcdSaverFlashLoan)
);
bool isGoodRatio;
uint256 ratioAfter;
(isGoodRatio, ratioAfter, errReason) = ratioGoodAfter(
Method.Boost,
_cdpId,
_nextPrice,
ratioBefore
);
require(isGoodRatio, errReason);
returnEth();
logger.Log(
address(this),
usersProxy,
"AutomaticMCDBoost",
abi.encode(ratioBefore, ratioAfter)
);
}
/// @dev One time migrate method to move all CDPs to the new dfs strategy system
/// @param cdpIds Ids of all list of cdps we want to migrate
/// @param _shouldRemoveAuth If we are removing the old auth for it, might not want to remove if user has other cdps automated
function migrate(uint256[] memory cdpIds, bool[] memory _shouldRemoveAuth) external onlyApproved {
for (uint256 i = 0; i < cdpIds.length; ++i) {
address userProxy = subscriptionsContract.getOwner(cdpIds[i]);
(bool isSub, CdpHolder memory cdpInfo) = subscriptionsContract.getCdpHolder(cdpIds[i]);
// format for new data sub
IMcdSubProxy.McdSubData memory newSubInfo = IMcdSubProxy.McdSubData({
vaultId: cdpInfo.cdpId,
minRatio: cdpInfo.minRatio,
maxRatio: cdpInfo.maxRatio,
targetRatioBoost: cdpInfo.optimalRatioBoost,
targetRatioRepay: cdpInfo.optimalRatioRepay,
boostEnabled: cdpInfo.boostEnabled
});
// if user is subed and still the owner of that cdp
if (isSub && cdpInfo.owner == userProxy) {
// call monitorProxy which will call users DSProxy to call mcdSubProxy and subscribe to strategy system with same data
// call to mcdSubProxy will also unsub the user from this automation
monitorProxyContract.callExecute(
userProxy,
address(mcdSubProxy),
abi.encodeWithSelector(IMcdSubProxy.subToMcdAutomation.selector, newSubInfo, _shouldRemoveAuth[i])
);
}
}
}
/******************* INTERNAL METHODS ********************************/
function returnEth() internal {
// return if some eth left
if (address(this).balance > 0) {
msg.sender.transfer(address(this).balance);
}
}
/******************* STATIC METHODS ********************************/
/// @notice Returns an address that owns the CDP
/// @param _cdpId Id of the CDP
function getOwner(uint256 _cdpId) public view returns (address) {
return manager.owns(_cdpId);
}
/// @notice Gets CDP info (collateral, debt)
/// @param _cdpId Id of the CDP
/// @param _ilk Ilk of the CDP
function getCdpInfo(uint256 _cdpId, bytes32 _ilk) public view returns (uint256, uint256) {
address urn = manager.urns(_cdpId);
(uint256 collateral, uint256 debt) = vat.urns(_ilk, urn);
(, uint256 rate, , , ) = vat.ilks(_ilk);
return (collateral, rmul(debt, rate));
}
/// @notice Gets a price of the asset
/// @param _ilk Ilk of the CDP
function getPrice(bytes32 _ilk) public view returns (uint256) {
(, uint256 mat) = spotter.ilks(_ilk);
(, , uint256 spot, , ) = vat.ilks(_ilk);
return rmul(rmul(spot, spotter.par()), mat);
}
/// @notice Gets CDP ratio
/// @param _cdpId Id of the CDP
/// @param _nextPrice Next price for user
function getRatio(uint256 _cdpId, uint256 _nextPrice) public view returns (uint256) {
bytes32 ilk = manager.ilks(_cdpId);
uint256 price = (_nextPrice == 0) ? getPrice(ilk) : _nextPrice;
(uint256 collateral, uint256 debt) = getCdpInfo(_cdpId, ilk);
if (debt == 0) return 0;
return rdiv(wmul(collateral, price), debt) / (10**18);
}
/// @notice Checks if Boost/Repay could be triggered for the CDP
/// @dev Called by MCDMonitor to enforce the min/max check
function checkPreconditions(
Method _method,
uint256 _cdpId,
uint256 _nextPrice
)
public
view
returns (
bool,
uint256,
string memory
)
{
(bool subscribed, CdpHolder memory holder) = subscriptionsContract.getCdpHolder(_cdpId);
// check if cdp is subscribed
if (!subscribed) return (false, 0, "Cdp not sub");
// check if using next price is allowed
if (_nextPrice > 0 && !holder.nextPriceEnabled)
return (false, 0, "Next price send but not enabled");
// check if boost and boost allowed
if (_method == Method.Boost && !holder.boostEnabled)
return (false, 0, "Boost not enabled");
// check if owner is still owner
if (getOwner(_cdpId) != holder.owner) return (false, 0, "EOA not subbed owner");
uint256 currRatio = getRatio(_cdpId, _nextPrice);
if (_method == Method.Repay) {
if (currRatio > holder.minRatio) return (false, 0, "Ratio is bigger than min");
} else if (_method == Method.Boost) {
if (currRatio < holder.maxRatio) return (false, 0, "Ratio is less than max");
}
return (true, currRatio, "");
}
/// @dev After the Boost/Repay check if the ratio doesn't trigger another call
function ratioGoodAfter(
Method _method,
uint256 _cdpId,
uint256 _nextPrice,
uint256 _beforeRatio
)
public
view
returns (
bool,
uint256,
string memory
)
{
(, CdpHolder memory holder) = subscriptionsContract.getCdpHolder(_cdpId);
uint256 currRatio = getRatio(_cdpId, _nextPrice);
if (_method == Method.Repay) {
if (currRatio >= holder.maxRatio)
return (false, currRatio, "Repay increased ratio over max");
if (currRatio <= _beforeRatio) return (false, currRatio, "Repay made ratio worse");
} else if (_method == Method.Boost) {
if (currRatio <= holder.minRatio)
return (false, currRatio, "Boost lowered ratio over min");
if (currRatio >= _beforeRatio) return (false, currRatio, "Boost didn't lower ratio");
}
return (true, currRatio, "");
}
/// @notice Calculates gas cost (in Eth) of tx
/// @dev Gas price is limited to MAX_GAS_PRICE to prevent attack of draining user CDP
/// @param _gasAmount Amount of gas used for the tx
function calcGasCost(uint256 _gasAmount) public view returns (uint256) {
uint256 gasPrice = tx.gasprice <= MAX_GAS_PRICE ? tx.gasprice : MAX_GAS_PRICE;
return mul(gasPrice, _gasAmount);
}
/******************* OWNER ONLY OPERATIONS ********************************/
/// @notice Allows owner to change gas cost for boost operation, but only up to 3 millions
/// @param _gasCost New gas cost for boost method
function changeBoostGasCost(uint256 _gasCost) public onlyOwner {
require(_gasCost < 3_000_000, "Boost gas cost over limit");
BOOST_GAS_COST = _gasCost;
}
/// @notice Allows owner to change gas cost for repay operation, but only up to 3 millions
/// @param _gasCost New gas cost for repay method
function changeRepayGasCost(uint256 _gasCost) public onlyOwner {
require(_gasCost < 3_000_000, "Repay gas cost over limit");
REPAY_GAS_COST = _gasCost;
}
/// @notice Allows owner to change max gas price
/// @param _maxGasPrice New max gas price
function changeMaxGasPrice(uint256 _maxGasPrice) public onlyOwner {
require(_maxGasPrice < 2000 gwei, "Max gas price over the limit");
MAX_GAS_PRICE = _maxGasPrice;
}
}
{
"compilationTarget": {
"MCDMonitorV2.sol": "MCDMonitorV2"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_newMcdSaverTakerAddress","type":"address"},{"internalType":"address","name":"_mcdSaverFlashLoan","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BOOST_GAS_COST","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BOOST_SELECTOR","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BOT_REGISTRY_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_GAS_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PROXY_PERMISSION_ADDR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REPAY_GAS_COST","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REPAY_SELECTOR","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"srcAddr","type":"address"},{"internalType":"address","name":"destAddr","type":"address"},{"internalType":"uint256","name":"srcAmount","type":"uint256"},{"internalType":"uint256","name":"destAmount","type":"uint256"},{"internalType":"uint256","name":"minPrice","type":"uint256"},{"internalType":"uint256","name":"dfsFeeDivider","type":"uint256"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"wrapper","type":"address"},{"internalType":"bytes","name":"wrapperData","type":"bytes"},{"components":[{"internalType":"address","name":"wrapper","type":"address"},{"internalType":"address","name":"exchangeAddr","type":"address"},{"internalType":"address","name":"allowanceTarget","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"protocolFee","type":"uint256"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct DFSExchangeData.OffchainData","name":"offchainData","type":"tuple"}],"internalType":"struct DFSExchangeData.ExchangeData","name":"_exchangeData","type":"tuple"},{"internalType":"uint256","name":"_cdpId","type":"uint256"},{"internalType":"uint256","name":"_nextPrice","type":"uint256"},{"internalType":"address","name":"_joinAddr","type":"address"},{"internalType":"enum FLHelper.FLType","name":"_flType","type":"uint8"}],"name":"boostFor","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_gasAmount","type":"uint256"}],"name":"calcGasCost","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_gasCost","type":"uint256"}],"name":"changeBoostGasCost","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxGasPrice","type":"uint256"}],"name":"changeMaxGasPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_gasCost","type":"uint256"}],"name":"changeRepayGasCost","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum StaticV2.Method","name":"_method","type":"uint8"},{"internalType":"uint256","name":"_cdpId","type":"uint256"},{"internalType":"uint256","name":"_nextPrice","type":"uint256"}],"name":"checkPreconditions","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_cdpId","type":"uint256"},{"internalType":"bytes32","name":"_ilk","type":"bytes32"}],"name":"getCdpInfo","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_cdpId","type":"uint256"}],"name":"getOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_ilk","type":"bytes32"}],"name":"getPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_cdpId","type":"uint256"},{"internalType":"uint256","name":"_nextPrice","type":"uint256"}],"name":"getRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"kill","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"logger","outputs":[{"internalType":"contract DefisaverLogger","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"manager","outputs":[{"internalType":"contract Manager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mcdSaverTakerAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"cdpIds","type":"uint256[]"},{"internalType":"bool[]","name":"_shouldRemoveAuth","type":"bool[]"}],"name":"migrate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"monitorProxyContract","outputs":[{"internalType":"contract MCDMonitorProxyV2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum StaticV2.Method","name":"_method","type":"uint8"},{"internalType":"uint256","name":"_cdpId","type":"uint256"},{"internalType":"uint256","name":"_nextPrice","type":"uint256"},{"internalType":"uint256","name":"_beforeRatio","type":"uint256"}],"name":"ratioGoodAfter","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"srcAddr","type":"address"},{"internalType":"address","name":"destAddr","type":"address"},{"internalType":"uint256","name":"srcAmount","type":"uint256"},{"internalType":"uint256","name":"destAmount","type":"uint256"},{"internalType":"uint256","name":"minPrice","type":"uint256"},{"internalType":"uint256","name":"dfsFeeDivider","type":"uint256"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"wrapper","type":"address"},{"internalType":"bytes","name":"wrapperData","type":"bytes"},{"components":[{"internalType":"address","name":"wrapper","type":"address"},{"internalType":"address","name":"exchangeAddr","type":"address"},{"internalType":"address","name":"allowanceTarget","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"protocolFee","type":"uint256"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct DFSExchangeData.OffchainData","name":"offchainData","type":"tuple"}],"internalType":"struct DFSExchangeData.ExchangeData","name":"_exchangeData","type":"tuple"},{"internalType":"uint256","name":"_cdpId","type":"uint256"},{"internalType":"uint256","name":"_nextPrice","type":"uint256"},{"internalType":"address","name":"_joinAddr","type":"address"},{"internalType":"enum FLHelper.FLType","name":"_flType","type":"uint8"}],"name":"repayFor","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_admin","type":"address"}],"name":"setAdminByAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_admin","type":"address"}],"name":"setAdminByOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"setOwnerByAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"spotter","outputs":[{"internalType":"contract Spotter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"subscriptionsContract","outputs":[{"internalType":"contract ISubscriptionsV2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vat","outputs":[{"internalType":"contract Vat","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawStuckFunds","outputs":[],"stateMutability":"nonpayable","type":"function"}]