Standardized Flash Loan Interface
Operations → Flash Loans → Standardized Interface (Composer)
While flash loan implementations are nearly all the same, the explicit usage and interface vary.
In this section we cover the flash loans provided by:
- Balancer V2 and forks
- Aave V2 & V3 and forks
- Morpho Blue
Flash Loan Parameters
The following parameters need to be provided for Aave V2, V3 and Morpho Blue:
Parameter Structure
Offset | Length (bytes) | Type | Description |
---|---|---|---|
0 | 20 | address |
Asset contract address to borrow |
20 | 20 | address |
Flash loan pool contract address |
40 | 16 | uint128 |
Amount to borrow (in asset decimals) |
56 | 2 | uint16 |
paramsLength + 1 (total parameter length) |
58 | 1 | uint8 |
Pool identifier for validation |
59 + paramsLength | paramsLength | bytes |
Packed composer operations to execute |
Key Technical Details
Validation Logic
Since flash loans use callbacks, we need poolId
to validate that the callback was triggered by a trusted Aave or Morpho pool.
Parameters Structure
params
is a packed set of composer operations that will be executed during the flash loan.
Important Considerations
- Re-entrancy: The composer re-enters itself during flash loan execution
- Caller Forwarding: The caller address is forwarded from the original call source
- Pool Validation: The validation logic is hard-coded, so only a limited set of pools are allowed
- DEX Limitations: Some flash loan sources are DEXs (e.g., Balancer V2, Uniswap V4) - this means swaps through these DEXs are not possible during the flash loan due to re-entrancy protection
Solidity Example: USDC Loop in Aave V3
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
// select 1M USDC
uint128 amount = uint128(1000000.0e6);
// Inner operations: deposit USDC and borrow USDC
bytes memory innerOperations = abi.encodePacked(
// Deposit USDC to Aave V3
uint8(ComposerCommands.LENDING),
uint8(LenderOps.DEPOSIT),
uint16(AAVE_V3_ID),
USDC, // asset
amount, // amount
uint8(0), // interest rate mode (0 = variable)
// Borrow USDC from Aave V3
uint8(ComposerCommands.LENDING),
uint8(LenderOps.BORROW),
uint16(AAVE_V3_ID),
USDC, // asset
amount, // amount
uint8(0) // interest rate mode (0 = variable)
);
// Main flash loan operation
bytes memory operation = abi.encodePacked(
uint8(ComposerCommands.FLASH_LOAN),
uint8(FlashLoanIds.AAVE_V3),
USDC, // asset to borrow
AAVE_V3_POOL, // flash loan pool
amount, // amount to borrow
uint16(innerOperations.length + 1), // params length + 1
uint8(0), // poolId for validation
innerOperations // packed operations to execute
);
// Execute the flash loan
composer.deltaCompose(operation);
TypeScript Example (using viem)
export function encodeFlashLoanLoop(composerAddress: `0x${string}`, amount: bigint): `0x${string}` {
// Inner operations: deposit and borrow
const innerOperations =
encodePacked(
["uint8", "uint8", "uint16", "address", "uint128", "uint8"],
[
ComposerCommands.LENDING,
LenderOps.DEPOSIT,
AAVE_V3_ID,
USDC_ADDRESS,
amount,
0, // interest rate mode (variable)
]
) +
encodePacked(
["uint8", "uint8", "uint16", "address", "uint128", "uint8"],
[
ComposerCommands.LENDING,
LenderOps.BORROW,
AAVE_V3_ID,
USDC_ADDRESS,
amount,
0, // interest rate mode (variable)
]
).slice(2) // Remove '0x' prefix
// Main flash loan operation
const operation = encodePacked(
[
"uint8", // ComposerCommands.FLASH_LOAN
"uint8", // FlashLoanIds.AAVE_V3
"address", // asset
"address", // pool
"uint128", // amount
"uint16", // paramsLength + 1
"uint8", // poolId
"bytes", // inner operations
],
[
ComposerCommands.FLASH_LOAN,
FlashLoanIds.AAVE_V3,
USDC_ADDRESS,
AAVE_V3_POOL,
amount,
innerOperations.length / 2 + 1, // length in bytes + 1
0, // poolId
innerOperations,
]
)
return operation
}
// Usage example
const operationData = encodeFlashLoanLoop(
"0x...", // composer address
1000000n
)
// Call the composer
await publicClient.writeContract({
address: composerAddress,
abi: parseAbi(["function deltaCompose(bytes data)"]),
functionName: "deltaCompose",
args: [operationData],
})
Notes
Pool ID Validation
The poolId
parameter is crucial for security. Each flash loan provider has specific pool IDs:
- Morpho Blue: Pool ID
0
for the main Morpho Blue contract - Aave V3: Pool IDs vary by deployment (e.g.,
0
for mainnet) - Aave V2: Pool IDs vary by deployment (e.g.,
7
for Granary)
Re-entrancy Considerations
When using DEX-based flash loans (Balancer V2, Uniswap V4), be aware that:
- You cannot perform swaps on the same DEX during the flash loan
- Re-entrancy guards prevent nested operations on these protocols
- Use alternative DEXs for any swaps needed within the flash loan