# Flash Loan Simple Flow
End-to-end execution flow for simple flash loans in Aave V3 (single Asset only).
## Quick Reference
| Aspect | Details |
|--------|---------|
| **Entry Point** | `Pool.flashLoanSimple(receiverAddress, asset, amount, params, referralCode)` |
| **Key Transformations** | [Premium Calculation](../transformations/index.md#flash-loan-premiums) |
| **State Changes** | `reserve.accruedToTreasury += premiumToProtocol.rayDiv(liquidityIndex)` |
| **Events Emitted** | `FlashLoan` |
---
## Flow Diagram
```mermaid
flowchart TD
%% Styling definitions
classDef validation fill:#ffcccc,stroke:#ff0000,stroke-width:2px
classDef transformation fill:#ccffcc,stroke:#00aa00,stroke-width:2px
classDef storage fill:#ccccff,stroke:#0000ff,stroke-width:2px
classDef event fill:#ffffcc,stroke:#aaaa00,stroke-width:2px
classDef error fill:#ff0000,stroke:#000000,color:#fff
classDef critical stroke:#ff0000,stroke-width:3px
classDef callback fill:#ccffff,stroke:#00aaaa,stroke-width:2px
%% Entry point
Entry["Pool.flashLoanSimple
receiverAddress, asset,
amount, params,
referralCode"] --> Execute["FlashLoanLogic
executeFlashLoanSimple"]
%% Validation Phase
subgraph ValidationPhase ["1. Validation Phase"]
direction TB
Validate["ValidationLogic
validateFlashloanSimple
CRITICAL: Reserve state"]
class Validate validation
end
Execute --> ValidationPhase
%% Token Transfer Phase
subgraph TransferPhase ["2. Token Transfer Phase"]
direction TB
CalcPremium["TRANSFORMATION
totalPremium =
amount.percentMul
flashLoanPremiumTotal"]
class CalcPremium transformation
CalcPremium --> Transfer["AToken
transferUnderlyingTo
receiverAddress"]
end
ValidationPhase --> TransferPhase
%% Callback Phase
subgraph CallbackPhase ["3. Callback Phase"]
direction TB
Callback["IFlashLoanSimpleReceiver
executeOperation
CALLBACK"]
class Callback callback
Callback --> CheckReturn{"Return Value"}
CheckReturn -->|true| Continue[Continue]
CheckReturn -->|false| Error1["REVERT
INVALID_FLASHLOAN_EXECUTOR_RETURN"]
class Error1 error
end
TransferPhase --> CallbackPhase
%% Repayment Phase
subgraph RepaymentPhase ["4. Repayment Phase"]
direction TB
SplitPremium["TRANSFORMATION
premiumToProtocol =
totalPremium.percentMul
flashLoanPremiumToProtocol
premiumToLP =
totalPremium - premiumToProtocol"]
class SplitPremium transformation
SplitPremium --> UpdateState["ReserveLogic
updateState
Updates indexes"]
UpdateState --> AccrueLP["STORAGE UPDATE
liquidityIndex +=
premiumToLP accrued"]
class AccrueLP storage
AccrueLP --> AccrueProtocol["STORAGE UPDATE
accruedToTreasury +=
premiumToProtocol.
rayDiv(liquidityIndex)"]
class AccrueProtocol storage
AccrueProtocol --> UpdateRates["ReserveLogic
updateInterestRates
amount + totalPremium added"]
UpdateRates --> PullRepayment["IERC20
safeTransferFrom
receiver -> aToken
amount + totalPremium"]
end
Continue --> RepaymentPhase
PullRepayment --> FinalEvent["EMIT
FlashLoan"]
class FinalEvent event
%% Error annotations
%% CRITICAL: Receiver must approve pool to pull repayment
%% CRITICAL: Receiver must return true from executeOperation
%% CRITICAL: Total premium must be available in receiver contract
%% Link styles for critical paths
linkStyle 2 stroke:#ff0000,stroke-width:3px
linkStyle 10 stroke:#ff0000,stroke-width:3px
```
---
## Step-by-Step Execution
### 1. Entry Point
**File:** `contracts/protocol/pool/Pool.sol`
```solidity
function flashLoanSimple(
address receiverAddress,
address asset,
uint256 amount,
bytes calldata params,
uint16 referralCode
) public virtual override {
DataTypes.FlashloanSimpleParams memory flashParams = DataTypes.FlashloanSimpleParams({
receiverAddress: receiverAddress,
asset: asset,
amount: amount,
params: params,
referralCode: referralCode,
flashLoanPremiumToProtocol: _flashLoanPremiumToProtocol,
flashLoanPremiumTotal: _flashLoanPremiumTotal
});
FlashLoanLogic.executeFlashLoanSimple(_reserves[asset], flashParams);
}
```
**Key difference from `flashLoan`:**
- `flashLoanSimple` handles a single Asset (no arrays)
- Simpler interface with direct parameters instead of arrays
- Lower gas overhead for single-Asset flash loans
### 2. Execute Flash Loan Simple
**File:** `contracts/protocol/libraries/logic/FlashLoanLogic.sol`
```solidity
function executeFlashLoanSimple(
DataTypes.ReserveData storage reserve,
DataTypes.FlashloanSimpleParams memory params
) external {
// The usual action flow (cache -> updateState -> validation -> changeState -> updateRates)
// is altered to (validation -> user payload -> cache -> updateState -> changeState -> updateRates) for flashloans.
// This is done to protect against reentrance and rate manipulation within the user specified payload.
ValidationLogic.validateFlashloanSimple(reserve);
IFlashLoanSimpleReceiver receiver = IFlashLoanSimpleReceiver(params.receiverAddress);
uint256 totalPremium = params.amount.percentMul(params.flashLoanPremiumTotal);
IAToken(reserve.aTokenAddress).transferUnderlyingTo(params.receiverAddress, params.amount);
require(
receiver.executeOperation(
params.asset,
params.amount,
totalPremium,
msg.sender,
params.params
),
Errors.INVALID_FLASHLOAN_EXECUTOR_RETURN
);
_handleFlashLoanRepayment(
reserve,
DataTypes.FlashLoanRepaymentParams({
asset: params.asset,
receiverAddress: params.receiverAddress,
amount: params.amount,
totalPremium: totalPremium,
flashLoanPremiumToProtocol: params.flashLoanPremiumToProtocol,
referralCode: params.referralCode
})
);
}
```
### 3. Validation Checks
**File:** `contracts/protocol/libraries/logic/ValidationLogic.sol`
```solidity
function validateFlashloanSimple(DataTypes.ReserveData storage reserve) internal view {
DataTypes.ReserveConfigurationMap memory configuration = reserve.configuration;
require(!configuration.getPaused(), Errors.RESERVE_PAUSED);
require(configuration.getActive(), Errors.RESERVE_INACTIVE);
require(configuration.getFlashLoanEnabled(), Errors.FLASHLOAN_DISABLED);
}
```
### 4. Handle Flash Loan Repayment
**File:** `contracts/protocol/libraries/logic/FlashLoanLogic.sol`
```solidity
function _handleFlashLoanRepayment(
DataTypes.ReserveData storage reserve,
DataTypes.FlashLoanRepaymentParams memory params
) internal {
uint256 premiumToProtocol = params.totalPremium.percentMul(params.flashLoanPremiumToProtocol);
uint256 premiumToLP = params.totalPremium - premiumToProtocol;
uint256 amountPlusPremium = params.amount + params.totalPremium;
DataTypes.ReserveCache memory reserveCache = reserve.cache();
reserve.updateState(reserveCache);
reserveCache.nextLiquidityIndex = reserve.cumulateToLiquidityIndex(
IERC20(reserveCache.aTokenAddress).totalSupply() +
uint256(reserve.accruedToTreasury).rayMul(reserveCache.nextLiquidityIndex),
premiumToLP
);
reserve.accruedToTreasury += premiumToProtocol
.rayDiv(reserveCache.nextLiquidityIndex)
.toUint128();
reserve.updateInterestRates(reserveCache, params.asset, amountPlusPremium, 0);
IERC20(params.asset).safeTransferFrom(
params.receiverAddress,
reserveCache.aTokenAddress,
amountPlusPremium
);
IAToken(reserveCache.aTokenAddress).handleRepayment(
params.receiverAddress,
params.receiverAddress,
amountPlusPremium
);
emit FlashLoan(
params.receiverAddress,
msg.sender,
params.asset,
params.amount,
DataTypes.InterestRateMode(0),
params.totalPremium,
params.referralCode
);
}
```
### 5. IFlashLoanSimpleReceiver Interface
**File:** `contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol`
```solidity
interface IFlashLoanSimpleReceiver {
/**
* @notice Executes an operation after receiving the flash-borrowed asset
* @dev Ensure that the contract can return the debt + premium, e.g., has
* enough funds to repay and has approved the Pool to pull the total amount
* @param asset The address of the flash-borrowed asset
* @param amount The amount of the flash-borrowed asset
* @param premium The fee of the flash-borrowed asset
* @param initiator The address of the flashloan initiator
* @param params The byte-encoded params passed when initiating the flashloan
* @return True if the execution of the operation succeeds, false otherwise
*/
function executeOperation(
address asset,
uint256 amount,
uint256 premium,
address initiator,
bytes calldata params
) external returns (bool);
function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
function POOL() external view returns (IPool);
}
```
---
## Amount Transformations
### Premium Calculation
```
User requests flash loan amount
↓
amount = 1000 * 10^18 // 1000 tokens
↓
flashLoanPremiumTotal = 0.09% = 9 (basis points)
↓
totalPremium = amount.percentMul(flashLoanPremiumTotal)
= (1000 * 10^18 * 9) / 10000
= 0.9 * 10^18 // 0.9 tokens
↓
flashLoanPremiumToProtocol = 0.03% = 3 (basis points)
↓
premiumToProtocol = totalPremium.percentMul(flashLoanPremiumToProtocol)
= (0.9 * 10^18 * 3) / 10000
= 0.27 * 10^18
↓
premiumToLP = totalPremium - premiumToProtocol
= 0.9 * 10^18 - 0.27 * 10^18
= 0.63 * 10^18
↓
amountPlusPremium = amount + totalPremium
= 1000.9 * 10^18
```
**Premium Distribution:**
- **Total Premium**: Paid by borrower (e.g., 0.09% on Aave V3 mainnet)
- **To Protocol**: Portion sent to treasury (e.g., 0.03%)
- **To LP**: Portion added to liquidity index for LPs (e.g., 0.06%)
**Key Points:**
- Receiver must have approved the Pool to pull `amount + totalPremium`
- Receiver must return `true` from `executeOperation` or transaction reverts
- Premiums are protocol-configurable and can vary by network
---
## Event Details
### FlashLoan Event
```solidity
event FlashLoan(
address indexed target, // Receiver contract address
address initiator, // msg.sender (initiator)
address indexed asset, // Asset address
uint256 amount, // Amount flash loaned
DataTypes.InterestRateMode interestRateMode, // Always 0 for flash loans
uint256 premium, // Total premium paid
uint16 indexed referralCode // Referral code
);
```
---
## Error Conditions
| Error | Condition | File |
|-------|-----------|------|
| `RESERVE_PAUSED` | Reserve is paused | ValidationLogic.sol |
| `RESERVE_INACTIVE` | Reserve is not active | ValidationLogic.sol |
| `FLASHLOAN_DISABLED` | Flash loans disabled for reserve | ValidationLogic.sol |
| `INVALID_FLASHLOAN_EXECUTOR_RETURN` | Receiver returns `false` from `executeOperation` | FlashLoanLogic.sol |
**Note:** Unlike `flashLoan`, `flashLoanSimple` does not validate the receiver address is a contract. The caller is responsible for ensuring the receiver correctly implements `IFlashLoanSimpleReceiver`.
---
## Related Flows
- [Flash Loan (Multi-Asset)](./flash_loan.md) - Multi-asset flash loan with arrays
- [Borrow Flow](./borrow.md) - Standard borrowing
- [Liquidation Flow](./liquidation.md) - Liquidation execution
- [Repay Flow](./repay.md) - Debt repayment
---
## Source File Locations
```
contracts/protocol/pool/Pool.sol
contracts/protocol/libraries/logic/FlashLoanLogic.sol
contracts/protocol/libraries/logic/ValidationLogic.sol
contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol
contracts/protocol/tokenization/AToken.sol
```