Documentation Index
Fetch the complete documentation index at: https://docs.enso.build/llms.txt
Use this file to discover all available pages before exploring further.
Flashloans let you borrow tokens from a lending protocol without upfront collateral, execute a series of DeFi actions, and repay the debt — all within a single atomic transaction. If any step fails, the entire transaction reverts and no tokens are moved.
Flashloans are only available through the Bundle
API. They are not available via
the /route endpoint.
How Flashloans Work
- You choose the flashloan protocol by setting the action’s
protocol field, for example morpho, aave-v3, or balancer-v3
- You submit a
/bundle request containing a flashloan action with callback actions
- The API compiles the bundle into a single transaction
- When executed, the FlashloanAdapter requests a flash borrow from the selected lending protocol
- The lending protocol transfers the borrowed tokens to the wallet
- The callback actions execute sequentially (swaps, deposits, borrows, etc.)
- The borrowed amount plus any protocol fee is repaid to the lending protocol
- The transaction completes — or reverts entirely if repayment cannot be satisfied
Flashloans are atomic: if any callback action fails or the repayment cannot be
satisfied, the entire transaction reverts and no tokens are moved.
Parameters
| Parameter | Type | Description | Required |
|---|
flashloanToken | string | Address of the token to flash borrow | Yes |
flashloanAmount | string | Amount to flash borrow in wei (with full decimals) | Yes |
tokenIn | string[] | Addresses of additional tokens provided by the user as input | No |
amountIn | string | Amount of tokenIn provided by the user in wei | No |
tokenOut | string[] | Addresses of tokens expected as output after callback execution | No |
callback | Action[] | Array of actions to execute after receiving the flash-borrowed tokens | Yes |
receiver | string | Address to receive output tokens if not the caller | No |
The callback actions must produce enough tokens to repay the flash-borrowed
amount plus any protocol fee. If repayment cannot be satisfied, the entire
transaction will revert.
Supported Protocols
The user must select the flashloan protocol by using one of these slugs as the action’s protocol value.
{
"protocol": "morpho-markets-v1",
"action": "flashloan",
"args": {
"flashloanToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"flashloanAmount": "1000000000",
"tokenOut": ["0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"],
"callback": []
}
}
| Protocol | Slug | Fee |
|---|
| Morpho | morpho-markets-v1 | 0% |
| Bend (Morpho fork) | bend | 0% |
| Dolomite | dolomite | 0% |
| Aave V3 | aave-v3 | Dynamic (pool-specific, basis points from FLASHLOAN_PREMIUM_TOTAL) |
| Hyperlend (Aave V3 fork) | hyperlend | Dynamic (pool-specific) |
| Balancer V3 | balancer-v3 | Pool-specific fee |
| Uniswap V3 | uniswap-v3 | Pool-specific fee (0.05%, 0.3%, or 1% depending on pool tier) |
| Kodiak (Uniswap V3 fork) | kodiak | Pool-specific fee |
Chain Availability
Not all flashloan protocols are deployed on every chain. The table below shows which protocols are available on each supported chain.
| Chain | Aave V3 | Morpho | Balancer V3 | Dolomite | Uniswap V3 |
|---|
| Ethereum | ✅ | ✅ | ✅ | ✅ | ✅ |
| Arbitrum | ✅ | ✅ | ✅ | ✅ | ✅ |
| Base | ✅ | ✅ | ✅ | ✅ | ✅ |
| HyperEVM | ✅ (Hyperlend) | ✅ | ✅ | ❌ | ❌ |
| Optimism | ✅ | ✅ | ❌ | ❌ | ✅ |
| Polygon | ✅ | ✅ | ❌ | ❌ | ✅ |
| Berachain | ❌ | ✅ (Bend) | ❌ | ✅ | ✅ (Kodiak) |
| Sonic | ✅ | ✅ | ❌ | ❌ | ❌ |
| Binance | ✅ | ❌ | ❌ | ❌ | ✅ |
| Avalanche | ✅ | ❌ | ❌ | ❌ | ❌ |
| Plasma | ✅ | ❌ | ❌ | ❌ | ❌ |
| Ink | ❌ | ❌ | ❌ | ✅ | ❌ |
| Unichain | ❌ | ✅ | ❌ | ❌ | ❌ |
| World | ❌ | ✅ | ❌ | ❌ | ✅ |
| Soneium | ❌ | ✅ | ❌ | ❌ | ❌ |
| Plume | ❌ | ✅ | ❌ | ❌ | ❌ |
| Katana | ❌ | ✅ | ❌ | ❌ | ❌ |
| Monad | ❌ | ✅ | ❌ | ❌ | ❌ |
Where a variant is noted (e.g. Hyperlend, Bend, Kodiak), the flashloan uses a fork or alternative deployment of the underlying protocol on that chain.
Routing Strategies
Flashloans are supported across three routing strategies. Each strategy uses a dedicated adapter contract:
| Strategy | Description | Adapter Contract |
|---|
ensowallet-v2 | Smart contract wallet — tokens stay in the user’s wallet | EnsoWalletFlashloanAdapter |
router | EOA via EnsoRouter — intermediate tokens held by the router | EnsoRouterFlashloanAdapter |
safe | Gnosis Safe multisig wallet | EnsoSafeFlashloanAdapter |
See the Routing Strategies reference for details on choosing a strategy.
Examples
Morpho WETH/USDC Leveraged Position (Open Loop)
Open a leveraged WETH long against a Morpho Markets V1 (Morpho Blue) market in a single atomic transaction. The user starts with 1 ETH and ends with a Morpho position holding ~1.45 WETH as collateral and 1,800 USDC of debt. The flashloan provides the temporary USDC to swap into extra collateral; the Morpho borrow at the end repays the flashloan from the freshly opened debt position.
import { EnsoClient } from "@ensofinance/sdk";
const ETHEREUM_ID = 1;
const WALLET_ADDRESS = "0xd8da6bf26964af9d7eed9e03e53415d37aa96045";
// Tokens
const NATIVE_TOKEN = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
const WETH = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
const USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
// Morpho Blue WETH/USDC market on Ethereum
const MORPHO_BLUE = "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb";
const WETH_USDC_MARKET_ID =
"0x94b823e6bd8ea533b4e33fbc307faea0b307301bc48763acc4d4aa4def7636cd";
const client = new EnsoClient({ apiKey: process.env.ENSO_API_KEY! });
const bundle = await client.getBundleData(
{
chainId: ETHEREUM_ID,
fromAddress: WALLET_ADDRESS,
receiver: WALLET_ADDRESS,
routingStrategy: "ensowallet-v2",
},
[
// Step 1: Wrap 1 ETH → WETH (will be combined with the swapped flashloan amount)
{
protocol: "wrapped-native",
action: "deposit",
args: {
primaryAddress: WETH,
tokenIn: NATIVE_TOKEN,
amountIn: "1000000000000000000", // 1 ETH
tokenOut: WETH,
},
},
// Step 2: Flashloan 1,800 USDC from Morpho and use it to open the leveraged position
{
protocol: "morpho-markets-v1",
action: "flashloan",
args: {
flashloanToken: USDC,
flashloanAmount: "1800000000", // 1,800 USDC (6 decimals)
tokenIn: WETH,
amountIn: { useOutputOfCallAt: 0 }, // 1 WETH from the wrap above
tokenOut: USDC,
callback: [
// 2a: Swap the flashloaned 1,800 USDC into WETH
{
protocol: "enso",
action: "route",
args: {
tokenIn: USDC,
amountIn: "1800000000",
tokenOut: WETH,
slippage: 300, // 3%
},
},
// 2b: Read the combined WETH balance (user's 1 WETH + swapped WETH)
{
protocol: "enso",
action: "balance",
args: { token: WETH },
},
// 2c: Deposit all WETH as collateral in the Morpho WETH/USDC market
{
protocol: "morpho-markets-v1",
action: "deposit",
args: {
primaryAddress: MORPHO_BLUE,
positionId: WETH_USDC_MARKET_ID,
tokenIn: WETH,
amountIn: { useOutputOfCallAt: 1 },
onBehalfOf: WALLET_ADDRESS,
},
},
// 2d: Borrow 1,800 USDC against the new collateral — this repays the flashloan
{
protocol: "morpho-markets-v1",
action: "borrow",
args: {
primaryAddress: MORPHO_BLUE,
positionId: WETH_USDC_MARKET_ID,
collateral: WETH,
tokenOut: USDC,
amountOut: "1800000000",
onBehalfOf: WALLET_ADDRESS,
},
},
],
},
},
],
);
Pick the flashloan asset to match the final borrow. Flashloaning USDC and
ending the callback with a USDC borrow lets the borrowed USDC repay the
flashloan directly — no extra swap needed at the end of the callback.
Alchemix AlchemistV3 alUSD Loop (Morpho Flashloan)
Open an AlchemistV3 alUSD position using a Morpho USDC flashloan. The user contributes 10 MIXUSD; the flashloan brings in 15 USDC of “synthetic” collateral that gets wrapped into 15 MIXUSD inside the callback. The combined 25 MIXUSD becomes AlchemistV3 collateral, against which 17 alUSD is minted and swapped back into the USDC needed to repay the flashloan.
alUsdLoopMorphoFlashloan.ts
import { EnsoClient } from "@ensofinance/sdk";
const ETHEREUM_ID = 1;
const WALLET_ADDRESS = "0xd8da6bf26964af9d7eed9e03e53415d37aa96045";
const USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
const MIXUSD = "0x9B44efCa3e2a707B63Dc00CE79d646E5E5D24bA5"; // alchemix-myts USDC
const ALUSD = "0xbc6da0fe9ad5f3b0d58160288917aa56653660e9";
const ALCHEMIST_V3_USD = "0xeB83112d925268BeDe86654C13D423a987587e3E";
const client = new EnsoClient({ apiKey: process.env.ENSO_API_KEY! });
const bundle = await client.getBundleData(
{
chainId: ETHEREUM_ID,
fromAddress: WALLET_ADDRESS,
routingStrategy: "router",
},
[
{
protocol: "morpho-markets-v1",
action: "flashloan",
args: {
flashloanToken: USDC,
flashloanAmount: "15000000", // 15 USDC (6 decimals)
tokenIn: [MIXUSD],
amountIn: ["10000000000000000000"], // 10 MIXUSD provided by the user
tokenOut: [USDC],
callback: [
// 1: Wrap the 15 flashloaned USDC into MIXUSD via alchemix-myts
{
protocol: "alchemix-myts",
action: "deposit",
args: {
primaryAddress: MIXUSD,
tokenIn: USDC,
tokenOut: MIXUSD,
amountIn: "15000000",
},
},
// 2: Read total MIXUSD (user's 10 + freshly minted 15)
{
protocol: "enso",
action: "balance",
args: { token: MIXUSD },
},
// 3: Open an AlchemistV3 NFT position with the combined MIXUSD
{
protocol: "alchemix-vaults-v3",
action: "deposit",
args: {
primaryAddress: ALCHEMIST_V3_USD,
tokenIn: MIXUSD,
tokenOut: ALCHEMIST_V3_USD,
amountIn: { useOutputOfCallAt: 1 },
tokenId: "0", // 0 = mint a new position
receiver: "0x0000000000000000000000000000000000000000",
},
},
// 4: Borrow 17 alUSD against the newly minted NFT
{
protocol: "alchemix-vaults-v3",
action: "borrow",
args: {
primaryAddress: ALCHEMIST_V3_USD,
collateral: ALUSD,
tokenOut: ALUSD,
amountOut: "17000000000000000000", // 17 alUSD
tokenId: { useOutputOfCallAt: 2, index: 0 },
},
},
// 5: Swap 17 alUSD → USDC to repay the flashloan
{
protocol: "enso",
action: "route",
args: {
tokenIn: [ALUSD],
amountIn: { useOutputOfCallAt: 3 },
tokenOut: [USDC],
slippage: "100", // 1%
},
},
],
},
},
],
);
tokenIn / amountIn on the flashloan action: these are user-contributed
assets that flow into the callback alongside the flashloaned funds — tokenIn
accepts a single address or an array, and amountIn accepts a single amount,
an array, or a useOutputOfCallAt reference to an earlier action’s output.
Uniswap V3 Flashloan Opening an Aave V3 Leveraged Position (eMode)
Open a leveraged Aave V3 position inside a Uniswap V3 flashloan callback on MegaETH. The user contributes 1 USDm collateral and flashloans 1 USDe from a specific Uniswap V3 pool; inside the callback the flashloan is swapped to USDm, deposited as collateral, and Aave V3 mints 1.503 USDe debt (slightly more than 1 to cover the pool fee) to repay the flashloan.
uniswapV3FlashloanAaveLeverage.ts
import { EnsoClient } from "@ensofinance/sdk";
const MEGAETH_ID = 4326;
const WALLET_ADDRESS = "0xc95E8A0c36aBCFB7f436417DF2f1875e6642ad7D";
// Tokens on MegaETH
const USDE = "0xFAfDdbb3FC7688494971a79cc65DCa3EF82079E7"; // flashloan asset (= Aave debt asset)
const USDM = "0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34"; // user input + Aave collateral
const AUSDM = "0x78f2cB75D664d6f71433174056c25A5958B4016F"; // Aave aToken receipt
// Uniswap V3 pool used as the flashloan source and the Aave V3 pool
const UNI_V3_POOL = "0x587F6eeAfc7Ad567e96eD1B62775fA6402164b22";
const AAVE_V3_POOL = "0x7e324AbC5De01d112AfC03a584966ff199741C28";
const client = new EnsoClient({ apiKey: process.env.ENSO_API_KEY! });
const bundle = await client.getBundleData(
{
chainId: MEGAETH_ID,
fromAddress: WALLET_ADDRESS,
receiver: WALLET_ADDRESS,
spender: WALLET_ADDRESS,
routingStrategy: "ensowallet-v2",
},
[
{
protocol: "uniswap-v3",
action: "flashloan",
args: {
primaryAddress: UNI_V3_POOL, // Uniswap V3 pool to flashloan from
flashloanToken: USDE,
flashloanAmount: "1000000000000000000", // 1 USDe
tokenIn: USDM,
amountIn: "1000000000000000000", // 1 USDm contributed by the user
tokenOut: USDE,
receiver: WALLET_ADDRESS,
callback: [
// 1: Swap the flashloaned 1 USDe into USDm
{
protocol: "enso",
action: "route",
args: {
tokenIn: USDE,
tokenOut: USDM,
amountIn: "1000000000000000000",
},
},
// 2: Read combined USDm balance (user's 1 + swapped)
{
protocol: "enso",
action: "balance",
args: { token: USDM },
},
// 3: Deposit USDm as collateral into Aave V3
{
protocol: "aave-v3",
action: "deposit",
args: {
primaryAddress: AAVE_V3_POOL,
tokenIn: USDM,
tokenOut: AUSDM,
amountIn: { useOutputOfCallAt: 1 },
onBehalfOf: WALLET_ADDRESS,
},
},
// 4: Borrow 1.503 USDe — repays the flashloan + Uniswap V3 pool fee
{
protocol: "aave-v3",
action: "borrow",
args: {
primaryAddress: AAVE_V3_POOL,
collateral: USDM,
tokenOut: USDE,
amountOut: "1503000000000000000", // 1.503 USDe
onBehalfOf: WALLET_ADDRESS,
args: {
eModeCategoryId: "7", // required by Aave V3 for this borrow path
},
},
},
],
},
},
],
);
Uniswap V3 flashloans require primaryAddress: unlike Morpho or Aave V3
(where the lender is global), Uniswap V3 flashloans borrow from a specific
pool. Pass the pool address as primaryAddress and pay the pool’s fee tier
(0.05% / 0.3% / 1%) when repaying — over-borrow slightly in the final borrow
action to cover it.
Aave V3 eMode: when the borrow target sits in an eMode category, pass
args.eModeCategoryId on the aave-v3.borrow action. Enso emits the matching
setUserEMode approval automatically.