Flash loans are instantaneous and unsecured DeFi lending solutions, enabled by smart contract code that reverses a lending transaction if the borrower is unable to fulfill borrowing obligations. Their use cases cover arbitrage, liquidation and so on.
Flash loan borrowers on CHFRY utilize Flash Fryer reserves for flash loan operations, in the same vein as Aave’s offering, and enhance their yields with CHEESE rewards.
In order to use CHFRY flash loans follow the standard proposal EIP-3165.
Flash loan fee
The flash loan fee is currently 0.06%, changeable via the normal governance process. To get the current value, call getConfigValue("FRYER_FLASH_FEE_PROPORTION") on the FryerConfig.sol contract.
Example
For developers, a helpful mental model to consider when developing your solution:
1/ Approve the specific Fryer contract amount and the flashloan fee; fee can be calculated by Fryer's flashFee().
2/ Your contract calls the Fryer contract, requesting a Flash Loan of a certain amount of reserves using flashLoan().
3/ After some sanity checks, the Fryer transfers the requested amount of the reserves to your contract, then calls onFlashLoan() on your contract.
4/ Your contract, now holding the flash loaned amount, executes any arbitrary operation in its code. Keep at least amount + fee in your contract.
5/ Finally, Fryer will transfer amount + fee from your contract. All of the above happens in one transaction (hence in a single ethereum block).
FlashBorrower.sol
//SPDX-License-Identifier: MITpragma solidity >=0.6.5<0.8.0;import'@openzeppelin/contracts/token/ERC20/IERC20.sol';import'./interfaces/IERC3156FlashBorrower.sol';import'./interfaces/IERC3156FlashLender.sol';import'./libraries/TransferHelper.sol';// FlashLoan DEMOcontract FlashBorrower2 is IERC3156FlashBorrower {enumAction { NORMAL, STEAL, REENTER }usingTransferHelper for address; IERC3156FlashLender lender; uint256 public flashBalance; address public flashInitiator; address public flashToken; uint256 public flashAmount; uint256 public flashFee; address public admin;constructor(address lender_) public { admin =msg.sender; lender =IERC3156FlashLender(lender_); }functionsetLender(address _lender) external {require(msg.sender == admin,'!admin'); lender =IERC3156FlashLender(_lender); }/// @dev ERC-3156 Flash loan callbackfunctiononFlashLoan( address initiator, address token, uint256 amount, uint256 fee, bytes calldata data ) externaloverridereturns (bytes32) {require(msg.sender ==address(lender),'FlashBorrower: Untrusted lender');require(initiator ==address(this),'FlashBorrower: External loan initiator'); Action action =abi.decode(data, (Action)); // Use this to unpack arbitrary data flashInitiator = initiator; flashToken = token; flashAmount = amount; flashFee = fee;if (action ==Action.NORMAL) { flashBalance =IERC20(token).balanceOf(address(this)); } elseif (action ==Action.STEAL) {// do nothing } elseif (action ==Action.REENTER) {flashBorrow(token, amount *2); }returnkeccak256('ERC3156FlashBorrower.onFlashLoan'); }functionflashBorrow(address token, uint256 amount) public {// Use this to pack arbitrary data to `onFlashLoan` bytes memory data =abi.encode(Action.NORMAL);approveRepayment(token, amount);lender.flashLoan(this, token, amount, data); }functionflashBorrowAndSteal(address token, uint256 amount) public {// Use this to pack arbitrary data to `onFlashLoan` bytes memory data =abi.encode(Action.STEAL);lender.flashLoan(this, token, amount, data); }functionflashBorrowAndReenter(address token, uint256 amount) public {// Use this to pack arbitrary data to `onFlashLoan` bytes memory data =abi.encode(Action.REENTER);approveRepayment(token, amount);lender.flashLoan(this, token, amount, data); }functionapproveRepayment(address token, uint256 amount) public { uint256 _allowance =IERC20(token).allowance(address(this),address(lender)); uint256 _fee =lender.flashFee(token, amount); uint256 _repayment = amount + _fee;token.safeApprove(address(lender),0);token.safeApprove(address(lender), _allowance + _repayment); }functiontransferFromAdmin( address _token, address _receiver, uint256 _amount ) external {require(msg.sender == admin,'!admin');_token.safeTransfer(_receiver, _amount); }}
IERC3156FlashBorrower.sol
// SPDX-License-Identifier: MITpragma solidity >=0.6.0<=0.8.0;interfaceIERC3156FlashBorrower {/** * @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" */functiononFlashLoan( address initiator, address token, uint256 amount, uint256 fee, bytes calldata data ) externalreturns (bytes32);}
IERC3156FlashLender.sol
// SPDX-License-Identifier: MITpragma solidity >=0.6.0<=0.8.0;import"./IERC3156FlashBorrower.sol";interfaceIERC3156FlashLender {/** * @dev The amount of currency available to be lended. * @param token The loan currency. * @return The amount of `token` that can be borrowed. */functionmaxFlashLoan( address token ) externalviewreturns (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. */functionflashFee( address token, uint256 amount ) externalviewreturns (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. */functionflashLoan( IERC3156FlashBorrower receiver, address token, uint256 amount, bytes calldata data ) externalreturns (bool);}