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.
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.
Plain-text reference — GET /v1/actions/loop/leverage
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
route | query | auto, bundler3, native, composer, proxy | no | Which 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, anEXACT_OUTPUTclose, 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 to pay with (optional zap-in asset) |
| payAmount | query | string | no | Pay amount in wei |
| 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) |
| selectedMode | query | integer | no | Position mode for new positions (Init Capital) |
Response 200
| Field | Type | Description |
|---|---|---|
success | True | |
data | object | Informational data (quotes, simulation results, etc.) |
data.lender | string | Protocol identifier |
data.quotes | object[] | Candidate routes, best output first. Execute exactly one. |
data.quotes[].deltas | object | |
data.quotes[].deltas.aggregator | string | Aggregator source |
data.quotes[].deltas.tradeInput | number | Trade input amount |
data.quotes[].deltas.tradeOutput | number | Trade output amount |
data.quotes[].deltas.deltas | object | Balance deltas |
data.quotes[].rateImpact | object[] | Projected interest-rate impact per market for THIS quote (its own trade amounts). Omitted if IRM data is unavailable. |
data.quotes[].rateImpact[].marketUid | string | Market identifier (format: lender:chainId:address) |
data.quotes[].rateImpact[].utilization | object | A current/projected pair for a single rate metric. |
data.quotes[].rateImpact[].borrowRate | object | A current/projected pair for a single rate metric. |
data.quotes[].rateImpact[].depositRate | object | A current/projected pair for a single rate metric. |
data.rateImpact | object[] | Projected interest-rate impact per market. Single-market actions produce 1 entry; loop actions produce 2. Null if IRM data is unavailable. |
data.rateImpact[].marketUid | string | Market identifier (format: lender:chainId:address) |
data.rateImpact[].utilization | object | A current/projected pair for a single rate metric. |
data.rateImpact[].utilization.current | number | Current value |
data.rateImpact[].utilization.projected | number | Projected value after the action |
data.rateImpact[].borrowRate | object | A current/projected pair for a single rate metric. |
data.rateImpact[].borrowRate.current | number | Current value |
data.rateImpact[].borrowRate.projected | number | Projected value after the action |
data.rateImpact[].depositRate | object | A current/projected pair for a single rate metric. |
data.rateImpact[].depositRate.current | number | Current value |
data.rateImpact[].depositRate.projected | number | Projected value after the action |
data.lender | string | Protocol identifier |
data.quotes | object[] | Candidate routes, best output first. Execute exactly one. |
data.quotes[].deltas | object | |
data.quotes[].deltas.aggregator | string | Aggregator source |
data.quotes[].deltas.tradeInput | number | Trade input amount |
data.quotes[].deltas.tradeOutput | number | Trade output amount |
data.quotes[].deltas.deltas | object | Balance deltas |
data.quotes[].tx | object | An EVM transaction ready to sign and broadcast. Send to, data and value as-is; do not re-encode them. |
data.quotes[].tx.to | string | Target contract address |
data.quotes[].tx.data | string | Encoded calldata |
data.quotes[].tx.value | string | ETH value to send with the transaction |
data.quotes[].tx.description | string | Human-readable label. For alternatives, this is the aggregator name (e.g. "Paraswap"). For transactions, describes the setup action (e.g. "Switch e-mode to 1"). |
data.quotes[].rateImpact | object[] | Projected interest-rate impact per market for THIS quote (its own trade amounts). Omitted if IRM data is unavailable. |
data.quotes[].rateImpact[].marketUid | string | Market identifier (format: lender:chainId:address) |
data.quotes[].rateImpact[].utilization | object | A current/projected pair for a single rate metric. |
Example response
{
"success": true,
"data": {
"lender": "AAVE_V3",
"quotes": [
{
"deltas": {
"aggregator": "string",
"tradeInput": 1.0,
"tradeOutput": 1.0,
"deltas": {}
},
"rateImpact": [
{
"marketUid": "AAVE_V3:8453:0x4200000000000000000000000000000000000006",
"utilization": {
"current": 1.0,
"projected": 1.0
},
"borrowRate": {
"current": 1.0,
"projected": 1.0
},
"depositRate": {
"current": 1.0,
"projected": 1.0
}
}
]
}
],
"rateImpact": [
{
"marketUid": "AAVE_V3:8453:0x4200000000000000000000000000000000000006",
"utilization": {
"current": 1.0,
"projected": 1.0
},
"borrowRate": {
"current": 1.0,
"projected": 1.0
},
"depositRate": {
"current": 1.0,
"projected": 1.0
}
}
]
},
"actions": {
"transactions": [
{
"to": "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2",
"data": "0x617ba037000000000000000000000000c02aaa39b2",
"value": "0",
"description": "string"
}
],
"alternatives": [
{
"to": "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2",
"data": "0x617ba037000000000000000000000000c02aaa39b2",
"value": "0",
"description": "string"
}
],
"permissions": [
{
"to": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"data": "0x617ba037000000000000000000000000c02aaa39b2",
"value": "0",
"description": "string",
"spender": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
}
]
}
}
Request
Responses
- 200
- 400
- 429
- 500
- 502
Quote or full build response
Validation error
Rate limited. Unauthenticated callers share a per-IP budget; send an x-api-key header to lift it. Retry with exponential backoff.
Unexpected server error. Safe to retry with backoff.
An upstream data source or protocol origin failed (error.code is ORIGIN_FAILED). error.details carries the per-origin status. This is also what a missing or malformed required parameter currently returns, rather than a 400.