//Code written for SamPriestley.com pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; //The code written by money-legos to make flash loans easier import "../Contract/DydxFlashLoanBase.sol"; import "../Contract/Interfaces/ICallee.sol"; //Interfaces to call outside contracts interface IErc20 { function approve(address, uint256) external returns (bool); function transfer(address, uint256) external returns (bool); function balanceOf(address) external view returns (uint); } interface ICErc20 is IErc20{ function mint(uint mintAmount) external returns (uint); function borrow(uint borrowAmount) external returns (uint); function underlying() external returns (address); } interface IComptroller{ function enterMarkets(address[] calldata cTokens) external returns (uint[] memory); } contract CompFarmer is DydxFlashloanBase, ICallee { address public immutable contractOwner; //Addresses of outside currencies address private constant SOLO = 0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e; address private constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; address private constant CDAI = 0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643; address private constant COMPTROLLER = 0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B; constructor() { contractOwner = msg.sender; } //Withdraw needed or else your Erc20 tokens stuck here forever function Withdraw( address _currency, uint256 _amount ) public { require(msg.sender == contractOwner, "OWNER ONLY"); IErc20 currency = IErc20(_currency); currency.approve(contractOwner, _amount); currency.transfer(contractOwner, _amount); } //The function we call function Invest() public { //Only the creator of the contract can interact require(msg.sender == contractOwner, "OWNER ONLY"); //For outside calls to DAI smart contract IErc20 dai = IErc20(DAI); //Check we have some dai to invest uint256 daiBalance = dai.balanceOf(address(this)); require(daiBalance >0, "NO DAI TO INVEST"); //We do not use decimals. So *2900/1000 is same as *2.9 //In production we would use SafeMath library uint256 borrowAmount = daiBalance * 2900/1000; ISoloMargin solo = ISoloMargin(SOLO); // Get marketId from token address uint256 marketId = _getMarketIdFromTokenAddress(SOLO, DAI); // Calculate repay amount (borrowAmount + (2 wei)) // Approve transfer from uint256 repayAmount = _getRepaymentAmountInternal(borrowAmount); dai.approve(SOLO, repayAmount); // 1. Withdraw $ // 2. Call callFunction(...) // 3. Deposit back $ Actions.ActionArgs[] memory operations = new Actions.ActionArgs[](3); //We encode data we want our callFunction to receive bytes memory data = abi.encode(repayAmount); //Borrow money operations[0] = _getWithdrawAction(marketId, borrowAmount); //Do our Compound investment operations[1] = _getCallAction(data); //Repay money operations[2] = _getDepositAction(marketId, repayAmount); Account.Info[] memory accountInfos = new Account.Info[](1); accountInfos[0] = _getAccountInfo(); solo.operate(accountInfos, operations); } //This contract is called by the flash loan lender. It is our main logic function callFunction( address sender, Account.Info memory account, bytes memory data ) public override { //We check origin of total transaction is owner. Cannot just check sender require(tx.origin == contractOwner, "OWNER ONLY"); (uint256 repayAmount) = abi.decode(data,(uint256)); ICErc20 cdai = ICErc20(CDAI); IErc20 dai = IErc20(DAI); IComptroller comptroller = IComptroller(COMPTROLLER); uint256 lendAmount = dai.balanceOf(address(this)); //Approve using DAI as collateral on Compound address[] memory markets = new address[](1); markets[0] = CDAI; comptroller.enterMarkets(markets); //Approve transfer of DAI to CDAI dai.approve(CDAI, lendAmount); //Lend out DAI cdai.mint(lendAmount); //Borrow enough DAI to repay loan cdai.borrow(repayAmount); require(dai.balanceOf(address(this)) > repayAmount, "CANT REPAY LOAN"); } }
0.7.0