ERC-7399 Flash loan standard interface (⚡ Flash Loans ⚡)

Review · Vaults & DeFi · created 2023-07-25

Unifies the request, callback, and repayment process for flash loans, which are borrowed and repaid within a single transaction, into one interface. Borrowers can switch between multiple lenders without modifying their code.

Why it is needed

A flash loan is a loan that lets you borrow a large amount without collateral, provided you repay it along with a fee within the same transaction. Right now, every provider of this feature does it differently. The syntax of the function that initiates the loan differs, some providers let you separate the address that receives the funds from the address that receives the callback while others don't, and repayment methods differ too, with some lenders pulling funds from the borrower and others requiring the borrower to send them directly. Handling of flash minting, where a lender mints its own token on the spot to lend it out, also differs. As a result, borrowers must write separate integration code for each lender, and the cost of maintaining compatibility grows as the ecosystem grows. This standard bundles the flexibility needed by both lenders and borrowers into a single interface, letting borrowers freely switch lenders without modifying their code.

What to implement

In depth

Separation of the four roles

This standard involves four roles. The initiator is the party that calls the lender's flash function, and it decides the asset and amount, the loan receiver, and the callback receiver and callback function. The loan receiver is the address that receives the borrowed asset, and the callback receiver is the address holding the function that the lender calls after sending the asset. The payment receiver is set by the lender and is the address that receives the principal and fee. Separating the addresses through which assets flow from the address that executes code allows control flow and asset flow to be designed independently. The loan receiver address is not passed as a callback argument, because it is usually the same as the callback receiver, and when it differs the initiator can pass it along in data.

Query functions do not revert

maxFlashLoan must return the maximum amount that can be borrowed for the given asset and must not revert. It must return 0 if loans of that asset are unavailable. flashFee must return the fee for borrowing the given amount and must likewise not revert, returning type(uint256).max if the loan is unavailable. The reason numbers are returned instead of reverting even in the unavailable case is to let borrowers query multiple lenders, compare terms, and sort them without handling reverts. The fee can be set to 0, and the lender decides for itself which assets to support, including supporting all assets.

Rules that the flash function must follow

flash must transfer amount of asset to the loan receiver before executing the callback. The callback must set msg.sender as the initiator and pass through the received asset, amount, and data unchanged. The fee argument in the callback must equal flashFee(asset, amount). Before the callback finishes, the asset balance of the payment receiver must have increased by amount + fee compared to the start of the callback, otherwise it must revert. The return value of flash must equal the return value of the callback. The reason initiator and amount are passed as separate arguments rather than mixed into data is that putting them in data would require the receiver to have a separate mechanism to verify their authenticity.

The callback is specified with a 20 byte address and a 4 byte signature

The callback argument of flash is encoded as 24 bytes, combining the 20 byte address of the callback receiver and the 4 byte function signature of the callback. The callback function can have any name and can be overloaded, but its argument layout must be (initiator, paymentReceiver, asset, amount, fee, data) and its return type must be bytes memory. Because the function to call can itself be specified, the callback receiver can have different functions for different purposes without needing routing information inside data to indicate which action to perform. Since the callback's return value is passed through flash back to the initiator unchanged, the receiver can also return arbitrary information to the initiator.

Repayment is push based

Repayment is a push model in which the borrower sends the principal and fee to the payment receiver. The pull model, where the lender draws funds from the borrower, is more common, but the push model allows asset flow and control flow to be separated. A lender that can only implement the pull model can deploy a simple wrapper contract that exposes this standard's external interface while internally pulling liquidity using the pull model. The function was named flash because it has clear meaning as a verb, is unlikely to collide with other function names, and covers both the case where the lender lends assets it holds and the case of flash minting, where tokens are issued on the spot.

This is a successor to ERC-3156 and is still under review

This standard is a successor to the existing flash loan standard, ERC-3156. There is no direct backwards compatibility, but the source explains that it is easy to build a wrapper contract that pulls liquidity from an ERC-3156 lender and exposes this standard's external interface. So an existing ERC-3156 based lender can accept borrowers that follow this standard without significant cost. The source's status is Review, meaning it is still under review and its content may change. It is safer to check the latest source at the time of actual implementation.

Things to watch out for

Original ERC · ← ERCs