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 |
|
Key Transformations |
|
State Changes |
|
Events Emitted |
|
Flow Diagram¶
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<br/>receiverAddress, asset,<br/>amount, params,<br/>referralCode"] --> Execute["FlashLoanLogic<br/>executeFlashLoanSimple"]
%% Validation Phase
subgraph ValidationPhase ["1. Validation Phase"]
direction TB
Validate["ValidationLogic<br/>validateFlashloanSimple<br/>CRITICAL: Reserve state"]
class Validate validation
end
Execute --> ValidationPhase
%% Token Transfer Phase
subgraph TransferPhase ["2. Token Transfer Phase"]
direction TB
CalcPremium["TRANSFORMATION<br/>totalPremium =<br/>amount.percentMul<br/>flashLoanPremiumTotal"]
class CalcPremium transformation
CalcPremium --> Transfer["AToken<br/>transferUnderlyingTo<br/>receiverAddress"]
end
ValidationPhase --> TransferPhase
%% Callback Phase
subgraph CallbackPhase ["3. Callback Phase"]
direction TB
Callback["IFlashLoanSimpleReceiver<br/>executeOperation<br/>CALLBACK"]
class Callback callback
Callback --> CheckReturn{"Return Value"}
CheckReturn -->|true| Continue[Continue]
CheckReturn -->|false| Error1["REVERT<br/>INVALID_FLASHLOAN_EXECUTOR_RETURN"]
class Error1 error
end
TransferPhase --> CallbackPhase
%% Repayment Phase
subgraph RepaymentPhase ["4. Repayment Phase"]
direction TB
SplitPremium["TRANSFORMATION<br/>premiumToProtocol =<br/>totalPremium.percentMul<br/>flashLoanPremiumToProtocol<br/>premiumToLP =<br/>totalPremium - premiumToProtocol"]
class SplitPremium transformation
SplitPremium --> UpdateState["ReserveLogic<br/>updateState<br/>Updates indexes"]
UpdateState --> AccrueLP["STORAGE UPDATE<br/>liquidityIndex +=<br/>premiumToLP accrued"]
class AccrueLP storage
AccrueLP --> AccrueProtocol["STORAGE UPDATE<br/>accruedToTreasury +=<br/>premiumToProtocol.<br/>rayDiv(liquidityIndex)"]
class AccrueProtocol storage
AccrueProtocol --> UpdateRates["ReserveLogic<br/>updateInterestRates<br/>amount + totalPremium added"]
UpdateRates --> PullRepayment["IERC20<br/>safeTransferFrom<br/>receiver -> aToken<br/>amount + totalPremium"]
end
Continue --> RepaymentPhase
PullRepayment --> FinalEvent["EMIT<br/>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
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:
flashLoanSimplehandles 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
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
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
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
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¶
Event Details¶
FlashLoan Event¶
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 is paused |
ValidationLogic.sol |
|
Reserve is not active |
ValidationLogic.sol |
|
Flash loans disabled for reserve |
ValidationLogic.sol |
|
Receiver returns |
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.
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