# Migrate position (cross-lender)

Endpoint reference for the 1delta API. Index: https://docs.1delta.io/llms.txt · every endpoint: https://docs.1delta.io/llms-full.txt

---

### GET /v1/actions/loop/migrate

- operationId: `loop-migrate`
- docs: https://docs.1delta.io/1delta-api/loop-migrate/
- markdown: https://docs.1delta.io/1delta-api/loop-migrate.md
- tags: Loop (Actions)

Migrate position (cross-lender)

Move a whole debt + collateral position from one lender to another in a single flash-loan transaction, optionally converting ONE leg (collateral OR debt) via an aggregator swap.

Flow (no conversion): flash the debt → repay the source → withdraw the source collateral → deposit it to the target → borrow the debt from the target → repay the flash. Same-lender / different-market moves (e.g. Morpho market A→B, Aave V4 reserve→reserve) are supported.

**Supported sources & targets:** Aave V2/V3, Aave V4, Compound V3, Morpho Blue / Lista, Fluid (an existing position via NFT-custody, OR a brand-new position — see below), Gearbox V3 (existing credit account as source; a fresh credit account is opened in-callback as target), Euler V2 (accountId 0; collateral-share transfer through the call forwarder). **Not supported:** Dolomite (either side), Euler as a TARGET, non-Venus Compound V2 as a target, both legs converting at once, Fluid↔Fluid.

> **Fluid target — existing vs fresh:** pass the position's NFT id in `accountId` to migrate INTO an existing Fluid position (delivered via the VaultFactory NFT-custody flow). Omit it (or pass 0) to open a **brand-new** Fluid position: the migrate emits a single dual-axis Fluid `operate` that supplies the withdrawn collateral, borrows the debt, and mints the new position NFT straight to you — no NFT custody, no target-side consent, and the transaction targets the composer (`deltaCompose`) normally.

> **Native ↔ wrapped-native (wrap conversion):** a native debt — e.g. a Venus `vBNB` borrow — migrates into a target lender's **wrapped-native** debt market (WBNB). On-behalf borrowing cannot be delegated for the native asset, so the target MUST be the wrapped form; a native TARGET debt is rejected. The builder flashes the wrapped form, unwraps it to repay the native source, and borrows the wrapped form on the target — no aggregator needed. Compound V2 / Venus can be a migration **source** but never a **target** (no borrow delegation). The **collateral** leg wraps BOTH ways — a native source collateral (e.g. Fluid's ETH slot) is wrapped to WETH before the target deposit, and a wrapped source collateral is unwrapped for a native-collateral target — again with no aggregator. Native collateral is only reachable as such a wrap pair; native → an unrelated ERC20 target is rejected.

> **Lista DAO markets:** two per-market extras decide the shape. A **brokered** market (non-zero `broker`) routes its debt leg through the fixed-term broker — pass `loanId` when it is the SOURCE (which loan to repay) and `termId` when it is the TARGET (which term to open); the target has no default because the broker's flexible borrow has no on-behalf variant. A market with a **`collateralProvider`** (e.g. the slisBNB markets) routes its collateral leg through that provider, which exposes no `position()` getter — so a provider-gated SOURCE cannot use the withdraw-all sentinel and is sized from your live collateral balance instead (read server-side; pass `collateralAmount` to override).

