n times. It turns a supply APY rs and a borrow cost rb into
f(L) = L·rs − (L−1)·rb, linear in leverage L.
This endpoint answers which pairs exist and roughly what they pay. It does not tell you how
much leverage to run, and it does not publish a loop APY.
L follows from a risk budget, a gross-exposure cap, a health-factor floor and the rest of a
book — none of which this API can see, and two consumers must be able to choose different L
from the same row. f(L) additionally depends on whether an emission-priced reward counts, which
is a judgement, not a venue fact. The API returns the frontier; the caller picks the point.List Loopable Pairs
GET/loops
One row per (collateral, debt) pair inside one lending pool, on one chain. The set is materialized
by the daily sync and served from cache: no chain reads, and no pagination — the whole screen comes
back in one response.
Query Parameters
number
Restrict to one chain, e.g.
143.string
Instrument ID of the supplied leg.
string
Instrument ID of the borrowed leg.
[] rather than an error.
Request
Response
Reading the row
reward.basis — read this before spending apyReward
reward.basis — read this before spending apyReward
emission means the reward APY is valued at the emission schedule and the token’s quoted price,
not at what a position could actually sell into. That is how the upstream number is built, and on
a thin reward token the two diverge, and leverage multiplies the gap. The error is directional —
it floats reward-heavy pairs to the top of any ranking — so discount the number rather than
inherit it.none means neither leg pays a reward. traded is reserved for a reward APY derived from an
executable quote and is not produced by this endpoint; a stream that could not be priced reports
emission, never something more optimistic.reward.liquidityUsd — where capacity actually binds
reward.liquidityUsd — where capacity actually binds
24h traded volume of the thinnest reward stream on the row, and
null when any of them is
unmeasured. null is unknown, not zero: the two must not be collapsed, because one is a reason to
size down and the other is a reason to look again.Compare it with emissionUsdPerDay. A stream emitting more per day than its token trades per day
has no exit at size, whatever the APY says.leverage.ltv — the pool default, not this pair's
leverage.ltv — the pool default, not this pair's
basis: "defillama-pool-default" is a warning, not decoration: the figure carries no e-mode, no
per-collateral factor and no per-market LLTV, and the gap between a default and an effective LTV
is the gap between very different ceilings. Size the screen with it; read the effective figure on
chain before anything is signed.sameAsset — a second risk axis when false
sameAsset — a second risk axis when false
A cross-asset loop needs a swap at per-turn notional, so its cost grows with
L and its quote can
go stale. Its health factor also drifts with the collateral/debt price ratio, not only with
interest: HF = (collateral × liqThreshold) / (debt × r), so liquidation arrives at r = HF₀ and
the divergence budget is exactly HF₀ − 1. A same-asset loop at HF = 1.03 is fine; a
cross-asset one at HF = 1.03 is three percent of price divergence from liquidation, and assets
that are supposed to track each other have moved further than that.What this screen cannot see
What this screen cannot see
- Routes. Nothing here knows whether a swap route exists at per-turn notional. A pair can screen well on rates and have no venue that will fill the turns.
- Oracles. Liquidation is decided by the pool’s oracle, not by market price.
- Freshness. Rates go stale intraday by design. This endpoint decides what to look at.
How a row is built
The supply side of each pair is the collateral instrument’s own snapshot — the same numbers/instruments serves — and the borrow side comes from DeFiLlama’s /lendBorrow, joined on the
pool id the two share. A reserve missing from /lendBorrow is not lending-shaped and is never
paired. A pair whose debt leg is not a listed instrument is omitted rather than synthesised, and a
collateral leg the venue gives no LTV (ltv = 0) can never start a loop however good its supply
rate looks.
loopId is deterministic from (chainId, pool, collateralInstrumentId, debtInstrumentId). The
order of the legs is part of it: supplying USDC to borrow AUSD and supplying AUSD to borrow USDC
are different trades, and on the measured snapshot one of them had a negative gradient. Leverage is
deliberately not part of it — L is a request parameter, so there is one row per pair rather than
one per (pair, L).
Build a Loop Bundle
GET/loops/:loopId/calldata
The tier-2 read. The screen said a pair exists; this re-reads it on chain at a pinned quote block,
builds every turn of the loop as one ordered batch for the account to sign, simulates the batch
atomically, and reports the position the simulation left behind. It is the same envelope as
/instruments/:id/calldata — calls, requires, leftovers, minOut, expiresAt, simulation
— plus a leverage block (the venue fact) and a simulated block (the measurement).
It still does not choose L. leverage is a request parameter, and the API builds whatever
reachable leverage it is asked for.
Query Parameters
string
required
open supplies amount and levers it to leverage. adjust moves an open position to
leverage with its equity unchanged — up or down. close unwinds fully, to zero debt, in one
batch.string
required
The smart account that supplies, borrows and receives.
string
Target collateral ÷ equity at the pool’s oracle, e.g.
4 or 3.25. Required for open and
adjust; rejected for close.string
Equity to supply, in raw units of the collateral token. Required for
open, rejected otherwise.
max is never accepted: every amount in a loop is hard-coded into the batch.string
default:"auto"
auto picks the e-mode category that admits both legs with the highest LTV; none keeps the
reserve’s default parameters; a number forces that category and is refused if it does not admit
the pair. close ignores it and unwinds under the account’s current category.number
default:"50"
Taken off each swap’s quote to size its exact output. What a swap does not spend stays in the
wallet, listed in
leftovers.number
default:"100"
Refuse a route whose quote, at the loop’s cumulative notional, is further than this below the
pool’s oracle.
Request
Response
A USDC/AUSD pair under e-mode, calls truncated:Reading the bundle
leverage — effective parameters, never the screen's
leverage — effective parameters, never the screen's
ltv and liquidationThreshold are what the pool will apply to this pair after the bundle — the
e-mode category’s where the account will be in one — read at the quote block. paramsBasis says
which (emode:<id> or default). A pair whose effective parameters cannot be read fails closed
rather than falling back to the screen’s pool default, and after simulating, the recipe reads the
account’s LTV and threshold back from the pool: if they disagree with what it planned under, the
bundle is refused.requiresAccountConfig — the bundle changes account-wide state
requiresAccountConfig — the bundle changes account-wide state
Aave’s e-mode is set per account per pool, not per position. When this is
true the bundle
contains a set_account_config call, and every other position the account holds in that pool
is re-priced by it — including an unlevered supply it has nothing to do with. Check the book before
signing. A close that leaves no other debt in the pool clears the category back to 0.simulated — measured, not modelled
simulated — measured, not modelled
Read back from the simulated block after the batch ran.
collateral/debt and leverage are
this pair’s; healthFactor and the total*Base figures are the pool’s own account-level
accounting, so they include anything else the account holds there. depegBufferBps is
(HF − 1) — the adverse move in the debt/collateral price ratio the position survives — for a
cross-asset pair only. The requested leverage is hit on the position itself: swap costs come out
of equity, not out of the health factor.turns — hard-coded, exact-output
turns — hard-coded, exact-output
Each turn is
borrow · swap · supply (borrow · supply for a same-asset pair; withdraw · swap · repay on the way down). Every amount is computed at the quote block and written into the
calldata; every swap is exact-output, so the supply after it is known. Each borrow keeps 25 bps of
headroom under the LTV and each intermediate withdrawal 50 bps above the liquidation threshold.
A full unwind buys 10 bps more debt asset than the quote-block debt and repays with max, so
interest accrued before it lands is cleared too.Swaps are quoted cumulatively through one pool fixed for the bundle: turn k is priced after the
turns before it, and the route is chosen — Uniswap V3 or the pinned V4 pool, whichever fills the
whole loop better — at the loop’s full notional, not at one turn of it.Errors
Errors
400—leverageat or above the pair’s effective ceiling (named, with its basis); no liquid route at the loop’s size; an e-mode that does not admit the pair;openover existing debt on the pair, orclose/adjustwith none; a simulation that reverted or left debt after a full unwind.501— no loop recipe for this venue.aave-v3-loopserves Aave V3 and its forks; Comet, Morpho Blue and Euler loops are separate recipes, not yet built.503— chain state, the swap quote or the simulation was unavailable.