pragma solidity ^0.6.12; interface VatAbstract { function wards(address) external view returns (uint256); function rely(address) external; function deny(address) external; function can(address, address) external view returns (uint256); function hope(address) external; function nope(address) external; function ilks(bytes32) external view returns (uint256, uint256, uint256, uint256, uint256); function urns(bytes32, address) external view returns (uint256, uint256); function gem(bytes32, address) external view returns (uint256); function dai(address) external view returns (uint256); function sin(address) external view returns (uint256); function debt() external view returns (uint256); function vice() external view returns (uint256); function Line() external view returns (uint256); function live() external view returns (uint256); function init(bytes32) external; function file(bytes32, uint256) external; function file(bytes32, bytes32, uint256) external; function cage() external; function slip(bytes32, address, int256) external; function flux(bytes32, address, address, uint256) external; function move(address, address, uint256) external; function frob(bytes32, address, address, address, int256, int256) external; function fork(bytes32, address, address, int256, int256) external; function grab(bytes32, address, address, address, int256, int256) external; function heal(uint256) external; function suck(address, address, uint256) external; function fold(bytes32, address, int256) external; } interface IERC20 { function totalSupply() external view returns (uint256); function balanceOf(address) external view returns (uint256); function allowance(address, address) external view returns (uint256); function approve(address, uint256) external returns (bool); function transfer(address, uint256) external returns (bool); function transferFrom(address, address, uint256) external returns (bool); } interface IERC3156FlashBorrower { /** * @dev Receive a flash loan. * @param initiator The initiator of the loan. * @param token The loan currency. * @param amount The amount of tokens lent. * @param fee The additional amount of tokens to repay. * @param data Arbitrary data structure, intended to contain user-defined parameters. * @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan" */ function onFlashLoan( address initiator, address token, uint256 amount, uint256 fee, bytes calldata data ) external returns (bytes32); } interface IERC3156FlashLender { /** * @dev The amount of currency available to be lent. * @param token The loan currency. * @return The amount of `token` that can be borrowed. */ function maxFlashLoan( address token ) external view returns (uint256); /** * @dev The fee to be charged for a given loan. * @param token The loan currency. * @param amount The amount of tokens lent. * @return The amount of `token` to be charged for the loan, on top of the returned principal. */ function flashFee( address token, uint256 amount ) external view returns (uint256); /** * @dev Initiate a flash loan. * @param receiver The receiver of the tokens in the loan, and the receiver of the callback. * @param token The loan currency. * @param amount The amount of tokens lent. * @param data Arbitrary data structure, intended to contain user-defined parameters. */ function flashLoan( IERC3156FlashBorrower receiver, address token, uint256 amount, bytes calldata data ) external returns (bool); } interface IVatDaiFlashBorrower { /** * @dev Receive a flash loan. * @param initiator The initiator of the loan. * @param amount The amount of tokens lent. [rad] * @param fee The additional amount of tokens to repay. [rad] * @param data Arbitrary data structure, intended to contain user-defined parameters. * @return The keccak256 hash of "IVatDaiFlashLoanReceiver.onVatDaiFlashLoan" */ function onVatDaiFlashLoan( address initiator, uint256 amount, uint256 fee, bytes calldata data ) external returns (bytes32); } interface IVatDaiFlashLender { /** * @dev Initiate a flash loan. * @param receiver The receiver of the tokens in the loan, and the receiver of the callback. * @param amount The amount of tokens lent. [rad] * @param data Arbitrary data structure, intended to contain user-defined parameters. */ function vatDaiFlashLoan( IVatDaiFlashBorrower receiver, uint256 amount, bytes calldata data ) external returns (bool); } contract FlashBorrower is IVatDaiFlashBorrower { // 0x68C7b18c987e7Fea919e02894614fD4FA0d58B96 enum Action {NORMAL, OTHER} VatAbstract vat; IVatDaiFlashLender lender; constructor ( VatAbstract vat_, // 0xbA987bDB501d131f766fEe8180Da5d81b34b69d9 IVatDaiFlashLender lender_ // 0x5aA1323f61D679E52a90120DFDA2ed1A76E4475A ) public { vat = vat_; lender = lender_; } /// @dev Vat Dai Flash loan callback function onVatDaiFlashLoan( address initiator, uint256 amount, uint256 fee, bytes calldata data ) external override returns (bytes32) { require( msg.sender == address(lender), "FlashBorrower: Untrusted lender" ); require( initiator == address(this), "FlashBorrower: Untrusted loan initiator" ); (Action action) = abi.decode(data, (Action)); if (action == Action.NORMAL) { require(vat.dai(address(this)) >= amount); // that's it, repay on 176 } else if (action == Action.OTHER) { // do another } // Repay the loan amount + fee // Be sure not to overpay as there are no safety guards for this vat.move(address(this), address(lender), amount + fee); return keccak256("VatDaiFlashBorrower.onVatDaiFlashLoan"); } /// @dev Initiate a flash loan function vatDaiFlashBorrow( uint256 amount ) public { bytes memory data = abi.encode(Action.NORMAL); lender.vatDaiFlashLoan(this, amount, data); } } /** contract FlashBorrower is IERC3156FlashBorrower { enum Action {NORMAL, OTHER} IERC3156FlashLender lender; constructor ( IERC3156FlashLender lender_ // 0x5aA1323f61D679E52a90120DFDA2ed1A76E4475A ) public { lender = lender_; } /// @dev ERC-3156 Flash loan callback function onFlashLoan( address initiator, address token, uint256 amount, uint256 fee, bytes calldata data ) external override returns (bytes32) { require( msg.sender == address(lender), "FlashBorrower: Untrusted lender" ); require( initiator == address(this), "FlashBorrower: Untrusted loan initiator" ); (Action action) = abi.decode(data, (Action)); if (action == Action.NORMAL) { require(IERC20(token).balanceOf(address(this)) >= amount); // make a profitable trade here IERC20(token).transfer(initiator, amount + fee); } else if (action == Action.OTHER) { // do another } return keccak256("ERC3156FlashBorrower.onFlashLoan"); } /// @dev Initiate a flash loan function flashBorrow( address token, // Dai 0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa uint256 amount // wad ) public { bytes memory data = abi.encode(Action.NORMAL); uint256 _allowance = IERC20(token).allowance(address(this), address(lender)); uint256 _fee = lender.flashFee(token, amount); uint256 _repayment = amount + _fee; IERC20(token).approve(address(lender), _allowance + _repayment); lender.flashLoan(this, token, amount, data); } } */