How to use

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: MIT
pragma 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 DEMO
contract FlashBorrower2 is IERC3156FlashBorrower {
	enum Action {
		NORMAL,
		STEAL,
		REENTER
	}

  using TransferHelper 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_);
	}

	function setLender(address _lender) external {
		require(msg.sender == admin, '!admin');
		lender = IERC3156FlashLender(_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: 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));
		} else if (action == Action.STEAL) {
			// do nothing
		} else if (action == Action.REENTER) {
			flashBorrow(token, amount * 2);
		}
		return keccak256('ERC3156FlashBorrower.onFlashLoan');
	}

	function flashBorrow(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);
	}

	function flashBorrowAndSteal(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);
	}

	function flashBorrowAndReenter(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);
	}

	function approveRepayment(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);
	}

	function transferFromAdmin(
		address _token,
		address _receiver,
		uint256 _amount
	) external {
		require(msg.sender == admin, '!admin');
		_token.safeTransfer(_receiver, _amount);
	}
}

IERC3156FlashBorrower.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <=0.8.0;


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

IERC3156FlashLender.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <=0.8.0;
import "./IERC3156FlashBorrower.sol";


interface IERC3156FlashLender {

    /**
     * @dev The amount of currency available to be lended.
     * @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);
}

TransferHelper.sol

//SPDX-License-Identifier: MIT
pragma solidity >=0.6.5 <0.8.0;

library TransferHelper {
    function safeApprove(address token, address to, uint value) internal {
        // bytes4(keccak256(bytes('approve(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: APPROVE_FAILED');
    }

    function safeTransfer(address token, address to, uint value) internal {
        // bytes4(keccak256(bytes('transfer(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FAILED');
    }

    function safeTransferFrom(address token, address from, address to, uint value) internal {
        // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FROM_FAILED');
    }

    function safeTransferETH(address to, uint value) internal {
        (bool success,) = to.call{value:value}(new bytes(0));
        require(success, 'TransferHelper: ETH_TRANSFER_FAILED');
    }
}

Last updated