文件 1 的 1:MCDSaverFlashLoan.sol
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;
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);
}
} 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});
}
} 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;
} abstract contract Jug {
struct Ilk {
uint256 duty;
uint256 rho;
}
mapping (bytes32 => Ilk) public ilks;
function drip(bytes32) public virtual returns (uint);
} abstract contract Vat {
struct Urn {
uint256 ink;
uint256 art;
}
struct Ilk {
uint256 Art;
uint256 rate;
uint256 spot;
uint256 line;
uint256 dust;
}
mapping (bytes32 => mapping (address => Urn )) public urns;
mapping (bytes32 => Ilk) public ilks;
mapping (bytes32 => mapping (address => uint)) public gem;
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 Gem {
function dec() virtual public returns (uint);
function gem() virtual public returns (Gem);
function join(address, uint) virtual public payable;
function exit(address, uint) virtual public;
function approve(address, uint) virtual public;
function transfer(address, uint) virtual public returns (bool);
function transferFrom(address, address, uint) virtual public returns (bool);
function deposit() virtual public payable;
function withdraw(uint) virtual public;
function allowance(address, address) virtual public returns (uint);
} abstract contract DaiJoin {
function vat() public virtual returns (Vat);
function dai() public virtual returns (Gem);
function join(address, uint) public virtual payable;
function exit(address, uint) public virtual;
} abstract contract Join {
bytes32 public ilk;
function dec() virtual public view returns (uint);
function gem() virtual public view returns (Gem);
function join(address, uint) virtual public payable;
function exit(address, uint) virtual public;
} 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);
}
}
}
} abstract contract DSAuthority {
function canCall(address src, address dst, bytes4 sig) public virtual view returns (bool);
} contract DSAuthEvents {
event LogSetAuthority(address indexed authority);
event LogSetOwner(address indexed owner);
}
contract DSAuth is DSAuthEvents {
DSAuthority public authority;
address public owner;
constructor() public {
owner = msg.sender;
emit LogSetOwner(msg.sender);
}
function setOwner(address owner_) public auth {
owner = owner_;
emit LogSetOwner(owner);
}
function setAuthority(DSAuthority authority_) public auth {
authority = authority_;
emit LogSetAuthority(address(authority));
}
modifier auth {
require(isAuthorized(msg.sender, msg.sig));
_;
}
function isAuthorized(address src, bytes4 sig) internal view returns (bool) {
if (src == address(this)) {
return true;
} else if (src == owner) {
return true;
} else if (authority == DSAuthority(0)) {
return false;
} else {
return authority.canCall(src, address(this), sig);
}
}
} contract DSNote {
event LogNote(
bytes4 indexed sig,
address indexed guy,
bytes32 indexed foo,
bytes32 indexed bar,
uint256 wad,
bytes fax
) anonymous;
modifier note {
bytes32 foo;
bytes32 bar;
assembly {
foo := calldataload(4)
bar := calldataload(36)
}
emit LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data);
_;
}
} abstract contract DSProxy is DSAuth, DSNote {
DSProxyCache public cache;
constructor(address _cacheAddr) public {
require(setCache(_cacheAddr));
}
receive() external payable {}
function execute(address _target, bytes memory _data)
public
payable
virtual
returns (bytes32 response);
function setCache(address _cacheAddr) public virtual payable returns (bool);
}
contract DSProxyCache {
mapping(bytes32 => address) cache;
function read(bytes memory _code) public view returns (address) {
bytes32 hash = keccak256(_code);
return cache[hash];
}
function write(bytes memory _code) public returns (address target) {
assembly {
target := create(0, add(_code, 0x20), mload(_code))
switch iszero(extcodesize(target))
case 1 {
revert(0, 0)
}
}
bytes32 hash = keccak256(_code);
cache[hash] = target;
}
} 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;
}
contract MCDSaverProxyHelper is DSMath {
enum ManagerType { MCD, BPROTOCOL }
function normalizeDrawAmount(uint _amount, uint _rate, uint _daiVatBalance) internal pure returns (int dart) {
if (_daiVatBalance < mul(_amount, RAY)) {
dart = toPositiveInt(sub(mul(_amount, RAY), _daiVatBalance) / _rate);
dart = mul(uint(dart), _rate) < mul(_amount, RAY) ? dart + 1 : dart;
}
}
function toRad(uint _wad) internal pure returns (uint) {
return mul(_wad, 10 ** 27);
}
function convertTo18(address _joinAddr, uint256 _amount) internal view returns (uint256) {
return mul(_amount, 10 ** (18 - Join(_joinAddr).dec()));
}
function toPositiveInt(uint _x) internal pure returns (int y) {
y = int(_x);
require(y >= 0, "int-overflow");
}
function normalizePaybackAmount(address _vat, address _urn, bytes32 _ilk) internal view returns (int amount) {
uint dai = Vat(_vat).dai(_urn);
(, uint rate,,,) = Vat(_vat).ilks(_ilk);
(, uint art) = Vat(_vat).urns(_ilk, _urn);
amount = toPositiveInt(dai / rate);
amount = uint(amount) <= art ? - amount : - toPositiveInt(art);
}
function getAllDebt(address _vat, address _usr, address _urn, bytes32 _ilk) internal view returns (uint daiAmount) {
(, uint rate,,,) = Vat(_vat).ilks(_ilk);
(, uint art) = Vat(_vat).urns(_ilk, _urn);
uint dai = Vat(_vat).dai(_usr);
uint rad = sub(mul(art, rate), dai);
daiAmount = rad / RAY;
daiAmount = mul(daiAmount, RAY) < rad ? daiAmount + 1 : daiAmount;
}
function getCollateralAddr(address _joinAddr) internal view returns (address) {
return address(Join(_joinAddr).gem());
}
function isEthJoinAddr(address _joinAddr) internal view returns (bool) {
if (_joinAddr == 0x9759A6Ac90977b93B58547b4A71c78317f391A28) return false;
if (address(Join(_joinAddr).gem()) == 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) {
return true;
}
return false;
}
function getCdpInfo(Manager _manager, uint _cdpId, bytes32 _ilk) public view returns (uint, uint) {
address vat = _manager.vat();
address urn = _manager.urns(_cdpId);
(uint collateral, uint debt) = Vat(vat).urns(_ilk, urn);
(,uint rate,,,) = Vat(vat).ilks(_ilk);
return (collateral, rmul(debt, rate));
}
function getOwner(Manager _manager, uint _cdpId) public view returns (address) {
DSProxy proxy = DSProxy(uint160(_manager.owns(_cdpId)));
return proxy.owner();
}
function getManagerAddr(ManagerType _managerType) public pure returns (address) {
if (_managerType == ManagerType.MCD) {
return 0x5ef30b9986345249bc32d8928B7ee64DE9435E39;
} else if (_managerType == ManagerType.BPROTOCOL) {
return 0x3f30c2381CD8B917Dd96EB2f1A4F96D91324BBed;
}
}
} 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 = 0xBc841B0dE0b93205e912CFBBd1D0c160A1ec6F00;
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 TokenInterface {
address public constant WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
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 ExchangeInterfaceV3 {
function sell(address _srcAddr, address _destAddr, uint _srcAmount, bytes memory _additionalData) external payable returns (uint);
function buy(address _srcAddr, address _destAddr, uint _destAmount, bytes memory _additionalData) external payable returns(uint);
function getSellRate(address _srcAddr, address _destAddr, uint _srcAmount, bytes memory _additionalData) external returns (uint);
function getBuyRate(address _srcAddr, address _destAddr, uint _srcAmount, bytes memory _additionalData) external 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 DFSExchangeData {
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;
address user;
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));
}
}
abstract contract IFeeRecipient {
function getFeeAddr() public view virtual returns (address);
function changeWalletAddr(address _newWallet) public virtual;
} contract DFSExchangeHelper {
string public constant ERR_OFFCHAIN_DATA_INVALID = "Offchain data invalid";
using SafeERC20 for ERC20;
address public constant KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
address public constant EXCHANGE_WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
IFeeRecipient public constant _feeRecipient = IFeeRecipient(0x39C4a92Dc506300c3Ea4c67ca4CA611102ee6F2A);
address public constant DISCOUNT_ADDRESS = 0x1b14E8D511c9A4395425314f849bD737BAF8208F;
address public constant SAVER_EXCHANGE_REGISTRY = 0x25dd3F51e0C3c3Ff164DDC02A8E4D65Bb9cBB12D;
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 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 getFee(uint256 _amount, address _user, address _token, uint256 _dfsFeeDivider) internal returns (uint256 feeAmount) {
if (_dfsFeeDivider != 0 && Discount(DISCOUNT_ADDRESS).isCustomFeeSet(_user)) {
_dfsFeeDivider = Discount(DISCOUNT_ADDRESS).getCustomServiceFee(_user);
}
if (_dfsFeeDivider == 0) {
feeAmount = 0;
} else {
feeAmount = _amount / _dfsFeeDivider;
if (feeAmount > (_amount / 10)) {
feeAmount = _amount / 10;
}
address walletAddr = _feeRecipient.getFeeAddr();
if (_token == KYBER_ETH_ADDRESS) {
payable(walletAddr).transfer(feeAmount);
} else {
ERC20(_token).safeTransfer(walletAddr, feeAmount);
}
}
}
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;
}
function writeUint256(bytes memory _b, uint256 _index, uint _input) internal pure {
if (_b.length < _index + 32) {
revert(ERR_OFFCHAIN_DATA_INVALID);
}
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 ? EXCHANGE_WETH_ADDRESS : _src;
}
} 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];
}
}
abstract contract OffchainWrapperInterface is DFSExchangeData {
function takeOrder(
ExchangeData memory _exData,
ActionType _type
) virtual public payable returns (bool success, uint256);
}
contract DFSExchangeCore is DFSExchangeHelper, DSMath, DFSExchangeData {
string public constant ERR_SLIPPAGE_HIT = "Slippage hit";
string public constant ERR_DEST_AMOUNT_MISSING = "Dest amount missing";
string public constant ERR_WRAPPER_INVALID = "Wrapper invalid";
string public constant ERR_NOT_ZEROX_EXCHANGE = "Zerox exchange invalid";
function _sell(ExchangeData memory exData) internal returns (address, uint) {
address wrapper;
uint swapedTokens;
bool success;
if (exData.srcAddr == KYBER_ETH_ADDRESS) {
exData.srcAddr = ethToWethAddr(exData.srcAddr);
TokenInterface(EXCHANGE_WETH_ADDRESS).deposit{value: exData.srcAmount}();
}
exData.srcAmount -= getFee(exData.srcAmount, exData.user, exData.srcAddr, exData.dfsFeeDivider);
if (exData.offchainData.price > 0) {
(success, swapedTokens) = takeOrder(exData, ActionType.SELL);
if (success) {
wrapper = exData.offchainData.exchangeAddr;
}
}
if (!success) {
swapedTokens = saverSwap(exData, ActionType.SELL);
wrapper = exData.wrapper;
}
if (getBalance(EXCHANGE_WETH_ADDRESS) > 0) {
TokenInterface(EXCHANGE_WETH_ADDRESS).withdraw(
TokenInterface(EXCHANGE_WETH_ADDRESS).balanceOf(address(this))
);
}
if (exData.destAddr == EXCHANGE_WETH_ADDRESS) {
require(getBalance(KYBER_ETH_ADDRESS) >= wmul(exData.minPrice, exData.srcAmount), ERR_SLIPPAGE_HIT);
} else {
require(getBalance(exData.destAddr) >= wmul(exData.minPrice, exData.srcAmount), ERR_SLIPPAGE_HIT);
}
return (wrapper, swapedTokens);
}
function _buy(ExchangeData memory exData) internal returns (address, uint) {
address wrapper;
uint swapedTokens;
bool success;
require(exData.destAmount != 0, ERR_DEST_AMOUNT_MISSING);
exData.srcAmount -= getFee(exData.srcAmount, exData.user, exData.srcAddr, exData.dfsFeeDivider);
if (exData.srcAddr == KYBER_ETH_ADDRESS) {
exData.srcAddr = ethToWethAddr(exData.srcAddr);
TokenInterface(EXCHANGE_WETH_ADDRESS).deposit{value: exData.srcAmount}();
}
if (exData.offchainData.price > 0) {
(success, swapedTokens) = takeOrder(exData, ActionType.BUY);
if (success) {
wrapper = exData.offchainData.exchangeAddr;
}
}
if (!success) {
swapedTokens = saverSwap(exData, ActionType.BUY);
wrapper = exData.wrapper;
}
if (getBalance(EXCHANGE_WETH_ADDRESS) > 0) {
TokenInterface(EXCHANGE_WETH_ADDRESS).withdraw(
TokenInterface(EXCHANGE_WETH_ADDRESS).balanceOf(address(this))
);
}
if (exData.destAddr == EXCHANGE_WETH_ADDRESS) {
require(getBalance(KYBER_ETH_ADDRESS) >= exData.destAmount, ERR_SLIPPAGE_HIT);
} else {
require(getBalance(exData.destAddr) >= exData.destAmount, ERR_SLIPPAGE_HIT);
}
return (wrapper, getBalance(exData.destAddr));
}
function takeOrder(
ExchangeData memory _exData,
ActionType _type
) private returns (bool success, uint256) {
if (!ZrxAllowlist(ZRX_ALLOWLIST_ADDR).isZrxAddr(_exData.offchainData.exchangeAddr)) {
return (false, 0);
}
if (!SaverExchangeRegistry(SAVER_EXCHANGE_REGISTRY).isWrapper(_exData.offchainData.wrapper)) {
return (false, 0);
}
ERC20(_exData.srcAddr).safeTransfer(_exData.offchainData.wrapper, _exData.srcAmount);
return OffchainWrapperInterface(_exData.offchainData.wrapper).takeOrder{value: _exData.offchainData.protocolFee}(_exData, _type);
}
function saverSwap(ExchangeData memory _exData, ActionType _type) internal returns (uint swapedTokens) {
require(SaverExchangeRegistry(SAVER_EXCHANGE_REGISTRY).isWrapper(_exData.wrapper), ERR_WRAPPER_INVALID);
ERC20(_exData.srcAddr).safeTransfer(_exData.wrapper, _exData.srcAmount);
if (_type == ActionType.SELL) {
swapedTokens = ExchangeInterfaceV3(_exData.wrapper).
sell(_exData.srcAddr, _exData.destAddr, _exData.srcAmount, _exData.wrapperData);
} else {
swapedTokens = ExchangeInterfaceV3(_exData.wrapper).
buy(_exData.srcAddr, _exData.destAddr, _exData.destAmount, _exData.wrapperData);
}
}
receive() external virtual payable {}
}
contract MCDSaverProxy is DFSExchangeCore, MCDSaverProxyHelper {
uint256 public constant MANUAL_SERVICE_FEE = 400;
uint256 public constant AUTOMATIC_SERVICE_FEE = 333;
bytes32 public constant ETH_ILK =
0x4554482d41000000000000000000000000000000000000000000000000000000;
address public constant VAT_ADDRESS = 0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B;
address public constant SPOTTER_ADDRESS = 0x65C79fcB50Ca1594B025960e539eD7A9a6D434A3;
address public constant DAI_JOIN_ADDRESS = 0x9759A6Ac90977b93B58547b4A71c78317f391A28;
address public constant JUG_ADDRESS = 0x19c0976f590D67707E62397C87829d896Dc0f1F1;
address public constant DAI_ADDRESS = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address public constant BOT_REGISTRY_ADDRESS = 0x637726f8b08a7ABE3aE3aCaB01A80E2d8ddeF77B;
Vat public constant vat = Vat(VAT_ADDRESS);
DaiJoin public constant daiJoin = DaiJoin(DAI_JOIN_ADDRESS);
Spotter public constant spotter = Spotter(SPOTTER_ADDRESS);
DefisaverLogger public constant logger =
DefisaverLogger(0x5c55B921f590a89C1Ebe84dF170E655a82b62126);
function repay(
ExchangeData memory _exchangeData,
uint256 _cdpId,
uint256 _gasCost,
address _joinAddr,
ManagerType _managerType
) public payable {
address managerAddr = getManagerAddr(_managerType);
address user = getOwner(Manager(managerAddr), _cdpId);
bytes32 ilk = Manager(managerAddr).ilks(_cdpId);
drawCollateral(managerAddr, _cdpId, _joinAddr, _exchangeData.srcAmount, true);
_exchangeData.user = user;
_exchangeData.dfsFeeDivider = isAutomation() ? AUTOMATIC_SERVICE_FEE : MANUAL_SERVICE_FEE;
(, uint256 daiAmount) = _sell(_exchangeData);
daiAmount = sub(daiAmount, takeFee(_gasCost, daiAmount));
paybackDebt(managerAddr, _cdpId, ilk, daiAmount, user);
if (address(this).balance > 0) {
tx.origin.transfer(address(this).balance);
}
logger.Log(
address(this),
msg.sender,
"MCDRepay",
abi.encode(_cdpId, user, _exchangeData.srcAmount, daiAmount)
);
}
function boost(
ExchangeData memory _exchangeData,
uint256 _cdpId,
uint256 _gasCost,
address _joinAddr,
ManagerType _managerType
) public payable {
address managerAddr = getManagerAddr(_managerType);
address user = getOwner(Manager(managerAddr), _cdpId);
bytes32 ilk = Manager(managerAddr).ilks(_cdpId);
uint256 daiDrawn = drawDai(managerAddr, _cdpId, ilk, _exchangeData.srcAmount);
_exchangeData.user = user;
_exchangeData.dfsFeeDivider = isAutomation() ? AUTOMATIC_SERVICE_FEE : MANUAL_SERVICE_FEE;
_exchangeData.srcAmount = sub(daiDrawn, takeFee(_gasCost, daiDrawn));
(, uint256 swapedColl) = _sell(_exchangeData);
addCollateral(managerAddr, _cdpId, _joinAddr, swapedColl);
if (address(this).balance > 0) {
tx.origin.transfer(address(this).balance);
}
logger.Log(
address(this),
msg.sender,
"MCDBoost",
abi.encode(_cdpId, user, _exchangeData.srcAmount, swapedColl)
);
}
function drawDai(
address _managerAddr,
uint256 _cdpId,
bytes32 _ilk,
uint256 _daiAmount
) internal returns (uint256) {
uint256 rate = Jug(JUG_ADDRESS).drip(_ilk);
uint256 daiVatBalance = vat.dai(Manager(_managerAddr).urns(_cdpId));
uint256 maxAmount = getMaxDebt(_managerAddr, _cdpId, _ilk);
if (_daiAmount >= maxAmount) {
_daiAmount = sub(maxAmount, 1);
}
Manager(_managerAddr).frob(
_cdpId,
int256(0),
normalizeDrawAmount(_daiAmount, rate, daiVatBalance)
);
Manager(_managerAddr).move(_cdpId, address(this), toRad(_daiAmount));
if (vat.can(address(this), address(DAI_JOIN_ADDRESS)) == 0) {
vat.hope(DAI_JOIN_ADDRESS);
}
DaiJoin(DAI_JOIN_ADDRESS).exit(address(this), _daiAmount);
return _daiAmount;
}
function addCollateral(
address _managerAddr,
uint256 _cdpId,
address _joinAddr,
uint256 _amount
) internal {
int256 convertAmount = 0;
if (isEthJoinAddr(_joinAddr)) {
Join(_joinAddr).gem().deposit{value: _amount}();
convertAmount = toPositiveInt(_amount);
} else {
convertAmount = toPositiveInt(convertTo18(_joinAddr, _amount));
}
ERC20(address(Join(_joinAddr).gem())).safeApprove(_joinAddr, _amount);
Join(_joinAddr).join(address(this), _amount);
vat.frob(
Manager(_managerAddr).ilks(_cdpId),
Manager(_managerAddr).urns(_cdpId),
address(this),
address(this),
convertAmount,
0
);
}
function drawCollateral(
address _managerAddr,
uint256 _cdpId,
address _joinAddr,
uint256 _amount,
bool _toEth
) internal returns (uint256) {
uint256 frobAmount = _amount;
uint256 tokenDecimal = Join(_joinAddr).dec();
require(tokenDecimal <= 18, "Token decimals too big");
if (tokenDecimal != 18) {
frobAmount = _amount * (10**(18 - tokenDecimal));
}
Manager(_managerAddr).frob(_cdpId, -toPositiveInt(frobAmount), 0);
Manager(_managerAddr).flux(_cdpId, address(this), frobAmount);
Join(_joinAddr).exit(address(this), _amount);
if (isEthJoinAddr(_joinAddr) && _toEth) {
Join(_joinAddr).gem().withdraw(_amount);
}
return _amount;
}
function paybackDebt(
address _managerAddr,
uint256 _cdpId,
bytes32 _ilk,
uint256 _daiAmount,
address _owner
) internal {
address urn = Manager(_managerAddr).urns(_cdpId);
uint256 wholeDebt = getAllDebt(VAT_ADDRESS, urn, urn, _ilk);
if (_daiAmount > wholeDebt) {
ERC20(DAI_ADDRESS).transfer(_owner, sub(_daiAmount, wholeDebt));
_daiAmount = wholeDebt;
}
if (ERC20(DAI_ADDRESS).allowance(address(this), DAI_JOIN_ADDRESS) == 0) {
ERC20(DAI_ADDRESS).approve(DAI_JOIN_ADDRESS, uint256(-1));
}
daiJoin.join(urn, _daiAmount);
Manager(_managerAddr).frob(_cdpId, 0, normalizePaybackAmount(VAT_ADDRESS, urn, _ilk));
}
function getMaxCollateral(
address _managerAddr,
uint256 _cdpId,
bytes32 _ilk,
address _joinAddr
) public view returns (uint256) {
uint256 price = getPrice(_ilk);
(uint256 collateral, uint256 debt) = getCdpInfo(Manager(_managerAddr), _cdpId, _ilk);
(, uint256 mat) = Spotter(SPOTTER_ADDRESS).ilks(_ilk);
uint256 maxCollateral = sub(collateral, (div(mul(mat, debt), price)));
uint256 tokenDecimal = Join(_joinAddr).dec();
require(tokenDecimal <= 18, "Token decimals too big");
uint256 normalizeMaxCollateral = maxCollateral / (10**(18 - tokenDecimal));
return (normalizeMaxCollateral * 99) / 100;
}
function getMaxDebt(
address _managerAddr,
uint256 _cdpId,
bytes32 _ilk
) public view virtual returns (uint256) {
uint256 price = getPrice(_ilk);
(, uint256 mat) = spotter.ilks(_ilk);
(uint256 collateral, uint256 debt) = getCdpInfo(Manager(_managerAddr), _cdpId, _ilk);
return sub(sub(div(mul(collateral, price), mat), debt), 10);
}
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);
}
function isAutomation() internal view returns (bool) {
return BotRegistry(BOT_REGISTRY_ADDRESS).botList(tx.origin);
}
function takeFee(uint256 _gasCost, uint256 _amount) internal returns (uint256) {
if (_gasCost > 0) {
uint256 ethDaiPrice = getPrice(ETH_ILK);
uint256 feeAmount = rmul(_gasCost, ethDaiPrice);
if (feeAmount > _amount / 5) {
feeAmount = _amount / 5;
}
address walletAddr = _feeRecipient.getFeeAddr();
ERC20(DAI_ADDRESS).safeTransfer(walletAddr, feeAmount);
return feeAmount;
}
return 0;
}
}
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;
}
contract MCDSaverFlashLoan is MCDSaverProxy, AdminAuth {
address internal constant AAVE_MARKET_ADDR = 0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5;
address internal constant BALANCER_VAULT_ADDR = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;
struct SaverData {
uint256 cdpId;
uint256 gasCost;
uint256 loanAmount;
uint256 fee;
address joinAddr;
ManagerType managerType;
}
function receiveFlashLoan(
address[] memory _tokens,
uint256[] memory _amounts,
uint256[] memory _fees,
bytes memory _params
) external {
require(msg.sender == BALANCER_VAULT_ADDR, "Only balancer can call");
_receiveFl(_tokens[0], _amounts[0], _fees[0], _params);
ERC20(_tokens[0]).safeTransfer(address(BALANCER_VAULT_ADDR), add(_amounts[0], _fees[0]));
}
function executeOperation(
address[] memory _assets,
uint256[] memory _amounts,
uint256[] memory _fees,
address,
bytes memory _params
) public returns (bool) {
address lendingPool = ILendingPoolAddressesProviderV2(AAVE_MARKET_ADDR).getLendingPool();
require(msg.sender == lendingPool, "Only aave pool can call");
_receiveFl(_assets[0], _amounts[0], _fees[0], _params);
ERC20(_assets[0]).safeApprove(lendingPool, add(_amounts[0], _fees[0]));
return true;
}
function _receiveFl(
address _tokenAddr,
uint256 _amount,
uint256 _fee,
bytes memory _params
) internal {
(
bytes memory exDataBytes,
uint256 cdpId,
uint256 gasCost,
address joinAddr,
bool isRepay,
uint8 managerType
) = abi.decode(_params, (bytes, uint256, uint256, address, bool, uint8));
ExchangeData memory exchangeData = unpackExchangeData(exDataBytes);
if (_tokenAddr == EXCHANGE_WETH_ADDRESS) {
TokenInterface(EXCHANGE_WETH_ADDRESS).withdraw(_amount);
}
SaverData memory saverData = SaverData({
cdpId: cdpId,
gasCost: gasCost,
loanAmount: _amount,
fee: _fee,
joinAddr: joinAddr,
managerType: ManagerType(managerType)
});
if (isRepay) {
repayWithLoan(exchangeData, saverData);
} else {
boostWithLoan(exchangeData, saverData);
}
if (address(this).balance > 0) {
tx.origin.transfer(address(this).balance);
}
}
function boostWithLoan(ExchangeData memory _exchangeData, SaverData memory _saverData)
internal
{
address managerAddr = getManagerAddr(_saverData.managerType);
address user = getOwner(Manager(managerAddr), _saverData.cdpId);
uint256 maxDebt = getMaxDebt(
managerAddr,
_saverData.cdpId,
Manager(managerAddr).ilks(_saverData.cdpId)
);
uint256 daiDrawn = drawDai(
managerAddr,
_saverData.cdpId,
Manager(managerAddr).ilks(_saverData.cdpId),
maxDebt
);
_exchangeData.srcAmount = sub(
add(daiDrawn, _saverData.loanAmount),
takeFee(_saverData.gasCost, add(daiDrawn, _saverData.loanAmount))
);
_exchangeData.user = user;
_exchangeData.dfsFeeDivider = isAutomation() ? AUTOMATIC_SERVICE_FEE : MANUAL_SERVICE_FEE;
(, uint256 swapedAmount) = _sell(_exchangeData);
addCollateral(managerAddr, _saverData.cdpId, _saverData.joinAddr, swapedAmount);
drawDai(
managerAddr,
_saverData.cdpId,
Manager(managerAddr).ilks(_saverData.cdpId),
add(_saverData.loanAmount, _saverData.fee)
);
logger.Log(
address(this),
msg.sender,
"MCDFlashBoost",
abi.encode(_saverData.cdpId, user, _exchangeData.srcAmount, swapedAmount)
);
}
function repayWithLoan(ExchangeData memory _exchangeData, SaverData memory _saverData)
internal
{
address managerAddr = getManagerAddr(_saverData.managerType);
address user = getOwner(Manager(managerAddr), _saverData.cdpId);
bytes32 ilk = Manager(managerAddr).ilks(_saverData.cdpId);
uint256 maxColl = getMaxCollateral(managerAddr, _saverData.cdpId, ilk, _saverData.joinAddr);
uint256 collDrawn = drawCollateral(
managerAddr,
_saverData.cdpId,
_saverData.joinAddr,
maxColl,
true
);
_exchangeData.srcAmount = add(_saverData.loanAmount, collDrawn);
_exchangeData.user = user;
_exchangeData.dfsFeeDivider = isAutomation() ? AUTOMATIC_SERVICE_FEE : MANUAL_SERVICE_FEE;
(, uint256 paybackAmount) = _sell(_exchangeData);
paybackAmount = sub(paybackAmount, takeFee(_saverData.gasCost, paybackAmount));
paybackDebt(managerAddr, _saverData.cdpId, ilk, paybackAmount, user);
drawCollateral(
managerAddr,
_saverData.cdpId,
_saverData.joinAddr,
add(_saverData.loanAmount, _saverData.fee),
false
);
logger.Log(
address(this),
msg.sender,
"MCDFlashRepay",
abi.encode(_saverData.cdpId, user, _exchangeData.srcAmount, paybackAmount)
);
}
receive() external payable override(DFSExchangeCore) {}
}