pragma solidity ^0.5.7; interface RegistryInterface { function proxies(address) external view returns (address); } interface UserWalletInterface { function owner() external view returns (address); } interface CTokenInterface { function borrow(uint borrowAmount) external returns (uint); function transfer(address, uint) external returns (bool); function repayBorrow(uint repayAmount) external returns (uint); function underlying() external view returns (address); function borrowBalanceCurrent(address account) external returns (uint); } interface CETHInterface { function balanceOf(address) external view returns (uint); function mint() external payable; // For ETH function repayBorrow() external payable; // For ETH function borrowBalanceCurrent(address account) external returns (uint); function redeem(uint redeemAmount) external returns (uint); } interface ComptrollerInterface { function enterMarkets(address[] calldata cTokens) external returns (uint[] memory); function exitMarket(address cTokenAddress) external returns (uint); } interface TubInterface { function open() external returns (bytes32); function join(uint) external; function exit(uint) external; function lock(bytes32, uint) external; function free(bytes32, uint) external; function draw(bytes32, uint) external; function wipe(bytes32, uint) external; function give(bytes32, address) external; function shut(bytes32) external; function cups(bytes32) external view returns (address, uint, uint, uint); function gem() external view returns (TokenInterface); function gov() external view returns (TokenInterface); function skr() external view returns (TokenInterface); function sai() external view returns (TokenInterface); function ink(bytes32) external view returns (uint); function tab(bytes32) external returns (uint); function rap(bytes32) external returns (uint); function per() external view returns (uint); function pep() external view returns (PepInterface); } interface TokenInterface { function allowance(address, address) external view returns (uint); function balanceOf(address) external view returns (uint); function approve(address, uint) external; function transfer(address, uint) external returns (bool); function deposit() external payable; function withdraw(uint) external; } interface PepInterface { function peek() external returns (bytes32, bool); } contract DSMath { function add(uint x, uint y) internal pure returns (uint z) { require((z = x + y) >= x, "math-not-safe"); } function mul(uint x, uint y) internal pure returns (uint z) { require(y == 0 || (z = x * y) / y == x, "math-not-safe"); } uint constant WAD = 10 ** 18; uint constant RAY = 10 ** 27; function rmul(uint x, uint y) internal pure returns (uint z) { z = add(mul(x, y), RAY / 2) / RAY; } function rdiv(uint x, uint y) internal pure returns (uint z) { z = add(mul(x, RAY), y / 2) / y; } function wdiv(uint x, uint y) internal pure returns (uint z) { z = add(mul(x, WAD), y / 2) / y; } } contract Helpers is DSMath { address public registry = 0x498b3BfaBE9F73db90D252bCD4Fa9548Cd0Fd981; address public comptrollerAddr = 0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B; address public saiTubAddress = 0x448a5065aeBB8E423F0896E6c5D525C040f59af3; address payable public controllerOne = 0x0f0EBD0d7672362D11e0b6d219abA30b0588954E; address payable public controllerTwo = 0xd8db02A498E9AFbf4A32BC006DC1940495b4e592; address public usdcAddr = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; address public cEth = 0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5; address public cDai = 0xF5DCe57282A584D2746FaF1593d3121Fcac444dC; address public cUsdc = 0x39AA39c021dfbaE8faC545936693aC917d5E7563; bytes32 public CDPID; /** * FOR SECURITY PURPOSE * only InstaDApp smart wallets can access the liquidity pool contract */ modifier isUserWallet { address userAdd = UserWalletInterface(msg.sender).owner(); address walletAdd = RegistryInterface(registry).proxies(userAdd); require(walletAdd != address(0), "not-user-wallet"); require(walletAdd == msg.sender, "not-wallet-owner"); _; } } contract CompoundResolver is Helpers { function mintAndBorrow(address[] memory cErc20, uint[] memory tknAmt) internal { if (cErc20.length > 0) { CETHInterface(cEth).mint.value(address(this).balance)(); for (uint i = 0; i < cErc20.length; i++) { if (cErc20[i] != address(0)) { if (tknAmt[i] > 0) { CTokenInterface ctknContract = CTokenInterface(cErc20[i]); if (cErc20[i] != cEth) { address tknAddr = ctknContract.underlying(); assert(ctknContract.borrow(tknAmt[i]) == 0); assert(TokenInterface(tknAddr).transfer(msg.sender, tknAmt[i])); } else { assert(ctknContract.borrow(tknAmt[i]) == 0); msg.sender.transfer(tknAmt[i]); } } } } } } function paybackAndWithdraw(address[] memory cErc20) internal { if (cErc20.length > 0) { CETHInterface cethContract = CETHInterface(cEth); for (uint i = 0; i < cErc20.length; i++) { if (cErc20[i] != address(0)) { CTokenInterface ctknContract = CTokenInterface(cErc20[i]); uint tknBorrowed = ctknContract.borrowBalanceCurrent(address(this)); if (tknBorrowed > 0) { if (cErc20[i] != cEth) { assert(ctknContract.repayBorrow(tknBorrowed) == 0); } else { cethContract.repayBorrow.value(tknBorrowed)(); } } assert(ctknContract.borrowBalanceCurrent(address(this)) == 0); } } uint ethSupplied = cethContract.balanceOf(address(this)); assert(cethContract.redeem(ethSupplied) == 0); } } } contract MakerResolver is CompoundResolver { function lockAndDraw(uint _wad) internal { if (_wad > 0) { uint ethToSupply = address(this).balance; bytes32 cup = CDPID; address tubAddr = saiTubAddress; TubInterface tub = TubInterface(tubAddr); TokenInterface weth = tub.gem(); (address lad,,,) = tub.cups(cup); require(lad == address(this), "cup-not-owned"); weth.deposit.value(ethToSupply)(); uint ink = rdiv(ethToSupply, tub.per()); ink = rmul(ink, tub.per()) <= ethToSupply ? ink : ink - 1; tub.join(ink); tub.lock(cup, ink); tub.draw(cup, _wad); tub.sai().transfer(msg.sender, _wad); } } function wipeAndFree() internal { TubInterface tub = TubInterface(saiTubAddress); TokenInterface weth = tub.gem(); bytes32 cup = CDPID; uint _wad = tub.tab(cup); if (_wad > 0) { (address lad,,,) = tub.cups(cup); require(lad == address(this), "cup-not-owned"); tub.wipe(cup, _wad); // free ETH uint _jam = rmul(tub.ink(cup), tub.per()); uint ink = rdiv(_jam, tub.per()); ink = rmul(ink, tub.per()) <= _jam ? ink : ink - 1; tub.free(cup, ink); tub.exit(ink); uint freeJam = weth.balanceOf(address(this)); // withdraw possible previous stuck WETH as well weth.withdraw(freeJam); } } } contract ProvideLiquidity is MakerResolver { /** * @dev user => ETH deposits */ mapping (address => uint) public deposits; event LogDepositETH(address user, uint amt); event LogWithdrawETH(address user, uint amt); /** * @dev deposit ETH */ function depositETH() external payable { deposits[msg.sender] += msg.value; emit LogDepositETH(msg.sender, msg.value); } /** * @dev withdraw ETH */ function withdrawETH(uint amount) external returns (uint withdrawAmt) { require(deposits[msg.sender] > 0, "no-balance"); withdrawAmt = amount < deposits[msg.sender] ? amount : deposits[msg.sender]; msg.sender.transfer(withdrawAmt); deposits[msg.sender] -= withdrawAmt; emit LogWithdrawETH(msg.sender, withdrawAmt); } } contract AccessLiquidity is ProvideLiquidity { event LogLiquidityBorrow(address user, address[] ctknAddr, uint[] amount , bool isCompound); event LogLiquidityPayback(address user, address[] ctknAddr, bool isCompound); /** * @dev borrow token and use them on InstaDApp's contract wallets */ function accessToken(address[] calldata ctknAddr, uint[] calldata tknAmt, bool isCompound) external isUserWallet { if (isCompound) { mintAndBorrow(ctknAddr, tknAmt); } else { lockAndDraw(tknAmt[0]); } emit LogLiquidityBorrow( msg.sender, ctknAddr, tknAmt, isCompound ); } /** * @dev Payback borrowed token and from InstaDApp's contract wallets */ function paybackToken(address[] calldata ctknAddr, bool isCompound) external payable isUserWallet { if (isCompound) { paybackAndWithdraw(ctknAddr); } else { wipeAndFree(); } emit LogLiquidityPayback(msg.sender, ctknAddr, isCompound); } } contract Controllers is AccessLiquidity { modifier isController { require(msg.sender == controllerOne || msg.sender == controllerTwo, "not-controller"); _; } /** * give approval to other addresses */ function setApproval(address erc20, address to) external isController { TokenInterface(erc20).approve(to, uint(-1)); } /** * enter compound market to enable borrowing */ function enterMarket(address[] calldata cTknAddrArr) external isController { ComptrollerInterface troller = ComptrollerInterface(comptrollerAddr); troller.enterMarkets(cTknAddrArr); } /** * exit compound market to disable borrowing */ function exitMarket(address cErc20) external isController { ComptrollerInterface troller = ComptrollerInterface(comptrollerAddr); troller.exitMarket(cErc20); } } contract InstaPool is Controllers { constructor() public { TubInterface tub = TubInterface(saiTubAddress); CDPID = tub.open(); // new SCD CDP TokenInterface weth = tub.gem(); TokenInterface peth = tub.skr(); TokenInterface dai = tub.sai(); TokenInterface mkr = tub.gov(); weth.approve(saiTubAddress, uint(-1)); peth.approve(saiTubAddress, uint(-1)); dai.approve(saiTubAddress, uint(-1)); mkr.approve(saiTubAddress, uint(-1)); dai.approve(cDai, uint(-1)); TokenInterface(usdcAddr).approve(cUsdc, uint(-1)); TokenInterface(cDai).approve(cDai, uint(-1)); TokenInterface(cUsdc).approve(cUsdc, uint(-1)); TokenInterface(cEth).approve(cEth, uint(-1)); address[] memory enterMarketArr = new address[](3); enterMarketArr[0] = cEth; enterMarketArr[1] = cDai; enterMarketArr[2] = cUsdc; ComptrollerInterface(comptrollerAddr).enterMarkets(enterMarketArr); } function() external payable {} }