Implementing ERC3156
https://eips.ethereum.org/EIPS/eip-3156
A flash lending feature integrates two smart contracts using a callback pattern. These are called the LENDER and the RECEIVER.
A flashloan lender must implement the IERC3156FlashLender interface. Hence our vault contract will inherit IERC3156FlashLender
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "lib/yield-utils-v2/contracts/mocks/ERC20Mock.sol";
import "lib/openzeppelin-contracts/contracts/access/Ownable.sol";
import "lib/yield-utils-v2/contracts/token/IERC20.sol";
import "src/IERC3156FlashLender.sol";
import "src/IERC3156FlashBorrower.sol";
/**
@title Flash Loan Server
@author Calnix
@dev Contract allows users to exchange a pre-specified ERC20 token for some other wrapped ERC20 tokens.
@notice Wrapped tokens will be burned, when user withdraws their deposited tokens.
*/
contract FlashLoanVault is ERC20Mock, IERC3156FlashLender {
///@dev The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan"
bytes32 public constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan");
///@notice Fee is a constant 0.1%
///@dev 1000 == 0.1% (1 == 0.0001 %)
uint256 public constant fee = 1000;
///@dev mapping of supported addresses by Flash loan provider
mapping(address => bool) public supportedTokens;
///@dev ERC20 interface specifying token contract functions
IERC20 public immutable underlying; Lender
The Lending contract needs to implement the following functions
flashfee & _flashfee()
These two functions are used to set the fee for each token the lender is willing to lend out. In our implementation we will only be lending a single token, DAI, hence the fee calculation is static.
maxFlashLoan()
Returns the maximum amount of a token the lender is able to offer in a flash loan - dependent on the lender's holdings.
Also, it is used to tell when a token is not support (or does not have liquidity) by returning a zero.
flashloan()
flashLoan() function executes the flash loan. A borrower would call on this function to execute a flash loan (via flashBorrow).
The receiver address must be a contract implementing the borrower interface. Any arbitrary data may be passed in addition to the call.
The only requirement for the implementation details of the function are that you have to call the onFlashLoan callback from the receiver:
require(receiver.onFlashLoan(msg.sender, token, amount, fee, data) == keccak256("ERC3156FlashBorrower.onFlashLoan"), "IERC3156: Callback failed");
After the callback, the flashLoan function must take the amount + fee token from the receiver, or revert if this is not successful.
Function checks for the following:
Tokens that was being requested for flash loan are supported
Calculates fee to be charged for flash loan amount
Transfers the loan amount to the receiver
Callback: calls back to receiver. Receiver has to be an
IERC3156FlashBorrowerand contain the functiononFlashLoanas described in the next section. This function ensures the legitimacy of the flash loan byverifying the sender is the correct lender
verifying the initiator for the flash loan was actually the receiver contract
returns
keccak256("ERC3156FlashBorrower.onFlashLoan")
Finally, the amount+fee is transferred from the receiver to the lender.
Receiver
Receiver has to implement IERC3156FlashBorrower interface. This will allow for callback pattern.
The borrower interface consists of only of 1 callback function: onFlashLoan(). We will overwrite this in our implementation.
Lender is a fixed IERC3156FlashLender contract defined on deployment.
Flash lender address is passed as IERC3156FlashLender into the constructor.
flashBorrow()
For the transaction to not revert, inside the onFlashLoan the borrower contract must approve amount + fee of the token to be taken by msg.sender.
OnFlashLoan()
This does three things:
verify the sender is actually the lender
verify the initiator for the flash loan was actually our contract
return the pre-defined hash to verify a successful flash loan
We could further implement additional logic here based on the passed data field if required. Essentially, what do we do with the flash loan once we have received it.
Borrower calls flashBorrow to initiate flash loan, which then calls flashLoan on the Lender contract.
Last updated