Skip to main content

Leverage (loop)

GET 

/v1/actions/loop/leverage

Open a leveraged position via borrow → swap → deposit. Identify the debt market with marketUidIn and collateral market with marketUidOut. Omit account for quote-only (returns data.quotes with price deltas). Include account to build full transaction calldata (populates actions with alternatives, transactions, and permissions).

Response actions fields (when account is provided):

  • alternatives: DEX aggregator swap transactions sorted best-output-first. Pick one to execute.
  • transactions: Pre-trade setup — mode/e-mode switch (Aave V3) or collateral enable (Venus). Empty if not needed.
  • permissions: ERC20 approvals (targeting the composer contract) and lender borrow delegations (targeting the lending protocol contract). Only missing approvals are returned. Execute these first.

Paying the margin: which payAsset a pair accepts is lender-specific — GET /v1/actions/loop/leverage/pay-assets lists them for the same two market uids, and an asset outside that list is refused here as PAY_ASSET_UNSUPPORTED (with the accepted set in details.payAssets) before anything is quoted.

Lista DAO fixed-term (brokered) markets: when marketUidIn is brokered, pass termId to open the loop's debt leg as a fixed-term broker loan. The collateral deposit leg is handled automatically (routed through the market's collateralProvider when set, e.g. slisBNB). Closing such a loop requires the loan's loanId on /v1/actions/loop/close.

