Skip to main content

Migration targets for a position

GET 

/v1/actions/loop/migrate/targets

Which markets can this position migrate to? The discovery companion to /v1/actions/loop/migrate — it returns the ranked set of destinations that endpoint would actually accept, so a client never encodes protocol knowledge to build that list.

Give it the two marketUids of an existing position (and, ideally, its size) and it does everything a caller would otherwise hand-roll:

  • Native ⇄ wrapped-native equivalence. Some lenders key a market to the native asset (Fluid ETH vaults), others to the wrapped ERC20 (Aave WETH); a migrate bridges them by wrapping. Selection is by shared assetGroup, so both forms surface in BOTH directions on every chain — no per-chain WETH table anywhere in the client.
  • The migrate support matrix. Lenders that cannot be a target are dropped (Dolomite, Euler, Curvance, LlamaLend and non-Venus Compound V2 forks today). This is the same code path /migrate validates with, so the list cannot drift out of sync with what builds.
  • The native-debt rule. On-behalf borrowing cannot be delegated for the native asset, so native-debt markets are dropped unless the move is a wrap conversion into Fluid.
  • Un-openable fixed-term books. An order-book lender (Morpho Midnight) reports a 0% variable rate and no terms in the pair feed whether its book is rich or empty. Each fixed-term candidate's real borrow term is resolved and the ones that cannot be opened are dropped — 10 of 12 Midnight borrow legs on Base at time of writing. Survivors get termsShort back-filled with the true rate + maturity.
  • Borrow liquidity at this position's size, applied BEFORE truncation (a client-side filter after paging silently loses real destinations to rows it then discards).

Rows come back in the /v1/data/lending/pairs/optimize shape, so an existing pair normaliser keeps working, plus a migrate block per row:

"migrate": {
"healthFactor": 1.87, // collateralUsd · liquidationThreshold / debtUsd on THIS target
"netApr": 0.041, // equity-weighted earn − pay for THIS position (not max-leverage aprTotal)
"depositApr": 0.031, // effective rates behind netApr (intrinsic yield folded in)
"borrowApr": 0.058,
"borrowLiquidityUsd": 6470141,
"sufficientLiquidity": true,
"maturity": 1787929200, // fixed-term targets only
"termHeadline": "Fixed 4.01% until 28 Aug 2026"
}

The envelope also carries source (the resolved position with its USD values), hiddenForLiquidity, and excluded — one entry per distinct drop reason, so a UI can explain a short list instead of silently showing one.

Native-asset convention. Every 1delta data payload spells the native asset as the ZERO ADDRESS. 0xEeee…EEeE is an encoding-layer sentinel: it is accepted on action inputs and normalised away, but it is never served and never matches a data filter. A client filtering markets by 0xEeee… silently gets no rows.

Plain-text reference — GET /v1/actions/loop/migrate/targets

Parameters

ParameterInTypeRequiredDescription
marketUidSourceCollateralquerystringyesCollateral market of the position being moved (lender:chainId:address).
marketUidSourceDebtquerystringyesDebt market of the position being moved (lender:chainId:address). Must be on the same chain as the collateral.
debtAmountquerystringnoLive debt of the position, in the debt asset’s wei. Supply it to filter out targets that cannot fund the whole borrow, to price each row AT this notional, and to get a resulting health factor per target.
collateralAmountquerystringnoLive collateral of the position, in the collateral asset’s wei. Needed (with debtAmount) for the per-target health factor.
convertLegquerycollateral, debtnoAsk for targets that CONVERT one leg via an aggregator swap. With convertTo omitted the response carries convertibleAssets — the assets that leg can convert into while still pairing with the fixed leg — so a picker can be populated without knowing which pairings exist.
convertToquerystringnoTarget asset address for the converted leg. Pins that leg to exactly this asset; the fixed leg keeps the source asset (native ⇄ wrapped-native included).
countqueryintegernoMaximum ranked targets to return (default 50).
includeIlliquidquerybooleannoKeep targets whose borrow liquidity cannot fund debtAmount (they sort last and carry migrate.sufficientLiquidity: false). Default false — they are counted in hiddenForLiquidity instead.
maxRiskScorequeryintegernoRisk cap for candidate markets. Defaults to 100, i.e. no cap: a migrate MOVES a position the caller already holds, so the pair browser’s default cap (which hides whole chains) would hide real destinations. Each row still carries its own risk breakdown.
collateralPriceUsdquerystringnoOverride. USD price of the source collateral, for sizing the position. Resolved server-side.
debtPriceUsdquerystringnoOverride. USD price of the source debt, for sizing the position. Resolved server-side.

Response 200

FieldTypeDescription
successTrue
dataobjectInformational data (quotes, simulation results, etc.)
actionsobjectTransaction calldata and approvals. Null for quote-only responses (no account provided).
actions.transactionsobject[]Pre-trade setup transactions (e.g. e-mode switch, collateral enable). Execute these before the main swap. Empty when no setup is needed.
actions.transactions[].tostringTarget contract address
actions.transactions[].datastringEncoded calldata
actions.transactions[].valuestringETH value to send with the transaction
actions.transactions[].descriptionstringHuman-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").
actions.alternativesobject[]DEX aggregator swap transactions sorted by best output (descending). Each entry's description is the aggregator name. The client should pick one to execute. Present on loop action endpoints.
actions.alternatives[].tostringTarget contract address
actions.alternatives[].datastringEncoded calldata
actions.alternatives[].valuestringETH value to send with the transaction
actions.alternatives[].descriptionstringHuman-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").
actions.permissionsobject[]Approval/delegation transactions that must execute before both transactions and alternatives. Includes ERC20 allowances (targeting the composer contract) and lender borrow/withdrawal delegations (targeting the lending protocol contract directly). Filtered against on-chain state so only missing approvals are returned. Null when no approvals are needed.
actions.permissions[].tostringTarget contract address
actions.permissions[].datastringEncoded calldata
actions.permissions[].valuestringETH value
actions.permissions[].descriptionstringHuman-readable description of the approval (e.g. "Approve borrow for AAVE_V3", "Approve ERC20")
actions.permissions[].spenderstringERC-20 approve spender (x-chain permissions). Match it against the selected quote's approvalTarget — several bridges can share one spender, so do not match by description.

Example response

{
"success": true,
"data": {},
"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

Ranked migration targets for the given position