Flash Loans
Flash loans let smart contracts borrow any active reserve for a single transaction. The contract must repay the borrowed amount plus a fee before the transaction ends, or the entire transaction reverts.
Two entry points
| Function | Reserves per call | Fee waiver for FLASH_BORROWER_ROLE | Allows opening debt |
|---|---|---|---|
flashLoan | One or many | Yes (when msg.sender holds FLASH_BORROWER_ROLE) | No (see deviation below) |
flashLoanSimple | Exactly one | No | No |
flashLoanSimple is cheaper for the common case and skips the multi-asset bookkeeping.
SparkLend deviation from upstream Aave v3
Upstream Aave v3 allows flashLoan callers to convert any leg of the loan into a stable or variable debt position by passing a non-zero interestRateModes[i]. SparkLend disables this. FlashLoanLogic.executeFlashLoan reverts with FLASHLOAN_INTO_BORROW_DEPRECATED if any element of interestRateModes is non-zero (contracts/protocol/libraries/logic/FlashLoanLogic.sol).
The revert is an if/else branch in the post-callback settlement loop, not upfront validation: the receiver's executeOperation runs first, and the transaction reverts when the offending leg is settled.
To take on debt, call Pool.borrow() directly. Flash loans must repay in-transaction, full stop.
Per-reserve preconditions
ValidationLogic.validateFlashloanSimple (called for every flashed asset) checks:
- The reserve is not paused (
RESERVE_PAUSED). - The reserve is active (
RESERVE_INACTIVE). - The reserve has the flash-loan-enabled flag set (
FLASHLOAN_DISABLED, error code91).
The flag is bit 63 of the reserve configuration and is toggled by PoolConfigurator.setReserveFlashLoaning(asset, enabled).
Fees
The flash-loan fee is split between the LPs of the borrowed reserve and the protocol treasury. Both shares are bps and are governance-set via PoolConfigurator.updateFlashloanPremiumTotal and updateFlashloanPremiumToProtocol. Current values can be read from Pool.FLASHLOAN_PREMIUM_TOTAL() and Pool.FLASHLOAN_PREMIUM_TO_PROTOCOL().
For flashLoan (multi-asset), the total fee is waived for msg.senders holding FLASH_BORROWER_ROLE on the ACLManager. For flashLoanSimple, the fee is never waived.
Execution flow
For the multi-asset variant, the pre-callback transfer and post-callback settlement loop over every entry of assets. The callback signature is executeOperation(address[] assets, uint256[] amounts, uint256[] premiums, address initiator, bytes params) returns (bool).
Receiver contract requirements
Your receiver contract must:
- Implement
IFlashLoanReceiver(multi-asset) orIFlashLoanSimpleReceiver(single-asset). TheexecuteOperationcallback must returntrue; returningfalsereverts withINVALID_FLASHLOAN_EXECUTOR_RETURN(error code13). - Hold or acquire
amount + premiumof the borrowed asset beforeexecuteOperationreturns. - Grant
Poolan ERC20 allowance foramount + premiumof the borrowed asset before returning fromexecuteOperation. ThePoolperformssafeTransferFromto settle.
If Pool cannot pull amount + premium, the transaction reverts.
Callers
- EOA: deploy a contract that implements the receiver interface and call
flashLoan/flashLoanSimpleonPoolwith that contract's address asreceiverAddress. - Different contract: same pattern; the receiver address is the one implementing
executeOperation. - Same contract: pass
address(this)asreceiverAddress. The same contract initiates and receives, then settles.