1/5
## Understanding Aave V3 Flash Loans: The `flashLoanSimple` Function Flash loans are a powerful and innovative feature in decentralized finance (DeFi), allowing users to borrow assets without providing any collateral. The defining characteristic of a flash loan is its atomicity: the borrowed amount, along with a small fee, must be repaid within the *same transaction* it was borrowed. If repayment isn't successful by the end of the transaction, the entire sequence of operations is reverted, as if nothing ever happened. The Aave V3 protocol, a leading lending and borrowing platform, provides robust flash loan capabilities through its `Pool.sol` contract. While it offers a versatile `flashLoan` function for borrowing multiple different tokens simultaneously, this lesson focuses on its simpler counterpart: `flashLoanSimple`, designed specifically for borrowing a single type of token. ## Initiating a Simple Flash Loan: A Look at `Pool.sol` To initiate a flash loan for a single asset, your smart contract (the "receiver") calls the `flashLoanSimple` function on Aave V3's `Pool.sol` contract. The function signature in `Pool.sol` (around line 412) is as follows: ```solidity // From Pool.sol function flashLoanSimple( address receiverAddress, address asset, uint256 amount, bytes calldata params, // Additional params for the receiver uint16 referralCode ) public virtual override { DataTypes.FlashLoanSimpleParams memory flashParams = DataTypes.FlashLoanSimpleParams({ receiverAddress: receiverAddress, asset: asset, amount: amount, params: params, // Note: original summary had a typo "ядер:", corrected to "params:" referralCode: referralCode, flashLoanPremiumToProtocol: _flashLoanPremiumToProtocol, // Fee for the protocol flashLoanPremiumTotal: _flashLoanPremiumTotal // Total fee (includes protocol fee + other potential fees) }); FlashLoanLogic.executeFlashLoanSimple(_reserves[asset], flashParams); } ``` Let's break down the parameters: * **`receiverAddress` (address):** This is the address of your smart contract that will receive the borrowed tokens and execute the logic to utilize them. Crucially, this contract *must* implement the `executeOperation` function as defined by the `IFlashLoanSimpleReceiver` interface. * **`asset` (address):** The contract address of the ERC20 token you wish to borrow. * **`amount` (uint256):** The quantity of the `asset` you intend to borrow. * **`params` (bytes calldata):** Arbitrary data that you can pass along with the flash loan. This data will be relayed to your `receiverAddress` contract's `executeOperation` function, allowing for flexible and dynamic loan usage. * **`referralCode` (uint16):** An optional code for referral programs. Internally, `flashLoanSimple` also accesses `_flashLoanPremiumTotal`, which represents the total percentage fee charged on the borrowed `amount`, and `_flashLoanPremiumToProtocol`, the portion of that fee allocated to the Aave protocol. The function then constructs a `flashParams` struct and delegates the core flash loan execution to the `executeFlashLoanSimple` function within the `FlashLoanLogic.sol` library contract, passing along the reserve data for the specified `asset` and the prepared `flashParams`. ## The Core Mechanics: `FlashLoanLogic.executeFlashLoanSimple` Deep Dive The heart of the `flashLoanSimple` operation resides in the `executeFlashLoanSimple` function within the `FlashLoanLogic.sol` library. This function orchestrates the loan, the callback to your contract, and the subsequent repayment. Here's a look at the relevant parts of `executeFlashLoanSimple` (from `FlashLoanLogic.sol`, around line 184): ```solidity // From FlashLoanLogic.sol function executeFlashLoanSimple( DataTypes.ReserveData storage reserve, DataTypes.FlashLoanSimpleParams memory params ) external { // ... (ValidationLogic call can be present for checks) IFlashLoanSimpleReceiver receiver = IFlashLoanSimpleReceiver(params.receiverAddress); uint256 totalPremium = params.amount.percentMul(params.flashLoanPremiumTotal); // Calculate the fee // ... (Logic related to virtual accounts may exist but is not the primary focus here) // 1. Transfer tokens to the receiver IAToken(reserve.aTokenAddress).transferUnderlyingTo(params.receiverAddress, params.amount); // 2. Call the receiver's executeOperation function require( receiver.executeOperation( params.asset, params.amount, totalPremium, msg.sender, // initiator of the flash loan call to Pool.sol params.params // user-defined params ), Errors.INVALID_FLASHLOAN_EXECUTOR_RETURN ); // 3. Handle repayment _handleFlashLoanRepayment( reserve, DataTypes.FlashLoanRepaymentParams({ asset: params.asset, receiverAddress: params.receiverAddress, amount: params.amount, totalPremium: totalPremium, flashLoanPremiumToProtocol: params.flashLoanPremiumToProtocol, referralCode: params.referralCode }) ); } ``` Let's dissect the key steps: 1. **Calculating the Premium:** * `uint256 totalPremium = params.amount.percentMul(params.flashLoanPremiumTotal);` * The first order of business is to calculate the fee (`totalPremium`) for the flash loan. This is done by multiplying the borrowed `params.amount` by the `params.flashLoanPremiumTotal` (which is a percentage, e.g., 0.09%). This `totalPremium` is the additional amount your contract will need to repay on top of the principal borrowed. 2. **Transferring Funds to the Receiver:** * `IAToken(reserve.aTokenAddress).transferUnderlyingTo(params.receiverAddress, params.amount);` * Next, the Aave protocol, through the AToken contract associated with the specific `asset`'s reserve (`reserve.aTokenAddress`), transfers the requested `params.amount` of the underlying token directly to your `params.receiverAddress`. At this point, your contract has custody of the borrowed funds. 3. **Executing Your Logic: The `executeOperation` Callback:** * `require(receiver.executeOperation(params.asset, params.amount, totalPremium, msg.sender, params.params), Errors.INVALID_FLASHLOAN_EXECUTOR_RETURN);` * This is the pivotal step where control is handed over to your contract. The `FlashLoanLogic` contract calls the `executeOperation` function on your `receiverAddress`. * The parameters passed to your `executeOperation` function are: * `params.asset`: The address of the borrowed token. * `params.amount`: The amount of the token borrowed. * `totalPremium`: The calculated fee that needs to be repaid. * `msg.sender`: The address that initiated the `flashLoanSimple` call to the `Pool.sol` contract. * `params.params`: The arbitrary `bytes` data you initially passed into `flashLoanSimple`. * **Inside your `executeOperation` function:** * Your contract now possesses the `params.amount` of `params.asset`. * You can implement any desired logic: arbitrage between DEXs, liquidating positions, swapping collateral, or any other operation that can be completed within a single transaction. * **Crucially, before your `executeOperation` function completes, your contract MUST approve the Aave Pool's AToken contract (`reserve.aTokenAddress`) to spend at least `params.amount + totalPremium` of the `params.asset` from your contract's balance.** This approval is vital for the repayment step. Without it, the Aave protocol cannot pull the funds back, and the entire transaction will revert. * Your `executeOperation` function must also return `true` to signal successful execution and readiness for repayment. If it returns `false` or reverts for any other reason, the entire flash loan transaction fails. 4. **Ensuring Repayment: `_handleFlashLoanRepayment`:** * Assuming your `executeOperation` returned `true` and the necessary ERC20 approval was set, the `FlashLoanLogic` contract proceeds to call its internal `_handleFlashLoanRepayment` function. ```solidity // From FlashLoanLogic.sol (around line 232) function _handleFlashLoanRepayment( DataTypes.ReserveData storage reserve, DataTypes.FlashLoanRepaymentParams memory params ) internal { // ... (calculations for premiumToProtocol, premiumToLP, and ultimately amountPlusPremium) uint256 amountPlusPremium = params.amount + params.totalPremium; // ... (various state updates, liquidity index updates, treasury accrual logic) // The actual repayment pull IERC20(params.asset).safeTransferFrom( params.receiverAddress, // From: Your contract reserve.aTokenAddress, // To: The AToken contract (representing the Aave pool) amountPlusPremium // Amount: Borrowed amount + total fee ); // ... (further logic to handle repayment on AToken, emit events, etc.) } ``` * Inside `_handleFlashLoanRepayment`, the total amount due, `amountPlusPremium` (which is `params.amount + params.totalPremium`), is confirmed. * The function then executes `IERC20(params.asset).safeTransferFrom(...)`. This call pulls the `amountPlusPremium` of the `params.asset` from your `params.receiverAddress` back to the Aave protocol (specifically, to the `reserve.aTokenAddress`). This step only succeeds because your contract granted the necessary token approval within `executeOperation`. * Following a successful transfer, various internal Aave state variables are updated, and events are emitted. ## Building Your Flash Loan Receiver Contract: Key Requirements To successfully utilize Aave V3's `flashLoanSimple`, your receiver contract must adhere to these requirements: 1. **Call `flashLoanSimple`**: Your contract initiates the process by calling the `flashLoanSimple` function on the Aave `Pool.sol` contract, providing the necessary parameters. 2. **Implement `executeOperation`**: Your contract must implement the `IFlashLoanSimpleReceiver` interface. This interface defines the `executeOperation` function: ```solidity interface IFlashLoanSimpleReceiver { function executeOperation( address asset, uint256 amount, uint256 premium, address initiator, bytes calldata params ) external returns (bool); } ``` 3. **Approve Repayment**: Within your `executeOperation` implementation, after performing your desired actions with the borrowed funds, your contract **must** approve the Aave AToken contract corresponding to the borrowed `asset` to withdraw `amount + premium`. The AToken address can be retrieved from the `reserve.aTokenAddress` which isn't directly passed to `executeOperation` but is known by the `FlashLoanLogic` which is the `msg.sender` for the AToken's `transferUnderlyingTo` and later the target for `safeTransferFrom`. For practical purposes, the receiver often needs to know or fetch this AToken address, or approve the `Pool` contract which would then have authority. A common pattern is `IERC20(asset).approve(address(this_aTokenAddress), amount + premium);`. You'll need to ensure you have the correct AToken address for the borrowed asset. 4. **Return `true`**: Your `executeOperation` function must return `true` to indicate that your logic executed successfully and you are prepared to repay the loan plus the premium. ## Practical Application: Arbitrage with Flash Loans A common use case for flash loans is arbitrage. Imagine an asset is trading at a lower price on DEX A and a higher price on DEX B. A contract could: 1. Flash loan the asset (e.g., DAI) from Aave. 2. Use the borrowed DAI to buy another asset (e.g., ETH) at a lower price on DEX A. 3. Sell the ETH on DEX B for more DAI. 4. Repay the flash loan (DAI + premium) to Aave. 5. Keep the remaining DAI as profit. All these steps occur within the single transaction initiated by the `flashLoanSimple` call. ## The `flashLoanSimple` Lifecycle: A Step-by-Step Summary Here's a condensed flow of a successful `flashLoanSimple` operation: 1. **Your Contract (`Receiver`)** calls `Pool.flashLoanSimple(yourContractAddress, assetAddress, loanAmount, yourParams, 0)`. 2. The **Aave `Pool` contract** validates inputs and calls `FlashLoanLogic.executeFlashLoanSimple(...)`. 3. **`FlashLoanLogic`** calculates the `totalPremium` (the fee). 4. **`FlashLoanLogic`** transfers `loanAmount` of `assetAddress` to **Your Contract**. 5. **`FlashLoanLogic`** calls `YourContract.executeOperation(assetAddress, loanAmount, totalPremium, originalCaller, yourParams)`. 6. **Inside `YourContract.executeOperation`:** * You implement your custom logic using the `loanAmount` of `assetAddress`. * You **MUST** execute `IERC20(assetAddress).approve(aTokenAddressForAsset, loanAmount + totalPremium);` to allow Aave to reclaim the funds. * You return `true`. 7. Control returns to **`FlashLoanLogic`**, which then calls `_handleFlashLoanRepayment(...)`. 8. **`_handleFlashLoanRepayment`** uses `IERC20(assetAddress).safeTransferFrom(YourContractAddress, aTokenAddressForAsset, loanAmount + totalPremium);` to pull the principal and fee back to the Aave protocol. 9. The transaction successfully completes. If any part of this process fails – for example, if your `executeOperation` returns `false`, if the required token approval is not set, or if your contract does not hold sufficient funds (borrowed amount + premium) of the asset at the time of repayment – the entire transaction will revert. The Aave protocol remains solvent, and it's as if the flash loan attempt never occurred.
flashLoanSimple
FunctionFlash loans are a powerful and innovative feature in decentralized finance (DeFi), allowing users to borrow assets without providing any collateral. The defining characteristic of a flash loan is its atomicity: the borrowed amount, along with a small fee, must be repaid within the same transaction it was borrowed. If repayment isn't successful by the end of the transaction, the entire sequence of operations is reverted, as if nothing ever happened.
The Aave V3 protocol, a leading lending and borrowing platform, provides robust flash loan capabilities through its Pool.sol
contract. While it offers a versatile flashLoan
function for borrowing multiple different tokens simultaneously, this lesson focuses on its simpler counterpart: flashLoanSimple
, designed specifically for borrowing a single type of token.
Pool.sol
To initiate a flash loan for a single asset, your smart contract (the "receiver") calls the flashLoanSimple
function on Aave V3's Pool.sol
contract.
The function signature in Pool.sol
(around line 412) is as follows:
Let's break down the parameters:
receiverAddress
(address): This is the address of your smart contract that will receive the borrowed tokens and execute the logic to utilize them. Crucially, this contract must implement the executeOperation
function as defined by the IFlashLoanSimpleReceiver
interface.
asset
(address): The contract address of the ERC20 token you wish to borrow.
amount
(uint256): The quantity of the asset
you intend to borrow.
params
(bytes calldata): Arbitrary data that you can pass along with the flash loan. This data will be relayed to your receiverAddress
contract's executeOperation
function, allowing for flexible and dynamic loan usage.
referralCode
(uint16): An optional code for referral programs.
Internally, flashLoanSimple
also accesses _flashLoanPremiumTotal
, which represents the total percentage fee charged on the borrowed amount
, and _flashLoanPremiumToProtocol
, the portion of that fee allocated to the Aave protocol.
The function then constructs a flashParams
struct and delegates the core flash loan execution to the executeFlashLoanSimple
function within the FlashLoanLogic.sol
library contract, passing along the reserve data for the specified asset
and the prepared flashParams
.
FlashLoanLogic.executeFlashLoanSimple
Deep DiveThe heart of the flashLoanSimple
operation resides in the executeFlashLoanSimple
function within the FlashLoanLogic.sol
library. This function orchestrates the loan, the callback to your contract, and the subsequent repayment.
Here's a look at the relevant parts of executeFlashLoanSimple
(from FlashLoanLogic.sol
, around line 184):
Let's dissect the key steps:
Calculating the Premium:
uint256 totalPremium = params.amount.percentMul(params.flashLoanPremiumTotal);
The first order of business is to calculate the fee (totalPremium
) for the flash loan. This is done by multiplying the borrowed params.amount
by the params.flashLoanPremiumTotal
(which is a percentage, e.g., 0.09%). This totalPremium
is the additional amount your contract will need to repay on top of the principal borrowed.
Transferring Funds to the Receiver:
IAToken(reserve.aTokenAddress).transferUnderlyingTo(params.receiverAddress, params.amount);
Next, the Aave protocol, through the AToken contract associated with the specific asset
's reserve (reserve.aTokenAddress
), transfers the requested params.amount
of the underlying token directly to your params.receiverAddress
. At this point, your contract has custody of the borrowed funds.
Executing Your Logic: The executeOperation
Callback:
require(receiver.executeOperation(params.asset, params.amount, totalPremium, msg.sender, params.params), Errors.INVALID_FLASHLOAN_EXECUTOR_RETURN);
This is the pivotal step where control is handed over to your contract. The FlashLoanLogic
contract calls the executeOperation
function on your receiverAddress
.
The parameters passed to your executeOperation
function are:
params.asset
: The address of the borrowed token.
params.amount
: The amount of the token borrowed.
totalPremium
: The calculated fee that needs to be repaid.
msg.sender
: The address that initiated the flashLoanSimple
call to the Pool.sol
contract.
params.params
: The arbitrary bytes
data you initially passed into flashLoanSimple
.
Inside your executeOperation
function:
Your contract now possesses the params.amount
of params.asset
.
You can implement any desired logic: arbitrage between DEXs, liquidating positions, swapping collateral, or any other operation that can be completed within a single transaction.
Crucially, before your executeOperation
function completes, your contract MUST approve the Aave Pool's AToken contract (reserve.aTokenAddress
) to spend at least params.amount + totalPremium
of the params.asset
from your contract's balance. This approval is vital for the repayment step. Without it, the Aave protocol cannot pull the funds back, and the entire transaction will revert.
Your executeOperation
function must also return true
to signal successful execution and readiness for repayment. If it returns false
or reverts for any other reason, the entire flash loan transaction fails.
Ensuring Repayment: _handleFlashLoanRepayment
:
Assuming your executeOperation
returned true
and the necessary ERC20 approval was set, the FlashLoanLogic
contract proceeds to call its internal _handleFlashLoanRepayment
function.
Inside _handleFlashLoanRepayment
, the total amount due, amountPlusPremium
(which is params.amount + params.totalPremium
), is confirmed.
The function then executes IERC20(params.asset).safeTransferFrom(...)
. This call pulls the amountPlusPremium
of the params.asset
from your params.receiverAddress
back to the Aave protocol (specifically, to the reserve.aTokenAddress
). This step only succeeds because your contract granted the necessary token approval within executeOperation
.
Following a successful transfer, various internal Aave state variables are updated, and events are emitted.
To successfully utilize Aave V3's flashLoanSimple
, your receiver contract must adhere to these requirements:
Call flashLoanSimple
: Your contract initiates the process by calling the flashLoanSimple
function on the Aave Pool.sol
contract, providing the necessary parameters.
Implement executeOperation
: Your contract must implement the IFlashLoanSimpleReceiver
interface. This interface defines the executeOperation
function:
Approve Repayment: Within your executeOperation
implementation, after performing your desired actions with the borrowed funds, your contract must approve the Aave AToken contract corresponding to the borrowed asset
to withdraw amount + premium
. The AToken address can be retrieved from the reserve.aTokenAddress
which isn't directly passed to executeOperation
but is known by the FlashLoanLogic
which is the msg.sender
for the AToken's transferUnderlyingTo
and later the target for safeTransferFrom
. For practical purposes, the receiver often needs to know or fetch this AToken address, or approve the Pool
contract which would then have authority. A common pattern is IERC20(asset).approve(address(this_aTokenAddress), amount + premium);
. You'll need to ensure you have the correct AToken address for the borrowed asset.
Return true
: Your executeOperation
function must return true
to indicate that your logic executed successfully and you are prepared to repay the loan plus the premium.
A common use case for flash loans is arbitrage. Imagine an asset is trading at a lower price on DEX A and a higher price on DEX B. A contract could:
Flash loan the asset (e.g., DAI) from Aave.
Use the borrowed DAI to buy another asset (e.g., ETH) at a lower price on DEX A.
Sell the ETH on DEX B for more DAI.
Repay the flash loan (DAI + premium) to Aave.
Keep the remaining DAI as profit.
All these steps occur within the single transaction initiated by the flashLoanSimple
call.
flashLoanSimple
Lifecycle: A Step-by-Step SummaryHere's a condensed flow of a successful flashLoanSimple
operation:
Your Contract (Receiver
) calls Pool.flashLoanSimple(yourContractAddress, assetAddress, loanAmount, yourParams, 0)
.
The Aave Pool
contract validates inputs and calls FlashLoanLogic.executeFlashLoanSimple(...)
.
FlashLoanLogic
calculates the totalPremium
(the fee).
FlashLoanLogic
transfers loanAmount
of assetAddress
to Your Contract.
FlashLoanLogic
calls YourContract.executeOperation(assetAddress, loanAmount, totalPremium, originalCaller, yourParams)
.
Inside YourContract.executeOperation
:
You implement your custom logic using the loanAmount
of assetAddress
.
You MUST execute IERC20(assetAddress).approve(aTokenAddressForAsset, loanAmount + totalPremium);
to allow Aave to reclaim the funds.
You return true
.
Control returns to FlashLoanLogic
, which then calls _handleFlashLoanRepayment(...)
.
_handleFlashLoanRepayment
uses IERC20(assetAddress).safeTransferFrom(YourContractAddress, aTokenAddressForAsset, loanAmount + totalPremium);
to pull the principal and fee back to the Aave protocol.
The transaction successfully completes.
If any part of this process fails – for example, if your executeOperation
returns false
, if the required token approval is not set, or if your contract does not hold sufficient funds (borrowed amount + premium) of the asset at the time of repayment – the entire transaction will revert. The Aave protocol remains solvent, and it's as if the flash loan attempt never occurred.
A practical breakdown to Aave V3's `flashLoanSimple`: Single-Token Borrowing - Learn the mechanics of Aave V3's `flashLoanSimple` for uncollateralized, single-asset loans, repaid within one transaction. Understand the receiver contract's role, the `executeOperation` callback, and the crucial token approval step for successful repayment.
Previous lesson
Previous
Next lesson
Next
Give us feedback
Course Overview
About the course
Aave V3 core functionalities and key features
APY & APR, market forces, utilization rates, interest rate models
Reserves, ATokens, debt tokens, liquidity and borrow index
Aave smart contracts architecture
Supply, borrow, repay, withdrawal, liquidation
Compound interest
Reserve factors, liquidation pricing, credit delegation, LTV
Aave V3 flash loans
How to buy long and sell short using Aave V3
How to build a functional app to buy long, sell short, and close a position.
Last updated on July 20, 2025
Duration: 8min
Duration: 49min
Duration: 43min
Duration: 12min
Course Overview
About the course
Aave V3 core functionalities and key features
APY & APR, market forces, utilization rates, interest rate models
Reserves, ATokens, debt tokens, liquidity and borrow index
Aave smart contracts architecture
Supply, borrow, repay, withdrawal, liquidation
Compound interest
Reserve factors, liquidation pricing, credit delegation, LTV
Aave V3 flash loans
How to buy long and sell short using Aave V3
How to build a functional app to buy long, sell short, and close a position.
Last updated on July 20, 2025