Trade economics (data.quotes[i].economics, best route mirrored at data.economics): a TradeEconomics block per route, so a client never re-derives an entry cost or a break-even from the deltas. It carries entryCostUsd (swap slippage vs the oracle, day-0 fees off the debt leg's term sheet + the flash fee, gas over the outstanding permissions + main transaction), carryPerDayUsd at the PROJECTED rates the same IRM path behind rateImpact produces (with and without incentives — carryBasis says sticker when the projection was unavailable), breakEvenDays, roundTripDays (the exit ASSUMED symmetric to the entry until a close is quoted — closeQuoted: false; null on a debt with no early exit), the trade's clock (horizonDays: PT expiry, debt maturity, rolling term) and breaksEvenBeforeHorizon. With an account, existingExposure makes it MARGINAL to the book the trade lands in: the debt the account already holds in the same market reprices with the new borrow, and that cost is netted out of the carry. Whatever could not be computed is null with a line in notes — the block itself is never omitted. Gas is a real eth_estimateGas when it succeeds (gas.estimated), a typical-size table otherwise; on a quote-only request it is not priced.

Plain-text reference — GET /v1/actions/loop/leverage

Parameters

ParameterInTypeRequiredDescription
routequeryauto, bundler3, native, composer, proxynoWhich assembly to use, for lenders that have more than one. Morpho Blue only today.
  • (omitted) — auto: prefer the protocol’s own tooling and fall back to the composer for anything it cannot express (a cross-market pair, a margin paid in a foreign asset, an EXACT_OUTPUT close, a chain with no bundler3 or no swap adapter).
  • bundler3 / native — force Morpho’s bundler3. A request it cannot serve becomes an ERROR instead of silently falling back, which is what makes the route testable.
  • composer / proxy — force the 1delta composer.

The default matters: the bundler3 route asks for setAuthorization(GeneralAdapter1), Morpho’s own audited adapter, where the composer route asks for a standing grant on ours. | | marketUidIn | query | string | yes | Market identifier for input side (lender:chainId:address). | | marketUidOut | query | string | yes | Market identifier for output side (lender:chainId:address). | | slippage | query | number | yes | Slippage tolerance (basis points) | | account | query | string | no | Account address. Include to build transaction, omit for quote-only. | | debtAmount | query | string | yes | Debt amount in wei | | payAsset | query | string | no | Asset the margin is paid in (optional zap-in asset). Which assets a given pair accepts is LENDER-SPECIFIC — the collateral, the debt, and native when the wrapped native is one of the two on the composer lenders; collateral only on Curvance / Fraxlend / Resupply / Twyne; no native on LlamaLend; the debt asset only on Exactly; the collateral (or native into a WETH collateral) on Flying Tulip, whose open is a signed flow: answers carry one actions.signs[0] step each until none is owed. Ask GET /v1/actions/loop/leverage/pay-assets with the same marketUidIn / marketUidOut to list them in advance. An asset outside that list is refused up front with PAY_ASSET_UNSUPPORTED, details.payAssets carrying the accepted set (Twyne excepted — it answers ZAP_REQUIRED with a swap quote instead). Native is the zero address. | | payAmount | query | string | no | Pay amount in wei | | hedge | query | boros | no | boros — attach data.hedge: the Pendle Boros SHORT that locks the perp FUNDING the new collateral earns (sUSDe, wstUSR …) at a fixed rate, sized on the best route’s collateral USD × hedgeRatio. A second set of transactions on ARBITRUM (42161), never part of the loop bundle and not atomic with it; available: false + reason when the collateral earns no funding. Tuning: hedgeRatio (default 1), hedgeTenor (longest|nearest), hedgeExposure (ethena|resolv), hedgeMarketIds, hedgeMarginBuffer, hedgeSlippage (bps of APR). See /v1/actions/boros/hedge. | | marginFromAccountId | query | integer | no | Dolomite only. Fund the margin from this existing Dolomite sub-account instead of the wallet, collapsing the open to ONE transaction: the transfer rides inside the leverage call and no deposit or token approval is emitted. Omit to pull payAmount from the wallet (two transactions — Dolomite's trader proxy can only move funds between the caller's own sub-accounts). 0 is the default sub-account and a valid source. Passing the trade account's own id is treated as absent, since the margin is already there. The source must hold payAmount of the pay asset or the transaction reverts. | | leverage | query | number | no | Target leverage multiplier | | usePendleMintRedeem | query | boolean | no | Use Pendle mint/redeem | | borrowMode | query | 0, 1, 2 | no | Borrow mode (0=NONE, 1=STABLE, 2=VARIABLE) | | termId | query | integer | no | Lista DAO fixed-term (brokered) debt markets only. Selects the fixed term to borrow for the loop's debt leg, from the debt market's terms[] rate card (the MarketTerm.termId). Required when marketUidIn is a brokered market — the borrow leg routes through the broker; ignored otherwise. | | posId | query | string | no | Twyne only. Which collateral vault to lever — a Twyne position is a contract the borrower deploys, and one account may own several in a market, each with its own collateral, debt and liquidation LTV. Omit when the account owns exactly one (or none). Passing none while owning several is refused rather than guessed. | | liqLtv | query | integer | no | Twyne only, and only when the account owns NO vault in this market yet. The liquidation LTV (1e4 scale) the position this call DEPLOYS will carry. The deploy runs as the first item of the same EVC batch, so a first-time borrower opens a leveraged position in one transaction; the response then carries createdPosition and a collateralVault address that does not exist until the transaction lands.

Defaults to the market’s LIVE ceiling (maxTwyneLTVs) — the extra LTV is the reason to borrow here, and the value is not a commitment: setTwyneLiqLTV moves it at any time with no cooldown, and lowering it RELEASES reserved credit and lowers what the borrower pays. Must sit inside the live band (externalLiqLTV × externalLiqBuffer … maxTwyneLTVs); both bounds move, so a value is validated against a fresh read and refused by name rather than reverting ValueOutOfRange() on chain.

Ignored when a position already exists — moving an existing dial is /v1/actions/twyne/set-liq-ltv. | | accountId | query | string | no | Account ID (Init Capital, Euler V2, Dolomite). Dolomite: a uint256 account number, or iso:<marketId>:<n> for a position inside an isolation-mode vault (dGM, dsavETH, dGMX …); borrow positions on those need n >= 100, account 0 is the supply-only vault balance. | | selectedMode | query | integer | no | Position mode for new positions (Init Capital) |

Response 200

FieldTypeDescription
successTrue
dataobjectInformational data (quotes, simulation results, etc.)
data.lenderstringProtocol identifier
data.quotesobject[]Candidate routes, best output first. Execute exactly one.
data.quotes[].deltasobject
data.quotes[].deltas.aggregatorstringAggregator source
data.quotes[].deltas.tradeInputnumberTrade input amount
data.quotes[].deltas.tradeOutputnumberTrade output amount
data.quotes[].deltas.deltasobjectBalance deltas
data.quotes[].rateImpactobject[]Projected interest-rate impact per market for THIS quote (its own trade amounts). Omitted if IRM data is unavailable.
data.quotes[].rateImpact[].marketUidstringMarket identifier (format: lender:chainId:address)
data.quotes[].rateImpact[].utilizationobjectA current/projected pair for a single rate metric.
data.quotes[].rateImpact[].borrowRateobjectA current/projected pair for a single rate metric.
data.quotes[].rateImpact[].depositRateobjectA current/projected pair for a single rate metric.
data.quotes[].rateImpact[].intrinsicYieldnumberThe asset's own yield in percent (LST staking yield, RWA NAV growth, a Gearbox quota rate as a negative), size-invariant, ADDED to both sides: earned on a deposit, forgone on a borrow. Absent when the lender meta was not readable when the response was built — never defaulted to 0 in that case.
data.quotes[].rateImpact[].depositRewardAprnumberSum of reward-stream APRs (%) on the deposit side. Absent with intrinsicYield.
data.quotes[].rateImpact[].borrowRewardAprnumberSum of reward-stream APRs (%) rebated on the borrow side. Absent with intrinsicYield.
data.quotes[].rateImpact[].effectiveDepositRateobjectA current/projected pair for a single rate metric.
data.quotes[].rateImpact[].effectiveBorrowRateobjectA current/projected pair for a single rate metric.
data.quotes[].rateImpact[].flatbooleanTrue when the market has no utilization curve to shift (Morpho Blue collateral, a fixed-rate broker): current === projected, taken from the published rates, and utilization is NaN. Present so the leg is not silently missing from a position preview.
data.quotes[].netAprobjectThe trade's carry at post-trade rates — the position net APR. annualUsd is the change in yearly carry the trade causes (deposit/withdraw legs earn, borrow/repay legs cost); aprPct puts it on the net equity the trade moves (the margin on an open) and is null when that equity is not positive (a pure leverage step, a swap). The bare figures exclude rewards; *Total include them.
data.quotes[].netApr.annualUsdnumber
data.quotes[].netApr.annualUsdTotalnumber
data.quotes[].netApr.equityUsdnumber
data.quotes[].netApr.aprPctnumber
data.quotes[].netApr.aprPctTotalnumber
data.quotes[].netApr.legsobject[]
data.quotes[].economicsobjectTHIS route's trade economics — TradeEconomics on an open, CloseEconomics on a close. See the schema for the discipline.
data.quotes[].economics.notionalUsdobjectThe position this route creates, at the oracle. equity is the margin the user brings (0 on a pure leverage step).
data.quotes[].economics.entryCostUsdobjectDay-0 cost of getting in. slippage = swap input at the oracle − swap output at the oracle (floored at 0; slippageBp keeps the sign). gas is null when it could not be priced.
data.quotes[].economics.slippageBpnumberSigned swap-vs-oracle, BP of the swap input. Negative = the route costs money.
data.quotes[].economics.entryCostBpnumberentryCostUsd.total over the DEBT notional, BP.
data.quotes[].economics.feesobject[]
data.quotes[].economics.carryPerDayUsdobjectThe trade's carry per day at PROJECTED rates — what it earns after its own borrow has moved the pool. total counts incentives, base does not. With an account, the repricing of the existing book is netted out.
data.quotes[].economics.carryBasisprojected, sticker, none
data.quotes[].economics.breakEvenDaysobjectentryCostUsd.total / carryPerDayUsd. Null when the carry is not positive (never pays for itself — see notes) or unknown.
data.quotes[].economics.roundTripDaysobject(entry + exit cost) / carry. closeQuoted says whether the exit was PRICED (a real close quote) or ASSUMED symmetric to the entry (the default today). Null on a debt that cannot be repaid early (Midnight): the round trip is at maturity, not before.
data.quotes[].economics.horizonDaysnumberDays until the trade's clock runs out. Null = perpetual.
data.quotes[].economics.horizonobject
data.quotes[].economics.breaksEvenBeforeHorizonboolean

Request​

Responses​

Quote or full build response