> **Asset conversion (swap leg):** ONE leg may change asset via an aggregator swap — either the collateral (`marketUidTargetCollateral` uses a different underlying) or the debt (`marketUidTargetDebt`), not both. The route fetches the aggregator quote server-side and bakes the trade into the flash callback. A **collateral** swap is EXACT_INPUT of the withdrawn collateral → provide a concrete `collateralAmount` (or `collateralAmountHint`) so the swap input lines up. A **debt** swap is EXACT_OUTPUT (buy exactly the source debt to repay, selling the flashed target debt). Tune the swap tolerance with `slippage`. **Euler as a source** still needs an off-chain eVault share-balance read via the `prepareMigrate` SDK.

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`).

**Setup transactions (`actions.permissions`):** returned only for the consents actually missing, to be executed BEFORE the migrate. These vary by lender pair — e.g. the source collateral withdrawal approval; Aave V4 Giver/Taker/Config Position-Manager authorizations + per-reserve borrow allowance + collateral-enable grant; Morpho `setAuthorization`; a Gearbox V3 SOURCE's `setBotPermissions` grant on the credit account; Euler collateral eVault share approval to the composer. Fluid legs need no separate permission (NFT custody — or, for a fresh open, the composer opening on your behalf — IS the authorization).

**Delivery:** the migrate transaction targets the composer (`deltaCompose`) normally, OR the Fluid `VaultFactory.safeTransferFrom` when a Fluid leg requires NFT custody — `actions.transactions[0].to` reflects this.

**`data.result` (resulting position):** a summary for the UI — `from`/`to` lenders with their collateral + debt assets (address/symbol/decimals/logo), the new position amounts (`to.collateral.amount`/`amountUsd`, `to.debt.amount`/`amountUsd`), `netUsd`, `leverage`, `apr: { deposit, borrow, net }` and `healthFactor`. The rates, liquidation threshold, decimals and prices behind those are **resolved server-side** from the target markets — the corresponding query params are overrides only. USD/symbol fields are best-effort (omitted when price/token metadata is unavailable). `to.collateral.amount` is present when the withdraw was sized server-side (Aave source) or passed explicitly.

**Finding a target:** [`GET /v1/actions/loop/migrate/targets`](https://docs.1delta.io/1delta-api/loop-migrate-targets/) returns the ranked destinations this endpoint accepts for a given position — support matrix, native ⇄ wrapped equivalence, un-openable fixed-term books and borrow liquidity all applied server-side.

**Parameters**

| Name | 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, 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. |
| `marketUidSourceCollateral` | query | string | yes | Source collateral market (`lender:chainId:address`). The position being moved FROM. |
| `marketUidSourceDebt` | query | string | yes | Source debt market (`lender:chainId:address`). |
| `marketUidTargetCollateral` | query | string | yes | Target collateral market (`lender:chainId:address`). The position being moved TO. May be a different lender and/or a different asset (collateral conversion). |
| `marketUidTargetDebt` | query | string | yes | Target debt market (`lender:chainId:address`). May be a different lender and/or a different asset (debt conversion). |
| `debtAmount` | query | string | yes | Live debt to migrate, in the debt asset’s wei. For a debt conversion this is the NEW-debt (flash/borrow) amount instead. The flash is sized a small buffer above this so the source repay clears in full (a residual would make the full collateral withdrawal revert). |
| `account` | query | string | no | Account address. Include to build the transaction + permission setup; omit for a quote-only response. |
| `isMaxIn` | query | boolean | no | Withdraw the full collateral balance from the source (default true for a same-asset move). Set false + `collateralAmount` for an exact-amount conversion so the swap input lines up. |
| `collateralAmount` | query | string | no | Exact source collateral to withdraw, in wei. Required (with `isMaxIn=false`) for a collateral conversion so the withdrawn amount matches the swap input. |
| `accountId` | query | string | no | Per-position id where the lender needs one: an existing Fluid NFT id (omit or 0 opens a BRAND-NEW Fluid position), Euler sub-account index (must be 0 — sub-accounts unsupported). Defaults to 0. |
| `loanId` | query | string | no | Lista fixed-term broker SOURCE only: the loan `posId` to repay, or `type(uint128).max` for the flex/dynamic position. Required when the source debt market is brokered; ignored otherwise. The target borrow is keyed by term, not loanId. |
| `termId` | query | integer | no | Lista fixed-term broker TARGET only: which term the migrated debt opens at. REQUIRED when the target debt market is brokered — the broker exposes no on-behalf flexible borrow, so there is no default; ignored otherwise. Mirrors the `termId` on /v1/actions/loop/open. |
| `irModeFrom` | query | 0 \| 1 \| 2 | no | Aave interest mode of the source debt (repay). 2 = variable. |
| `irModeTo` | query | 0 \| 1 \| 2 | no | Aave interest mode of the target debt (borrow). 2 = variable. |
| `slippage` | query | string | no | Swap-leg slippage tolerance as a FRACTION (`0.005` = 0.5%). Only used when a leg is converted via an aggregator swap; ignored for same-asset / wrap moves. Defaults to 0.5%. ⚠ **This endpoint is the exception.** Every other `slippage` in this API is in BASIS POINTS (`50` = 0.5%) — migrate takes a fraction, which is 100× smaller for the same tolerance. Sending `50` here would ask for 5000% slippage. |
| `eModeTo` | query | string | no | Risk-config category to read the TARGET collateral's liquidation threshold from (Aave-style e-modes publish one config per category). Defaults to `0` (no e-mode). Display only — it does not change the built transaction. |
| `collateralDecimals` | query | integer | no | **Override.** Target collateral decimals. Resolved server-side; pass only to overrule the published metadata (e.g. a market whose token-list decimals are known-wrong, which would mis-scale the `data.result` USD values). Never affects the on-chain amounts. |
| `debtDecimals` | query | integer | no | **Override.** TARGET debt decimals — the swap target’s when the debt converts, else the source’s. Resolved server-side; see `collateralDecimals`. |
| `sourceDebtDecimals` | query | integer | no | **Override.** SOURCE debt decimals, for the `from`-leg display when the debt is converted. Resolved server-side; defaults to `debtDecimals`. |
| `depositApr` | query | string | no | **Override.** Target collateral deposit APR as a FRACTION (0.05 = 5%), intrinsic yield folded in. Resolved server-side and reported as `data.result.apr.deposit`. |
| `borrowApr` | query | string | no | **Override.** Target debt borrow APR as a FRACTION (0.05 = 5%), intrinsic yield folded in — for a fixed-term target this is the term rate, not the 0% variable rate. Resolved server-side. |
| `liqThreshold` | query | string | no | **Override.** Target collateral liquidation threshold as a FRACTION (0.85 = 85%), driving `data.result.healthFactor` (= collateralUsd · liqThreshold / debtUsd; < 1 ⇒ liquidatable). Resolved server-side from the target market’s risk config — see `eModeTo`. |
| `collateralPriceUsd` | query | string | no | **Override.** USD price of the TARGET collateral for the display conversion. Resolved server-side, preferring the market’s own ORACLE price (what the lender liquidates against). |
| `debtPriceUsd` | query | string | no | **Override.** USD price of the TARGET debt for the display conversion. Resolved server-side; see `collateralPriceUsd`. |

**Response `200`** — Quote or full build response

| 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[].utilization.current` | number | Current value |
| `data.quotes[].rateImpact[].utilization.projected` | number | Projected value after the action |
| `data.quotes[].rateImpact[].borrowRate` | object | A current/projected pair for a single rate metric. |
| `data.quotes[].rateImpact[].borrowRate.current` | number | Current value |
| `data.quotes[].rateImpact[].borrowRate.projected` | number | Projected value after the action |
| `data.quotes[].rateImpact[].depositRate` | object | A current/projected pair for a single rate metric. |
| `data.quotes[].rateImpact[].depositRate.current` | number | Current value |
| `data.quotes[].rateImpact[].depositRate.projected` | number | Projected value after the action |
| `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.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.permissionTxns` | object[] | Approvals needed for this specific quote. Most integrators should use the deduplicated envelope-level `actions.permissions` instead. |
| `data.permissionTxns[].to` | string | Target contract address |
| `data.permissionTxns[].data` | string | Encoded calldata |
| `data.permissionTxns[].value` | string | ETH value |
| `data.permissionTxns[].description` | string | Human-readable description of the approval (e.g. "Approve borrow for AAVE_V3", "Approve ERC20") |
| `data.permissionTxns[].spender` | string | ERC-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. |
| `actions` | object | Transaction calldata and approvals. Null for quote-only responses (no account provided). |
| `actions.transactions` | object[] | 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[].to` | string | Target contract address |
| `actions.transactions[].data` | string | Encoded calldata |
| `actions.transactions[].value` | string | ETH value to send with the transaction |
| `actions.transactions[].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"). |
| `actions.alternatives` | object[] | 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[].to` | string | Target contract address |
| `actions.alternatives[].data` | string | Encoded calldata |
| `actions.alternatives[].value` | string | ETH value to send with the transaction |
| `actions.alternatives[].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"). |
| `actions.permissions` | object[] | 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[].to` | string | Target contract address |
| `actions.permissions[].data` | string | Encoded calldata |
| `actions.permissions[].value` | string | ETH value |
| `actions.permissions[].description` | string | Human-readable description of the approval (e.g. "Approve borrow for AAVE_V3", "Approve ERC20") |
| `actions.permissions[].spender` | string | ERC-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**

```json
{
  "success": true,
  "data": {
    "lender": "AAVE_V3",
    "quotes": [
      {
        "deltas": {
          "aggregator": "string",
          "tradeInput": 1,
          "tradeOutput": 1,
          "deltas": {}
        },
        "rateImpact": [
          {
            "marketUid": "AAVE_V3:8453:0x4200000000000000000000000000000000000006",
            "utilization": {
              "current": 1,
              "projected": 1
            },
            "borrowRate": {
              "current": 1,
              "projected": 1
            },
            "depositRate": {
              "current": 1,
              "projected": 1
            }
          }
        ]
      }
    ],
    "rateImpact": [
      {
        "marketUid": "AAVE_V3:8453:0x4200000000000000000000000000000000000006",
        "utilization": {
          "current": 1,
          "projected": 1
        },
        "borrowRate": {
          "current": 1,
          "projected": 1
        },
        "depositRate": {
          "current": 1,
          "projected": 1
        }
      }
    ]
  },
  "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"
      }
    ]
  }
}
```

**Response `400`** — Validation error

**Response `429`** — Rate limited. Unauthenticated callers share a per-IP budget; send an `x-api-key` header to lift it. Retry with exponential backoff.

**Response `500`** — Unexpected server error. Safe to retry with backoff.

**Response `502`** — 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.
