//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");
}
}