文件 1 的 1:DSProxy.sol
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;
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) {
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
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");
(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");
(bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
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) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
} 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));
}
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) {
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 = msg.sender;
admin = 0x25eFA336886C74eA8E282ac466BdCd0199f85BB9;
}
function setAdminByOwner(address _admin) public {
require(msg.sender == owner);
require(admin == address(0));
admin = _admin;
}
function setAdminByAdmin(address _admin) public {
require(msg.sender == admin);
admin = _admin;
}
function setOwnerByAdmin(address _owner) public {
require(msg.sender == admin);
owner = _owner;
}
function kill() public onlyOwner {
selfdestruct(payable(owner));
}
function withdrawStuckFunds(address _token, uint _amount) public onlyOwner {
if (_token == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) {
payable(owner).transfer(_amount);
} else {
ERC20(_token).safeTransfer(owner, _amount);
}
}
} 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;
}
} abstract contract GasTokenInterface is ERC20 {
function free(uint256 value) public virtual returns (bool success);
function freeUpTo(uint256 value) public virtual returns (uint256 freed);
function freeFrom(address from, uint256 value) public virtual returns (bool success);
function freeFromUpTo(address from, uint256 value) public virtual returns (uint256 freed);
} contract GasBurner {
GasTokenInterface public constant gasToken = GasTokenInterface(0x0000000000b3F879cb30FE243b4Dfee438691c04);
modifier burnGas(uint _amount) {
if (gasToken.balanceOf(address(this)) >= _amount) {
gasToken.free(_amount);
}
_;
}
} abstract contract DSProxyInterface {
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);
}
contract CompoundMonitorProxy is AdminAuth {
using SafeERC20 for ERC20;
uint public CHANGE_PERIOD;
address public monitor;
address public newMonitor;
address public lastMonitor;
uint public changeRequestedTimestamp;
mapping(address => bool) public allowed;
event MonitorChangeInitiated(address oldMonitor, address newMonitor);
event MonitorChangeCanceled();
event MonitorChangeFinished(address monitor);
event MonitorChangeReverted(address monitor);
modifier onlyAllowed() {
require(allowed[msg.sender] || msg.sender == owner);
_;
}
modifier onlyMonitor() {
require (msg.sender == monitor);
_;
}
constructor(uint _changePeriod) public {
CHANGE_PERIOD = _changePeriod * 1 days;
}
function callExecute(address _owner, address _compoundSaverProxy, bytes memory _data) public payable onlyMonitor {
DSProxyInterface(_owner).execute{value: msg.value}(_compoundSaverProxy, _data);
if (address(this).balance > 0) {
msg.sender.transfer(address(this).balance);
}
}
function setMonitor(address _monitor) public onlyAllowed {
require(monitor == address(0));
monitor = _monitor;
}
function changeMonitor(address _newMonitor) public onlyAllowed {
require(changeRequestedTimestamp == 0);
changeRequestedTimestamp = now;
lastMonitor = monitor;
newMonitor = _newMonitor;
emit MonitorChangeInitiated(lastMonitor, newMonitor);
}
function cancelMonitorChange() public onlyAllowed {
require(changeRequestedTimestamp > 0);
changeRequestedTimestamp = 0;
newMonitor = address(0);
emit MonitorChangeCanceled();
}
function confirmNewMonitor() public onlyAllowed {
require((changeRequestedTimestamp + CHANGE_PERIOD) < now);
require(changeRequestedTimestamp != 0);
require(newMonitor != address(0));
monitor = newMonitor;
newMonitor = address(0);
changeRequestedTimestamp = 0;
emit MonitorChangeFinished(monitor);
}
function revertMonitor() public onlyAllowed {
require(lastMonitor != address(0));
monitor = lastMonitor;
emit MonitorChangeReverted(monitor);
}
function addAllowed(address _user) public onlyAllowed {
allowed[_user] = true;
}
function removeAllowed(address _user) public onlyAllowed {
allowed[_user] = false;
}
function setChangePeriod(uint _periodInDays) public onlyAllowed {
require(_periodInDays * 1 days > CHANGE_PERIOD);
CHANGE_PERIOD = _periodInDays * 1 days;
}
function withdrawToken(address _token) public onlyOwner {
uint balance = ERC20(_token).balanceOf(address(this));
ERC20(_token).safeTransfer(msg.sender, balance);
}
function withdrawEth() public onlyOwner {
uint balance = address(this).balance;
msg.sender.transfer(balance);
}
}
contract CompoundSubscriptions is AdminAuth {
struct CompoundHolder {
address user;
uint128 minRatio;
uint128 maxRatio;
uint128 optimalRatioBoost;
uint128 optimalRatioRepay;
bool boostEnabled;
}
struct SubPosition {
uint arrPos;
bool subscribed;
}
CompoundHolder[] public subscribers;
mapping (address => SubPosition) public subscribersPos;
uint public changeIndex;
event Subscribed(address indexed user);
event Unsubscribed(address indexed user);
event Updated(address indexed user);
event ParamUpdates(address indexed user, uint128, uint128, uint128, uint128, bool);
function subscribe(uint128 _minRatio, uint128 _maxRatio, uint128 _optimalBoost, uint128 _optimalRepay, bool _boostEnabled) external {
uint128 localMaxRatio = _boostEnabled ? _maxRatio : uint128(-1);
require(checkParams(_minRatio, localMaxRatio), "Must be correct params");
SubPosition storage subInfo = subscribersPos[msg.sender];
CompoundHolder memory subscription = CompoundHolder({
minRatio: _minRatio,
maxRatio: localMaxRatio,
optimalRatioBoost: _optimalBoost,
optimalRatioRepay: _optimalRepay,
user: msg.sender,
boostEnabled: _boostEnabled
});
changeIndex++;
if (subInfo.subscribed) {
subscribers[subInfo.arrPos] = subscription;
emit Updated(msg.sender);
emit ParamUpdates(msg.sender, _minRatio, localMaxRatio, _optimalBoost, _optimalRepay, _boostEnabled);
} else {
subscribers.push(subscription);
subInfo.arrPos = subscribers.length - 1;
subInfo.subscribed = true;
emit Subscribed(msg.sender);
}
}
function unsubscribe() external {
_unsubscribe(msg.sender);
}
function checkParams(uint128 _minRatio, uint128 _maxRatio) internal pure returns (bool) {
if (_minRatio > _maxRatio) {
return false;
}
return true;
}
function _unsubscribe(address _user) internal {
require(subscribers.length > 0, "Must have subscribers in the list");
SubPosition storage subInfo = subscribersPos[_user];
require(subInfo.subscribed, "Must first be subscribed");
address lastOwner = subscribers[subscribers.length - 1].user;
SubPosition storage subInfo2 = subscribersPos[lastOwner];
subInfo2.arrPos = subInfo.arrPos;
subscribers[subInfo.arrPos] = subscribers[subscribers.length - 1];
subscribers.pop();
changeIndex++;
subInfo.subscribed = false;
subInfo.arrPos = 0;
emit Unsubscribed(msg.sender);
}
function isSubscribed(address _user) public view returns (bool) {
SubPosition storage subInfo = subscribersPos[_user];
return subInfo.subscribed;
}
function getHolder(address _user) public view returns (CompoundHolder memory) {
SubPosition storage subInfo = subscribersPos[_user];
return subscribers[subInfo.arrPos];
}
function getSubscribers() public view returns (CompoundHolder[] memory) {
return subscribers;
}
function getSubscribersByPage(uint _page, uint _perPage) public view returns (CompoundHolder[] memory) {
CompoundHolder[] memory holders = new CompoundHolder[](_perPage);
uint start = _page * _perPage;
uint end = start + _perPage;
end = (end > holders.length) ? holders.length : end;
uint count = 0;
for (uint i = start; i < end; i++) {
holders[count] = subscribers[i];
count++;
}
return holders;
}
function unsubscribeByAdmin(address _user) public onlyOwner {
SubPosition storage subInfo = subscribersPos[_user];
if (subInfo.subscribed) {
_unsubscribe(_user);
}
}
} 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;
}
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);
}
}
}
} contract DefisaverLogger {
event LogEvent(
address indexed contractAddress,
address indexed caller,
string indexed logName,
bytes data
);
function Log(address _contract, address _caller, string memory _logName, bytes memory _data)
public
{
emit LogEvent(_contract, _caller, _logName, _data);
}
} abstract contract CompoundOracleInterface {
function getUnderlyingPrice(address cToken) external view virtual returns (uint);
}
abstract contract ComptrollerInterface {
struct CompMarketState {
uint224 index;
uint32 block;
}
function claimComp(address holder) public virtual;
function claimComp(address holder, address[] memory cTokens) public virtual;
function claimComp(address[] memory holders, address[] memory cTokens, bool borrowers, bool suppliers) public virtual;
function compSupplyState(address) public view virtual returns (CompMarketState memory);
function compSupplierIndex(address,address) public view virtual returns (uint);
function compAccrued(address) public view virtual returns (uint);
function compBorrowState(address) public view virtual returns (CompMarketState memory);
function compBorrowerIndex(address,address) public view virtual returns (uint);
function enterMarkets(address[] calldata cTokens) external virtual returns (uint256[] memory);
function exitMarket(address cToken) external virtual returns (uint256);
function getAssetsIn(address account) external virtual view returns (address[] memory);
function markets(address account) public virtual view returns (bool, uint256);
function getAccountLiquidity(address account) external virtual view returns (uint256, uint256, uint256);
function oracle() public virtual view returns (address);
} abstract contract CTokenInterface is ERC20 {
function mint(uint256 mintAmount) external virtual returns (uint256);
function accrueInterest() public virtual returns (uint);
function redeem(uint256 redeemTokens) external virtual returns (uint256);
function redeemUnderlying(uint256 redeemAmount) external virtual returns (uint256);
function borrow(uint256 borrowAmount) external virtual returns (uint256);
function borrowIndex() public view virtual returns (uint);
function borrowBalanceStored(address) public view virtual returns(uint);
function repayBorrow(uint256 repayAmount) external virtual returns (uint256);
function repayBorrow() external virtual payable;
function repayBorrowBehalf(address borrower, uint256 repayAmount) external virtual returns (uint256);
function repayBorrowBehalf(address borrower) external virtual payable;
function liquidateBorrow(address borrower, uint256 repayAmount, address cTokenCollateral)
external virtual
returns (uint256);
function liquidateBorrow(address borrower, address cTokenCollateral) external virtual payable;
function exchangeRateCurrent() external virtual returns (uint256);
function supplyRatePerBlock() external virtual returns (uint256);
function borrowRatePerBlock() external virtual returns (uint256);
function totalReserves() external virtual returns (uint256);
function reserveFactorMantissa() external virtual returns (uint256);
function borrowBalanceCurrent(address account) external virtual returns (uint256);
function totalBorrowsCurrent() external virtual returns (uint256);
function getCash() external virtual returns (uint256);
function balanceOfUnderlying(address owner) external virtual returns (uint256);
function underlying() external virtual returns (address);
function getAccountSnapshot(address account) external virtual view returns (uint, uint, uint, uint);
} contract CarefulMath {
enum MathError {
NO_ERROR,
DIVISION_BY_ZERO,
INTEGER_OVERFLOW,
INTEGER_UNDERFLOW
}
function mulUInt(uint a, uint b) internal pure returns (MathError, uint) {
if (a == 0) {
return (MathError.NO_ERROR, 0);
}
uint c = a * b;
if (c / a != b) {
return (MathError.INTEGER_OVERFLOW, 0);
} else {
return (MathError.NO_ERROR, c);
}
}
function divUInt(uint a, uint b) internal pure returns (MathError, uint) {
if (b == 0) {
return (MathError.DIVISION_BY_ZERO, 0);
}
return (MathError.NO_ERROR, a / b);
}
function subUInt(uint a, uint b) internal pure returns (MathError, uint) {
if (b <= a) {
return (MathError.NO_ERROR, a - b);
} else {
return (MathError.INTEGER_UNDERFLOW, 0);
}
}
function addUInt(uint a, uint b) internal pure returns (MathError, uint) {
uint c = a + b;
if (c >= a) {
return (MathError.NO_ERROR, c);
} else {
return (MathError.INTEGER_OVERFLOW, 0);
}
}
function addThenSubUInt(uint a, uint b, uint c) internal pure returns (MathError, uint) {
(MathError err0, uint sum) = addUInt(a, b);
if (err0 != MathError.NO_ERROR) {
return (err0, 0);
}
return subUInt(sum, c);
}
} contract Exponential is CarefulMath {
uint constant expScale = 1e18;
uint constant doubleScale = 1e36;
uint constant halfExpScale = expScale/2;
uint constant mantissaOne = expScale;
struct Exp {
uint mantissa;
}
struct Double {
uint mantissa;
}
function getExp(uint num, uint denom) pure internal returns (MathError, Exp memory) {
(MathError err0, uint scaledNumerator) = mulUInt(num, expScale);
if (err0 != MathError.NO_ERROR) {
return (err0, Exp({mantissa: 0}));
}
(MathError err1, uint rational) = divUInt(scaledNumerator, denom);
if (err1 != MathError.NO_ERROR) {
return (err1, Exp({mantissa: 0}));
}
return (MathError.NO_ERROR, Exp({mantissa: rational}));
}
function addExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) {
(MathError error, uint result) = addUInt(a.mantissa, b.mantissa);
return (error, Exp({mantissa: result}));
}
function subExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) {
(MathError error, uint result) = subUInt(a.mantissa, b.mantissa);
return (error, Exp({mantissa: result}));
}
function mulScalar(Exp memory a, uint scalar) pure internal returns (MathError, Exp memory) {
(MathError err0, uint scaledMantissa) = mulUInt(a.mantissa, scalar);
if (err0 != MathError.NO_ERROR) {
return (err0, Exp({mantissa: 0}));
}
return (MathError.NO_ERROR, Exp({mantissa: scaledMantissa}));
}
function mulScalarTruncate(Exp memory a, uint scalar) pure internal returns (MathError, uint) {
(MathError err, Exp memory product) = mulScalar(a, scalar);
if (err != MathError.NO_ERROR) {
return (err, 0);
}
return (MathError.NO_ERROR, truncate(product));
}
function mulScalarTruncateAddUInt(Exp memory a, uint scalar, uint addend) pure internal returns (MathError, uint) {
(MathError err, Exp memory product) = mulScalar(a, scalar);
if (err != MathError.NO_ERROR) {
return (err, 0);
}
return addUInt(truncate(product), addend);
}
function divScalar(Exp memory a, uint scalar) pure internal returns (MathError, Exp memory) {
(MathError err0, uint descaledMantissa) = divUInt(a.mantissa, scalar);
if (err0 != MathError.NO_ERROR) {
return (err0, Exp({mantissa: 0}));
}
return (MathError.NO_ERROR, Exp({mantissa: descaledMantissa}));
}
function divScalarByExp(uint scalar, Exp memory divisor) pure internal returns (MathError, Exp memory) {
(MathError err0, uint numerator) = mulUInt(expScale, scalar);
if (err0 != MathError.NO_ERROR) {
return (err0, Exp({mantissa: 0}));
}
return getExp(numerator, divisor.mantissa);
}
function divScalarByExpTruncate(uint scalar, Exp memory divisor) pure internal returns (MathError, uint) {
(MathError err, Exp memory fraction) = divScalarByExp(scalar, divisor);
if (err != MathError.NO_ERROR) {
return (err, 0);
}
return (MathError.NO_ERROR, truncate(fraction));
}
function mulExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) {
(MathError err0, uint doubleScaledProduct) = mulUInt(a.mantissa, b.mantissa);
if (err0 != MathError.NO_ERROR) {
return (err0, Exp({mantissa: 0}));
}
(MathError err1, uint doubleScaledProductWithHalfScale) = addUInt(halfExpScale, doubleScaledProduct);
if (err1 != MathError.NO_ERROR) {
return (err1, Exp({mantissa: 0}));
}
(MathError err2, uint product) = divUInt(doubleScaledProductWithHalfScale, expScale);
assert(err2 == MathError.NO_ERROR);
return (MathError.NO_ERROR, Exp({mantissa: product}));
}
function mulExp(uint a, uint b) pure internal returns (MathError, Exp memory) {
return mulExp(Exp({mantissa: a}), Exp({mantissa: b}));
}
function mulExp3(Exp memory a, Exp memory b, Exp memory c) pure internal returns (MathError, Exp memory) {
(MathError err, Exp memory ab) = mulExp(a, b);
if (err != MathError.NO_ERROR) {
return (err, ab);
}
return mulExp(ab, c);
}
function divExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) {
return getExp(a.mantissa, b.mantissa);
}
function truncate(Exp memory exp) pure internal returns (uint) {
return exp.mantissa / expScale;
}
function lessThanExp(Exp memory left, Exp memory right) pure internal returns (bool) {
return left.mantissa < right.mantissa;
}
function lessThanOrEqualExp(Exp memory left, Exp memory right) pure internal returns (bool) {
return left.mantissa <= right.mantissa;
}
function greaterThanExp(Exp memory left, Exp memory right) pure internal returns (bool) {
return left.mantissa > right.mantissa;
}
function isZeroExp(Exp memory value) pure internal returns (bool) {
return value.mantissa == 0;
}
function sub_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
return Exp({mantissa: sub_(a.mantissa, b.mantissa)});
}
function sub_(Double memory a, Double memory b) pure internal returns (Double memory) {
return Double({mantissa: sub_(a.mantissa, b.mantissa)});
}
function sub_(uint a, uint b) pure internal returns (uint) {
return sub_(a, b, "subtraction underflow");
}
function sub_(uint a, uint b, string memory errorMessage) pure internal returns (uint) {
require(b <= a, errorMessage);
return a - b;
}
function mul_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
return Exp({mantissa: mul_(a.mantissa, b.mantissa) / expScale});
}
function mul_(Exp memory a, uint b) pure internal returns (Exp memory) {
return Exp({mantissa: mul_(a.mantissa, b)});
}
function mul_(uint a, Exp memory b) pure internal returns (uint) {
return mul_(a, b.mantissa) / expScale;
}
function mul_(Double memory a, Double memory b) pure internal returns (Double memory) {
return Double({mantissa: mul_(a.mantissa, b.mantissa) / doubleScale});
}
function mul_(Double memory a, uint b) pure internal returns (Double memory) {
return Double({mantissa: mul_(a.mantissa, b)});
}
function mul_(uint a, Double memory b) pure internal returns (uint) {
return mul_(a, b.mantissa) / doubleScale;
}
function mul_(uint a, uint b) pure internal returns (uint) {
return mul_(a, b, "multiplication overflow");
}
function mul_(uint a, uint b, string memory errorMessage) pure internal returns (uint) {
if (a == 0 || b == 0) {
return 0;
}
uint c = a * b;
require(c / a == b, errorMessage);
return c;
}
function div_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
return Exp({mantissa: div_(mul_(a.mantissa, expScale), b.mantissa)});
}
function div_(Exp memory a, uint b) pure internal returns (Exp memory) {
return Exp({mantissa: div_(a.mantissa, b)});
}
function div_(uint a, Exp memory b) pure internal returns (uint) {
return div_(mul_(a, expScale), b.mantissa);
}
function div_(Double memory a, Double memory b) pure internal returns (Double memory) {
return Double({mantissa: div_(mul_(a.mantissa, doubleScale), b.mantissa)});
}
function div_(Double memory a, uint b) pure internal returns (Double memory) {
return Double({mantissa: div_(a.mantissa, b)});
}
function div_(uint a, Double memory b) pure internal returns (uint) {
return div_(mul_(a, doubleScale), b.mantissa);
}
function div_(uint a, uint b) pure internal returns (uint) {
return div_(a, b, "divide by zero");
}
function div_(uint a, uint b, string memory errorMessage) pure internal returns (uint) {
require(b > 0, errorMessage);
return a / b;
}
function add_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
return Exp({mantissa: add_(a.mantissa, b.mantissa)});
}
function add_(Double memory a, Double memory b) pure internal returns (Double memory) {
return Double({mantissa: add_(a.mantissa, b.mantissa)});
}
function add_(uint a, uint b) pure internal returns (uint) {
return add_(a, b, "addition overflow");
}
function add_(uint a, uint b, string memory errorMessage) pure internal returns (uint) {
uint c = a + b;
require(c >= a, errorMessage);
return c;
}
} contract CompoundSafetyRatio is Exponential, DSMath {
ComptrollerInterface public constant comp = ComptrollerInterface(0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B);
function getSafetyRatio(address _user) public view returns (uint) {
address[] memory assets = comp.getAssetsIn(_user);
address oracleAddr = comp.oracle();
uint sumCollateral = 0;
uint sumBorrow = 0;
for (uint i = 0; i < assets.length; i++) {
address asset = assets[i];
(, uint cTokenBalance, uint borrowBalance, uint exchangeRateMantissa)
= CTokenInterface(asset).getAccountSnapshot(_user);
Exp memory oraclePrice;
if (cTokenBalance != 0 || borrowBalance != 0) {
oraclePrice = Exp({mantissa: CompoundOracleInterface(oracleAddr).getUnderlyingPrice(asset)});
}
if (cTokenBalance != 0) {
(, uint collFactorMantissa) = comp.markets(address(asset));
Exp memory collateralFactor = Exp({mantissa: collFactorMantissa});
Exp memory exchangeRate = Exp({mantissa: exchangeRateMantissa});
(, Exp memory tokensToUsd) = mulExp3(collateralFactor, exchangeRate, oraclePrice);
(, sumCollateral) = mulScalarTruncateAddUInt(tokensToUsd, cTokenBalance, sumCollateral);
}
if (borrowBalance != 0) {
(, sumBorrow) = mulScalarTruncateAddUInt(oraclePrice, borrowBalance, sumBorrow);
}
}
if (sumBorrow == 0) return uint(-1);
uint borrowPowerUsed = (sumBorrow * 10**18) / sumCollateral;
return wdiv(1e18, borrowPowerUsed);
}
} abstract contract TokenInterface {
function allowance(address, address) public virtual returns (uint256);
function balanceOf(address) public virtual returns (uint256);
function approve(address, uint256) public virtual;
function transfer(address, uint256) public virtual returns (bool);
function transferFrom(address, address, uint256) public virtual returns (bool);
function deposit() public virtual payable;
function withdraw(uint256) public virtual;
} interface ExchangeInterfaceV2 {
function sell(address _srcAddr, address _destAddr, uint _srcAmount) external payable returns (uint);
function buy(address _srcAddr, address _destAddr, uint _destAmount) external payable returns(uint);
function getSellRate(address _srcAddr, address _destAddr, uint _srcAmount) external view returns (uint);
function getBuyRate(address _srcAddr, address _destAddr, uint _srcAmount) external view returns (uint);
} contract ZrxAllowlist is AdminAuth {
mapping (address => bool) public zrxAllowlist;
mapping(address => bool) private nonPayableAddrs;
constructor() public {
zrxAllowlist[0x6958F5e95332D93D21af0D7B9Ca85B8212fEE0A5] = true;
zrxAllowlist[0x61935CbDd02287B511119DDb11Aeb42F1593b7Ef] = true;
zrxAllowlist[0xDef1C0ded9bec7F1a1670819833240f027b25EfF] = true;
zrxAllowlist[0x080bf510FCbF18b91105470639e9561022937712] = true;
nonPayableAddrs[0x080bf510FCbF18b91105470639e9561022937712] = true;
}
function setAllowlistAddr(address _zrxAddr, bool _state) public onlyOwner {
zrxAllowlist[_zrxAddr] = _state;
}
function isZrxAddr(address _zrxAddr) public view returns (bool) {
return zrxAllowlist[_zrxAddr];
}
function addNonPayableAddr(address _nonPayableAddr) public onlyOwner {
nonPayableAddrs[_nonPayableAddr] = true;
}
function removeNonPayableAddr(address _nonPayableAddr) public onlyOwner {
nonPayableAddrs[_nonPayableAddr] = false;
}
function isNonPayableAddr(address _addr) public view returns(bool) {
return nonPayableAddrs[_addr];
}
} contract Discount {
address public owner;
mapping(address => CustomServiceFee) public serviceFees;
uint256 constant MAX_SERVICE_FEE = 400;
struct CustomServiceFee {
bool active;
uint256 amount;
}
constructor() public {
owner = msg.sender;
}
function isCustomFeeSet(address _user) public view returns (bool) {
return serviceFees[_user].active;
}
function getCustomServiceFee(address _user) public view returns (uint256) {
return serviceFees[_user].amount;
}
function setServiceFee(address _user, uint256 _fee) public {
require(msg.sender == owner, "Only owner");
require(_fee >= MAX_SERVICE_FEE || _fee == 0);
serviceFees[_user] = CustomServiceFee({active: true, amount: _fee});
}
function disableServiceFee(address _user) public {
require(msg.sender == owner, "Only owner");
serviceFees[_user] = CustomServiceFee({active: false, amount: 0});
}
} contract SaverExchangeHelper {
using SafeERC20 for ERC20;
address public constant KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
address public constant WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address payable public constant WALLET_ID = 0x322d58b9E75a6918f7e7849AEe0fF09369977e08;
address public constant DISCOUNT_ADDRESS = 0x1b14E8D511c9A4395425314f849bD737BAF8208F;
address public constant SAVER_EXCHANGE_REGISTRY = 0x25dd3F51e0C3c3Ff164DDC02A8E4D65Bb9cBB12D;
address public constant ERC20_PROXY_0X = 0x95E6F48254609A6ee006F7D493c8e5fB97094ceF;
address public constant ZRX_ALLOWLIST_ADDR = 0x4BA1f38427b33B8ab7Bb0490200dAE1F1C36823F;
function getDecimals(address _token) internal view returns (uint256) {
if (_token == KYBER_ETH_ADDRESS) return 18;
return ERC20(_token).decimals();
}
function getBalance(address _tokenAddr) internal view returns (uint balance) {
if (_tokenAddr == KYBER_ETH_ADDRESS) {
balance = address(this).balance;
} else {
balance = ERC20(_tokenAddr).balanceOf(address(this));
}
}
function approve0xProxy(address _tokenAddr, uint _amount) internal {
if (_tokenAddr != KYBER_ETH_ADDRESS) {
ERC20(_tokenAddr).safeApprove(address(ERC20_PROXY_0X), _amount);
}
}
function sendLeftover(address _srcAddr, address _destAddr, address payable _to) internal {
if (address(this).balance > 0) {
_to.transfer(address(this).balance);
}
if (getBalance(_srcAddr) > 0) {
ERC20(_srcAddr).safeTransfer(_to, getBalance(_srcAddr));
}
if (getBalance(_destAddr) > 0) {
ERC20(_destAddr).safeTransfer(_to, getBalance(_destAddr));
}
}
function sliceUint(bytes memory bs, uint256 start) internal pure returns (uint256) {
require(bs.length >= start + 32, "slicing out of range");
uint256 x;
assembly {
x := mload(add(bs, add(0x20, start)))
}
return x;
}
} contract SaverExchangeRegistry is AdminAuth {
mapping(address => bool) private wrappers;
constructor() public {
wrappers[0x880A845A85F843a5c67DB2061623c6Fc3bB4c511] = true;
wrappers[0x4c9B55f2083629A1F7aDa257ae984E03096eCD25] = true;
wrappers[0x42A9237b872368E1bec4Ca8D26A928D7d39d338C] = true;
}
function addWrapper(address _wrapper) public onlyOwner {
wrappers[_wrapper] = true;
}
function removeWrapper(address _wrapper) public onlyOwner {
wrappers[_wrapper] = false;
}
function isWrapper(address _wrapper) public view returns(bool) {
return wrappers[_wrapper];
}
}
contract SaverExchangeCore is SaverExchangeHelper, DSMath {
enum ExchangeType { _, OASIS, KYBER, UNISWAP, ZEROX }
enum ActionType { SELL, BUY }
struct ExchangeData {
address srcAddr;
address destAddr;
uint srcAmount;
uint destAmount;
uint minPrice;
address wrapper;
address exchangeAddr;
bytes callData;
uint256 price0x;
}
function _sell(ExchangeData memory exData) internal returns (address, uint) {
address wrapper;
uint swapedTokens;
bool success;
uint tokensLeft = exData.srcAmount;
if (exData.srcAddr == KYBER_ETH_ADDRESS) {
exData.srcAddr = ethToWethAddr(exData.srcAddr);
TokenInterface(WETH_ADDRESS).deposit.value(exData.srcAmount)();
}
if (exData.price0x > 0) {
approve0xProxy(exData.srcAddr, exData.srcAmount);
uint ethAmount = getProtocolFee(exData.srcAddr, exData.srcAmount);
(success, swapedTokens, tokensLeft) = takeOrder(exData, ethAmount, ActionType.SELL);
if (success) {
wrapper = exData.exchangeAddr;
}
}
if (!success) {
swapedTokens = saverSwap(exData, ActionType.SELL);
wrapper = exData.wrapper;
}
require(getBalance(exData.destAddr) >= wmul(exData.minPrice, exData.srcAmount), "Final amount isn't correct");
if (getBalance(WETH_ADDRESS) > 0) {
TokenInterface(WETH_ADDRESS).withdraw(
TokenInterface(WETH_ADDRESS).balanceOf(address(this))
);
}
return (wrapper, swapedTokens);
}
function _buy(ExchangeData memory exData) internal returns (address, uint) {
address wrapper;
uint swapedTokens;
bool success;
require(exData.destAmount != 0, "Dest amount must be specified");
if (exData.srcAddr == KYBER_ETH_ADDRESS) {
exData.srcAddr = ethToWethAddr(exData.srcAddr);
TokenInterface(WETH_ADDRESS).deposit.value(exData.srcAmount)();
}
if (exData.price0x > 0) {
approve0xProxy(exData.srcAddr, exData.srcAmount);
uint ethAmount = getProtocolFee(exData.srcAddr, exData.srcAmount);
(success, swapedTokens,) = takeOrder(exData, ethAmount, ActionType.BUY);
if (success) {
wrapper = exData.exchangeAddr;
}
}
if (!success) {
swapedTokens = saverSwap(exData, ActionType.BUY);
wrapper = exData.wrapper;
}
require(getBalance(exData.destAddr) >= exData.destAmount, "Final amount isn't correct");
if (getBalance(WETH_ADDRESS) > 0) {
TokenInterface(WETH_ADDRESS).withdraw(
TokenInterface(WETH_ADDRESS).balanceOf(address(this))
);
}
return (wrapper, getBalance(exData.destAddr));
}
function takeOrder(
ExchangeData memory _exData,
uint256 _ethAmount,
ActionType _type
) private returns (bool success, uint256, uint256) {
if (_type == ActionType.SELL) {
writeUint256(_exData.callData, 36, _exData.srcAmount);
} else {
writeUint256(_exData.callData, 36, _exData.destAmount);
}
if (ZrxAllowlist(ZRX_ALLOWLIST_ADDR).isNonPayableAddr(_exData.exchangeAddr)) {
_ethAmount = 0;
}
uint256 tokensBefore = getBalance(_exData.destAddr);
if (ZrxAllowlist(ZRX_ALLOWLIST_ADDR).isZrxAddr(_exData.exchangeAddr)) {
(success, ) = _exData.exchangeAddr.call{value: _ethAmount}(_exData.callData);
} else {
success = false;
}
uint256 tokensSwaped = 0;
uint256 tokensLeft = _exData.srcAmount;
if (success) {
tokensLeft = getBalance(_exData.srcAddr);
if (_exData.destAddr == KYBER_ETH_ADDRESS) {
TokenInterface(WETH_ADDRESS).withdraw(
TokenInterface(WETH_ADDRESS).balanceOf(address(this))
);
}
tokensSwaped = getBalance(_exData.destAddr) - tokensBefore;
}
return (success, tokensSwaped, tokensLeft);
}
function saverSwap(ExchangeData memory _exData, ActionType _type) internal returns (uint swapedTokens) {
require(SaverExchangeRegistry(SAVER_EXCHANGE_REGISTRY).isWrapper(_exData.wrapper), "Wrapper is not valid");
uint ethValue = 0;
ERC20(_exData.srcAddr).safeTransfer(_exData.wrapper, _exData.srcAmount);
if (_type == ActionType.SELL) {
swapedTokens = ExchangeInterfaceV2(_exData.wrapper).
sell{value: ethValue}(_exData.srcAddr, _exData.destAddr, _exData.srcAmount);
} else {
swapedTokens = ExchangeInterfaceV2(_exData.wrapper).
buy{value: ethValue}(_exData.srcAddr, _exData.destAddr, _exData.destAmount);
}
}
function writeUint256(bytes memory _b, uint256 _index, uint _input) internal pure {
if (_b.length < _index + 32) {
revert("Incorrent lengt while writting bytes32");
}
bytes32 input = bytes32(_input);
_index += 32;
assembly {
mstore(add(_b, _index), input)
}
}
function ethToWethAddr(address _src) internal pure returns (address) {
return _src == KYBER_ETH_ADDRESS ? WETH_ADDRESS : _src;
}
function getProtocolFee(address _srcAddr, uint256 _srcAmount) internal view returns(uint256) {
if (_srcAddr != WETH_ADDRESS) return address(this).balance;
if (address(this).balance > _srcAmount) return address(this).balance - _srcAmount;
return address(this).balance;
}
function packExchangeData(ExchangeData memory _exData) public pure returns(bytes memory) {
bytes memory part1 = abi.encode(
_exData.srcAddr,
_exData.destAddr,
_exData.srcAmount,
_exData.destAmount
);
bytes memory part2 = abi.encode(
_exData.minPrice,
_exData.wrapper,
_exData.exchangeAddr,
_exData.callData,
_exData.price0x
);
return abi.encode(part1, part2);
}
function unpackExchangeData(bytes memory _data) public pure returns(ExchangeData memory _exData) {
(
bytes memory part1,
bytes memory part2
) = abi.decode(_data, (bytes,bytes));
(
_exData.srcAddr,
_exData.destAddr,
_exData.srcAmount,
_exData.destAmount
) = abi.decode(part1, (address,address,uint256,uint256));
(
_exData.minPrice,
_exData.wrapper,
_exData.exchangeAddr,
_exData.callData,
_exData.price0x
)
= abi.decode(part2, (uint256,address,address,bytes,uint256));
}
receive() external virtual payable {}
}
contract CompoundMonitor is AdminAuth, DSMath, CompoundSafetyRatio, GasBurner {
using SafeERC20 for ERC20;
enum Method { Boost, Repay }
uint public REPAY_GAS_TOKEN = 20;
uint public BOOST_GAS_TOKEN = 20;
uint constant public MAX_GAS_PRICE = 500000000000;
uint public REPAY_GAS_COST = 2000000;
uint public BOOST_GAS_COST = 2000000;
address public constant GAS_TOKEN_INTERFACE_ADDRESS = 0x0000000000b3F879cb30FE243b4Dfee438691c04;
address public constant DEFISAVER_LOGGER = 0x5c55B921f590a89C1Ebe84dF170E655a82b62126;
address public constant BOT_REGISTRY_ADDRESS = 0x637726f8b08a7ABE3aE3aCaB01A80E2d8ddeF77B;
CompoundMonitorProxy public compoundMonitorProxy;
CompoundSubscriptions public subscriptionsContract;
address public compoundFlashLoanTakerAddress;
DefisaverLogger public logger = DefisaverLogger(DEFISAVER_LOGGER);
modifier onlyApproved() {
require(BotRegistry(BOT_REGISTRY_ADDRESS).botList(msg.sender), "Not auth bot");
_;
}
constructor(address _compoundMonitorProxy, address _subscriptions, address _compoundFlashLoanTaker) public {
compoundMonitorProxy = CompoundMonitorProxy(_compoundMonitorProxy);
subscriptionsContract = CompoundSubscriptions(_subscriptions);
compoundFlashLoanTakerAddress = _compoundFlashLoanTaker;
}
function repayFor(
SaverExchangeCore.ExchangeData memory _exData,
address[2] memory _cAddresses,
address _user
) public payable onlyApproved burnGas(REPAY_GAS_TOKEN) {
(bool isAllowed, uint ratioBefore) = canCall(Method.Repay, _user);
require(isAllowed);
uint256 gasCost = calcGasCost(REPAY_GAS_COST);
compoundMonitorProxy.callExecute{value: msg.value}(
_user,
compoundFlashLoanTakerAddress,
abi.encodeWithSignature(
"repayWithLoan((address,address,uint256,uint256,uint256,address,address,bytes,uint256),address[2],uint256)",
_exData,
_cAddresses,
gasCost
)
);
(bool isGoodRatio, uint ratioAfter) = ratioGoodAfter(Method.Repay, _user);
require(isGoodRatio);
returnEth();
logger.Log(address(this), _user, "AutomaticCompoundRepay", abi.encode(ratioBefore, ratioAfter));
}
function boostFor(
SaverExchangeCore.ExchangeData memory _exData,
address[2] memory _cAddresses,
address _user
) public payable onlyApproved burnGas(BOOST_GAS_TOKEN) {
(bool isAllowed, uint ratioBefore) = canCall(Method.Boost, _user);
require(isAllowed);
uint256 gasCost = calcGasCost(BOOST_GAS_COST);
compoundMonitorProxy.callExecute{value: msg.value}(
_user,
compoundFlashLoanTakerAddress,
abi.encodeWithSignature(
"boostWithLoan((address,address,uint256,uint256,uint256,address,address,bytes,uint256),address[2],uint256)",
_exData,
_cAddresses,
gasCost
)
);
(bool isGoodRatio, uint ratioAfter) = ratioGoodAfter(Method.Boost, _user);
require(isGoodRatio);
returnEth();
logger.Log(address(this), _user, "AutomaticCompoundBoost", abi.encode(ratioBefore, ratioAfter));
}
function returnEth() internal {
if (address(this).balance > 0) {
msg.sender.transfer(address(this).balance);
}
}
function canCall(Method _method, address _user) public view returns(bool, uint) {
bool subscribed = subscriptionsContract.isSubscribed(_user);
CompoundSubscriptions.CompoundHolder memory holder = subscriptionsContract.getHolder(_user);
if (!subscribed) return (false, 0);
if (_method == Method.Boost && !holder.boostEnabled) return (false, 0);
uint currRatio = getSafetyRatio(_user);
if (_method == Method.Repay) {
return (currRatio < holder.minRatio, currRatio);
} else if (_method == Method.Boost) {
return (currRatio > holder.maxRatio, currRatio);
}
}
function ratioGoodAfter(Method _method, address _user) public view returns(bool, uint) {
CompoundSubscriptions.CompoundHolder memory holder;
holder= subscriptionsContract.getHolder(_user);
uint currRatio = getSafetyRatio(_user);
if (_method == Method.Repay) {
return (currRatio < holder.maxRatio, currRatio);
} else if (_method == Method.Boost) {
return (currRatio > holder.minRatio, currRatio);
}
}
function calcGasCost(uint _gasAmount) public view returns (uint) {
uint gasPrice = tx.gasprice <= MAX_GAS_PRICE ? tx.gasprice : MAX_GAS_PRICE;
return mul(gasPrice, _gasAmount);
}
function changeCompoundFlashLoanTaker(address _newCompoundFlashLoanTakerAddress) public onlyAdmin {
compoundFlashLoanTakerAddress = _newCompoundFlashLoanTakerAddress;
}
function changeBoostGasCost(uint _gasCost) public onlyOwner {
require(_gasCost < 3000000);
BOOST_GAS_COST = _gasCost;
}
function changeRepayGasCost(uint _gasCost) public onlyOwner {
require(_gasCost < 3000000);
REPAY_GAS_COST = _gasCost;
}
function transferERC20(address _tokenAddress, address _to, uint _amount) public onlyOwner {
ERC20(_tokenAddress).safeTransfer(_to, _amount);
}
function transferEth(address payable _to, uint _amount) public onlyOwner {
_to.transfer(_amount);
}
}