> ## Documentation Index
> Fetch the complete documentation index at: https://docs.compasslabs.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Leveraged Looping

> Let your users amplify a collateral position, unwind it, and rebalance a whole book of loops - each in one atomic transaction.

## Why This Matters

Leverage is the most requested feature in every lending product, and the hardest to ship. Doing it by hand means a dozen supply/borrow/swap round trips, a new liquidation risk on every leg, and a partially-built position if any step fails. Most teams never ship it.

We've done that work. One call returns one transaction that builds the whole position - or unwinds it - atomically. If any leg fails, nothing happens.

## What It Does

Let users take a leveraged position on a lending market: supply collateral, borrow against it, swap the borrowed token back to collateral, and repeat until they reach the leverage they asked for. Unwinding runs the same loop in reverse. Both are a single signature.

You call our API, we return a transaction payload. You or your users sign and broadcast. Users get leverage, you capture the flow.

**Important:** Users must first create a [Credit Account](/v2/Products/Accounts) and fund it with the collateral token. Looping never pulls from the user's wallet mid-transaction - the Credit Account must already hold `initial_collateral_amount`.

## How a Loop Works

A loop is one transaction containing many iterations. Each iteration:

1. **Supply** the collateral token to the lending market
2. **Borrow** the loan token at the requested `loan_to_value`
3. **Swap** the borrowed token back to the collateral token

The output of step 3 becomes the next iteration's supply. Iterations continue until total collateral reaches `multiplier` × `initial_collateral_amount`.

### Every swap has a guaranteed floor

The API never chains one leg onto a *hoped-for* swap output. Each swap carries a minimum output enforced on-chain, and the next supply is sized against that minimum. A fill anywhere inside your slippage tolerance can never break a later step.

Anything a swap delivers **above** the floor stays in the Credit Account as recoverable surplus, never lost. `preview.estimated_max_dust` is the worst-case bound on it. Tighter `max_slippage_percent` means less surplus.

<Info>
  `estimated_max_dust` is **not** a cost - it is money kept. The cost of entering a loop is `preview.estimated_swap_cost`: DEX fees plus price impact, in collateral-token units, excluding gas. That's the amount the position has to earn back before it's ahead.
</Info>

### When the collateral is a vault share

Some markets take an ERC-4626 vault share as collateral - a yield-bearing token like a Morpho vault's `steakUSDC`. Those tokens have no honest DEX route: aggregators will still quote them, but through dust pools at prices far off net asset value. Since the loop derives its guaranteed floors *from the quote*, a mispriced quote would be executable rather than merely unroutable.

So the conversion goes through the vault instead of a DEX: a loop swaps the borrow token to the vault's underlying asset and calls the vault's own `deposit()`, and an unwind `redeem()`s the shares and swaps the underlying back. Both settle at net asset value. A direct route for the share token is only ever used when it prices within 1% of NAV. You do not configure any of this - pass the share token as `collateral_token` and the engine picks the path. Browse the vaults themselves with [`GET /v2/earn/vaults`](/v2/api-reference/earn/list-vaults).

## Supported Venues

| Protocol        | Send                                       | How you name a market                       | Browse markets with                                                                                    |
| --------------- | ------------------------------------------ | ------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| **Aave V3**     | `protocol: "AAVE"`                         | `collateral_token` + `borrow_token` symbols | [`GET /v2/earn/aave_markets`](/v2/api-reference/earn/list-aave-markets)                                |
| **Morpho Blue** | `protocol: "MORPHO"`                       | `market_id` (bytes32)                       | [`GET /v2/credit/morpho_markets`](/v2/api-reference/credit/list-curated-morpho-markets)                |
| **HyperLend**   | `protocol: "AAVE"` + `chain: "hyperevm"`   | `collateral_token` + `borrow_token` symbols | [`GET /v2/earn/aave_markets?chain=hyperevm`](/v2/api-reference/earn/list-aave-markets)                 |
| **Felix**       | `protocol: "MORPHO"` + `chain: "hyperevm"` | `market_id` (bytes32)                       | [`GET /v2/credit/morpho_markets?chain=hyperevm`](/v2/api-reference/credit/list-curated-morpho-markets) |

Select the protocol per request with the `protocol` field. It defaults to `AAVE`, so existing integrations need no changes. HyperLend and Felix are not separate `protocol` values — they are what Aave and Morpho resolve to on HyperEVM.

Euler V2 markets are available today for plain borrowing through [Crypto-Backed Loans](/v2/Products/Credit); full leverage support for them is on the way.

### Finding a market to loop

Both discovery endpoints return everything you need to populate a market picker, and both accept a `chain` filter:

* **`/v2/earn/aave_markets`** returns each Aave reserve with its supply and borrow APY per chain. Despite living under `earn`, it is the rates source for Aave-style credit too — including HyperLend, which it serves as a read-only exception on `chain=hyperevm`. Pick a collateral token and a borrow token from it and pass their symbols straight to `/loop`.
* **`/v2/credit/morpho_markets`** returns the curated Morpho market set with live `lltv`, `borrow_apy`, `utilization`, and `available_liquidity`, read on-chain per request. Sort by `tvl_usd`, `liquidity_usd`, or `lltv`. The `market_id` you pass to `/loop` comes from here, and the market's own pair fixes your `collateral_token` and `borrow_token`.

Two things to check before offering a market: `lltv` (Morpho) or the reserve LTV (Aave) is the ceiling on `loan_to_value`, and `available_liquidity` must exceed the loop's total borrow or the build returns a 400.

Neither discovery endpoint knows anything about a particular user - both describe the market, not the account. Two per-account reads complement them:

* [`GET /v2/credit/looped_positions`](/v2/api-reference/credit/list-looped-leveraged-credit-positions) - the leveraged positions this Credit Account already holds, with live leverage, health factor, and net APY. This is the one to poll for a dashboard; see [Track Looped Positions](#track-looped-positions).
* [`GET /v2/credit/positions`](/v2/api-reference/credit/list-credit-positions) - the account's raw collateral and debt, plus a `borrowable_tokens` array giving the maximum each token can still be borrowed at and its current APY. Only this endpoint reports remaining borrow capacity; `looped_positions` does not carry it.

### Chain coverage

| Chain     |     Aave V3     |    Morpho   |
| --------- | :-------------: | :---------: |
| Ethereum  |       Yes       |     Yes     |
| Base      |       Yes       |     Yes     |
| Arbitrum  |       Yes       |     Yes     |
| BNB Chain |       Yes       |      -      |
| HyperEVM  | Yes (HyperLend) | Yes (Felix) |

Other chains in the `chain` enum have no credit venue deployed yet and return a 422 naming the chains that do.

## Preview Before You Build

`preview: true` gives you the projected end state **without spending a firm quote**. Use it on every call made while a user is moving a slider, and leave it `false` only for the build they actually intend to sign.

This matters: on firm-quoted pairs, quotes are single-use maker commitments. Requesting them for displays that are never executed degrades the pricing available to everyone on the API.

<Warning>
  `preview: true` guarantees **no firm quote was spent**. It does *not* guarantee `transaction` is absent. On a pair no firm provider covers - and on `pricing: "market"` - the request falls through to the aggregator and a signable `transaction` comes back anyway. Decide what to do from the field you actually got, not from the flag you sent.
</Warning>

Three outcomes are possible:

| Situation                                     | `transaction` | `preview`                  | Priced by      |
| --------------------------------------------- | ------------- | -------------------------- | -------------- |
| Firm-covered pair, `pricing` `auto` or `firm` | absent        | populated, indicative      | firm provider  |
| `pricing: "firm"`, no firm coverage           | absent        | **absent** - advisory only | nothing priced |
| Not firm-covered, or `pricing: "market"`      | **present**   | populated, market-priced   | DEX aggregator |

<Note>
  These endpoints omit null fields from the JSON entirely rather than sending `null`. Test for **absence** (`?.` / `.get(...)`), not for a null value - `"quote_expires_at": null` never appears on the wire.
</Note>

### Pricing policy

The `pricing` field controls how swap legs are routed:

| Value            | Behaviour                                                                                                                                                                                                              |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `auto` (default) | Firm quotes where a firm provider covers the pair; transparent fallback to the DEX aggregator otherwise                                                                                                                |
| `firm`           | Never fall back to the aggregator. A preview the firm provider cannot serve returns the coverage advisory alone (`preview: null`); an execution fails with a typed error rather than silently substituting DEX pricing |
| `market`         | Never route through the firm provider. Every leg is priced by a DEX aggregator and bounded by `max_slippage_percent`                                                                                                   |

`pricing: "firm"` is incompatible with `gas_sponsorship: true` - sponsored transactions always route through the aggregator.

The two routes differ in where the price comes from. A **firm provider** commits a quote at a fixed price for a short window, so the fill is exact: zero slippage and no dust, but the quote expires. A **DEX aggregator** routes the swap across on-chain pool liquidity at the prevailing rate, so the fill moves with the market and is protected by an on-chain minimum output derived from `max_slippage_percent`. The `pricing` values are named `firm` and `market` after the two.

### Reading the pricing fields back

| Field                 | Meaning                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `swap_provider`       | Which route actually priced the legs - a firm provider or a DEX aggregator. Always present, including on fallbacks. **This is the signal to branch on.** `pricing` is only what you *asked for* (under `auto` a firm build can fall back to market without telling you), and `quote_expires_at` is absent on every preview - so on a preview this field is the only way to know whether the numbers you are showing are firm or market. |
| `quote_expires_at`    | Deadline of the firm quotes - the earliest across the legs. **Sign and broadcast before it or the transaction reverts on-chain.** Refresh by re-calling the endpoint and discarding the previous payload. Present only on executable firm builds - never on a preview, so it cannot tell you how a preview was priced. Use `swap_provider` for that.                                                                                    |
| `max_firm_multiplier` | Loop previews only: the highest multiplier firm quotes can fill at this size and LTV. Recompute on every parameter change.                                                                                                                                                                                                                                                                                                              |
| `firm_available`      | Unloop previews only: whether a firm provider can serve this unwind. Computed on every policy, including `market`, so the firm/aggregator boundary stays visible even if you opted out.                                                                                                                                                                                                                                                 |

<Warning>
  The signing window on a firm build is short - a few tens of seconds, set by the maker, not by us. **Read `quote_expires_at`; never hardcode a TTL.** If the user takes too long, re-call the endpoint for a fresh payload rather than broadcasting the old one. Expired quotes surface as a `409 Firm quotes expired` on `pricing: "firm"`, and are silently rebuilt on the aggregator route under `auto`.
</Warning>

## Open a Loop

Build a 2x WETH position on Aave, borrowing USDC at 70% LTV against 1 WETH already sitting in the Credit Account.

<CodeGroup>
  ```python Python theme={"system"}
  import httpx

  response = httpx.post(
      "https://api.compasslabs.ai/v2/credit/loop",
      json={
          "owner": "0xYourWalletAddress",
          "chain": "ethereum",
          "protocol": "AAVE",
          "collateral_token": "WETH",
          "borrow_token": "USDC",
          "initial_collateral_amount": "1",
          "multiplier": 2,
          "loan_to_value": 70,
          "max_slippage_percent": 0.5,
          "preview": True,
      },
      headers={"x-api-key": "YOUR_API_KEY"},
  )
  result = response.json()
  preview = result["preview"]

  print(f"Iterations:        {preview['iterations']}")
  print(f"Total collateral:  {preview['total_collateral_supplied']} WETH")
  print(f"Total borrowed:    {preview['total_borrowed']} USDC")
  print(f"Leverage (floor):  {preview['achieved_multiplier']}x")
  print(f"Health factor:     {preview['projected_health_factor']}")
  print(f"Entry cost:        {preview['estimated_swap_cost']} WETH")
  print(f"Max surplus kept:  {preview['estimated_max_dust']} WETH")
  ```

  ```typescript TypeScript theme={"system"}
  const response = await fetch("https://api.compasslabs.ai/v2/credit/loop", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": "YOUR_API_KEY",
    },
    body: JSON.stringify({
      owner: "0xYourWalletAddress",
      chain: "ethereum",
      protocol: "AAVE",
      collateral_token: "WETH",
      borrow_token: "USDC",
      initial_collateral_amount: "1",
      multiplier: 2,
      loan_to_value: 70,
      max_slippage_percent: 0.5,
      preview: true,
    }),
  });
  const { preview } = await response.json();

  console.log(`Iterations:        ${preview.iterations}`);
  console.log(`Total collateral:  ${preview.total_collateral_supplied} WETH`);
  console.log(`Total borrowed:    ${preview.total_borrowed} USDC`);
  console.log(`Leverage (floor):  ${preview.achieved_multiplier}x`);
  console.log(`Health factor:     ${preview.projected_health_factor}`);
  console.log(`Entry cost:        ${preview.estimated_swap_cost} WETH`);
  console.log(`Max surplus kept:  ${preview.estimated_max_dust} WETH`);
  ```
</CodeGroup>

Set `preview: false` for the build the user is about to sign, then sign and broadcast the returned `transaction` as usual.

### What the loop preview tells you

| Field                       | Meaning                                                                               |
| --------------------------- | ------------------------------------------------------------------------------------- |
| `iterations`                | How many supply/borrow/swap rounds are bundled                                        |
| `total_collateral_supplied` | **Guaranteed minimum** collateral supplied - the real figure can only be higher       |
| `total_borrowed`            | Total loan tokens borrowed across iterations                                          |
| `achieved_multiplier`       | Guaranteed leverage floor (`total_collateral_supplied / initial_collateral_amount`)   |
| `projected_ltv`             | Projected end loan-to-value, in percent                                               |
| `projected_health_factor`   | Projected health factor, floored - never overstated at the 1.0 boundary               |
| `estimated_swap_cost`       | One-off cost of entering, in collateral units (DEX fees + price impact, excludes gas) |
| `estimated_max_dust`        | Upper bound of the surplus left in the Credit Account                                 |
| `legs[]`                    | Per-iteration `supply_amount`, `borrow_amount`, `min_swap_out`, `expected_swap_out`   |

<Tip>
  There is no breakeven field, but you can derive one. Multiply `estimated_swap_cost` by the collateral price to get the entry cost in USD, then divide by the daily carry on **equity** (not on total collateral - `net_apy` is already a return on equity):

  `days ≈ entry_cost_usd ÷ (equity_usd × net_apy ÷ 100 ÷ 365)`

  `net_apy` and `net_usd_value` (the equity) both come from [looped positions](#track-looped-positions). If `net_apy` is at or below zero the position never breaks even at the current rates - say that rather than showing a large number.
</Tip>

### Looping on Morpho

Same call, swapping the market identifier for a `market_id`. This is the wstETH/WETH market at 96.5% LLTV on Ethereum; the body also carries `owner` and `chain`.

```json theme={"system"}
{
  "protocol": "MORPHO",
  "market_id": "0xb8fc70e82bc5bb53e773626fcc6a23f7eefa036918d7ef216ecfb1950a94a85e",
  "collateral_token": "wstETH",
  "borrow_token": "WETH",
  "initial_collateral_amount": "1",
  "multiplier": 4,
  "loan_to_value": 94
}
```

`collateral_token` and `borrow_token` must match the market's own pair, or the call is a 422. `market_id` is Morpho-only and `emode_category` is Aave-only — sending either on the wrong protocol is a 422 rather than a silent no-op.

## Track Looped Positions

`GET /v2/credit/looped_positions` reconstructs each leveraged position from indexed on-chain events - a transaction carrying lending, borrowing, and swap legs is a loop. It takes `chain` and `owner`, with no filters or pagination.

Detection is event-shaped, not API-created: a position assembled by hand through `/v2/credit/bundle` shows up the same way. The events do have to belong to the Credit Account derived from `owner` - leverage held directly by an EOA is invisible here. If the Credit Account isn't deployed yet, the call is a 400.

<CodeGroup>
  ```python Python theme={"system"}
  response = httpx.get(
      "https://api.compasslabs.ai/v2/credit/looped_positions",
      params={"chain": "ethereum", "owner": "0xYourWalletAddress"},
      headers={"x-api-key": "YOUR_API_KEY"},
  )

  for p in response.json()["positions"]:
      print(f"{p['collateral_symbol']}/{p['debt_symbol']} on {p['protocol']} - {p['status']}")
      c = p.get("current")  # absent on CLOSED positions
      if c:
          print(f"  leverage {c['leverage']}x, health {c['health_factor']} ({c['health_factor_scope']})")
          print(f"  net APY  {c['net_apy']}%  (collateral {c['collateral_apy']}%, borrow {c['borrow_apy']}%)")
  ```

  ```typescript TypeScript theme={"system"}
  const response = await fetch(
    "https://api.compasslabs.ai/v2/credit/looped_positions?chain=ethereum&owner=0xYourWalletAddress",
    { headers: { "x-api-key": "YOUR_API_KEY" } }
  );

  for (const p of (await response.json()).positions) {
    console.log(`${p.collateral_symbol}/${p.debt_symbol} on ${p.protocol} - ${p.status}`);
    const c = p.current; // absent on CLOSED positions
    if (c) {
      console.log(`  leverage ${c.leverage}x, health ${c.health_factor} (${c.health_factor_scope})`);
      console.log(`  net APY  ${c.net_apy}% (collateral ${c.collateral_apy}%, borrow ${c.borrow_apy}%)`);
    }
  }
  ```
</CodeGroup>

Each position carries `status` (`OPEN` / `CLOSED`), live `current` state, lifetime `totals`, and a full `history` of classified transactions (`loop_open`, `loop_increase`, `unwind`, `liquidation`, and single-sided actions), each with the detected swap leg.

Two fields deserve care:

* **`health_factor_scope`** is `"market"` on Morpho (isolated per market) and `"account"` on Aave. Aave collateral is pooled, so one health factor is shared by every Aave position on the account - see `aave_account_summary` for the account-level figures.
* **`net_apy`** is the levered carry on equity: `leverage × collateral_apy − (leverage − 1) × borrow_apy`. It is a snapshot at today's rates, not a forecast, and it is denominated in the debt asset - a positive `net_apy` still loses USD value if the pair moves against the user. It is `null` when either leg is unknown rather than guessed.

## Unwind a Loop

`POST /v2/credit/unloop` runs the loop in reverse: withdraw collateral, swap it to the borrow token at a guaranteed minimum, repay.

| `target_multiplier` | Result                                                                                                     |
| ------------------- | ---------------------------------------------------------------------------------------------------------- |
| omitted (`null`)    | **Full close.** Debt cleared exactly, accrued interest included; collateral returned to the Credit Account |
| `1`                 | Same exact debt close, but the collateral stays supplied and earning                                       |
| above `1`           | Delever to that multiplier (must be below the current one)                                                 |

<CodeGroup>
  ```python Python theme={"system"}
  response = httpx.post(
      "https://api.compasslabs.ai/v2/credit/unloop",
      json={
          "owner": "0xYourWalletAddress",
          "chain": "ethereum",
          "protocol": "AAVE",
          "collateral_token": "WETH",
          "borrow_token": "USDC",
          "max_slippage_percent": 0.5,
          "allow_partial": True,
      },
      headers={"x-api-key": "YOUR_API_KEY"},
  )
  result = response.json()

  if not result["preview"]["fully_unwound"]:
      print("Partial progress - call unloop again to finish.")
  ```

  ```typescript TypeScript theme={"system"}
  const response = await fetch("https://api.compasslabs.ai/v2/credit/unloop", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": "YOUR_API_KEY",
    },
    body: JSON.stringify({
      owner: "0xYourWalletAddress",
      chain: "ethereum",
      protocol: "AAVE",
      collateral_token: "WETH",
      borrow_token: "USDC",
      max_slippage_percent: 0.5,
      allow_partial: true,
    }),
  });
  const result = await response.json();

  if (!result.preview.fully_unwound) {
    console.log("Partial progress - call unloop again to finish.");
  }
  ```
</CodeGroup>

### Positions that need two transactions

Each withdrawal is sized to keep the health factor at or above **1.02** at that step. A position opened very close to its liquidation threshold may not have enough headroom to reach the target in one transaction.

Set `allow_partial: true` to take the maximum single-transaction progress instead of a 400. The response comes back with `preview.fully_unwound: false`; call unloop again from the now much lower leverage to finish. Without it, an unreachable target is an error.

The unloop preview adds `ending_collateral`, `ending_debt`, `ending_multiplier`, and `fully_unwound` to the shared fields.

### Closing an Aave position that shares a reserve

Aave holds one pooled balance per reserve, not one per pair. So if an account has two open Aave loops using the same collateral token, a full close of one does **not** withdraw the whole reserve balance - it withdraws only that position's attributed share, leaving the rest supplied to back the other loop. `preview.ending_collateral` reports what stays behind, so read it rather than assuming zero on a full close. Morpho markets are isolated and have no such interaction.

## Rebalance a Whole Book

`POST /v2/credit/rebalance` moves several positions toward their desired end states in one atomic transaction. Each entry in `targets` names a position and a goal - `target_equity_usd` (size), `target_multiplier` (leverage), both, or `close: true`.

The engine derives one of seven directions per target and reports it as `preview.per_target[].direction`:

| Direction                 | Meaning                           |
| ------------------------- | --------------------------------- |
| `open`                    | Not in the book yet - a new loop  |
| `close`                   | Full unwind                       |
| `grow` / `shrink`         | More / less equity committed      |
| `lever_up` / `lever_down` | Same equity, different multiplier |
| `noop`                    | Already at target - zero legs     |

Three properties are worth designing around:

* **Scoped, not a portfolio wipe.** Positions not named in `targets` are left untouched.
* **It releases capital before it consumes it.** Delevers and closes run first, freed collateral is routed between positions with capital-routing swaps when the tokens differ, then grows and opens run. A shrink on one position can fund a grow on another - including across protocols, in the same transaction.
* **Growth beyond freed capital comes from the Credit Account's idle balance.** If the book grows past what's available you get a 422 asking you to fund the account first with [`/v2/credit/transfer`](/v2/api-reference/credit/transfer-tokens-tofrom-credit-account).

When every named target is already at its goal, the plan is a no-op: `preview` still comes back with every `direction` set to `noop`, and there is no `transaction` to sign. That makes rebalance safe to poll in a converge loop.

<Note>
  Rebalance runs on Ethereum, Base, Arbitrum, and BNB Chain. It is **not available on HyperEVM** - use `/loop` and `/unloop` there. Also note that at most one Aave target per collateral token and per borrow token is allowed per call, because Aave pools collateral and debt per reserve rather than per pair.
</Note>

## HyperEVM: HyperLend and Felix

Looping runs on HyperEVM (chain `hyperevm`) against two venues:

| Venue                        | Send                               | Market                                                |
| ---------------------------- | ---------------------------------- | ----------------------------------------------------- |
| **HyperLend** (Aave V3 fork) | `protocol: "AAVE"`                 | wstHYPE / WHYPE, e-mode category `1`                  |
| **Felix** (Morpho)           | `protocol: "MORPHO"` + `market_id` | wstHYPE / WHYPE (86% LLTV), wstHYPE / USDC (77% LLTV) |

HyperLend is registered as the chain's Aave, so you address it with `protocol: "AAVE"` - there is no separate protocol value.

```json theme={"system"}
{
  "owner": "0xYourWalletAddress",
  "chain": "hyperevm",
  "protocol": "AAVE",
  "collateral_token": "wstHYPE",
  "borrow_token": "WHYPE",
  "initial_collateral_amount": "10",
  "multiplier": 3,
  "loan_to_value": 77,
  "emode_category": 1
}
```

A few things behave differently here:

**The entry conversion is a mint, not a swap.** There is no honest generic DEX path from WHYPE to wstHYPE, so the loop unwraps to native HYPE and mints through the staking Overseer at 1:1 - instant, permissionless, no venue spread. Exits route through a HyperEVM aggregator instead, because redemption through the Overseer queues for roughly a week and can never sit inside an atomic bundle.

**Loop bundles exceed the small-block gas limit.** HyperEVM small blocks cap out at 2M gas; a multi-iteration loop needs more. The sending EOA must have HyperCore big blocks enabled before broadcasting, or the transaction will not be included.

**Capacity is tight.** Supply caps and borrowable liquidity on these markets are small relative to the majors. Sizing is cap-aware and fails with an actionable 422 telling you the reachable size, rather than reverting at execution - surface that message to the user.

**Nothing else on the chain is swappable.** Only the wstHYPE conversion pairs have a route on HyperEVM. Any credit action needing a general swap returns a 400 - including `/v2/credit/borrow` when `token_in` differs from `collateral_token`. Supply the collateral token directly.

**Rebalance is not available on HyperEVM.** Use `/loop` and `/unloop`.

Only four tokens are registered on HyperEVM: `WHYPE`, `wstHYPE`, `stHYPE`, and a chain-native `USDC` at its own address (not the mainnet one).

## Errors Worth Handling

The OpenAPI spec only declares `200` and `422`. These runtime errors are the ones a real integration hits, so handle them explicitly. Each carries a prose `message` with the actionable figure - surface it rather than a generic failure.

**Loop**

| Status | Case                                                                                                           | What to do                                                                                     |
| ------ | -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| 400    | `Credit Account not deployed`                                                                                  | Call `/v2/credit/create_account` first                                                         |
| 400    | `Multiplier not reachable` - the loop converges below the requested multiplier at this LTV, slippage, and size | The message names the reachable figure; offer it, or ask for a higher LTV or a larger position |
| 400    | `Loop leverage exceeded` - unreachable at any convergence                                                      | The ceiling is roughly `0.9 ÷ (1 − loan_to_value/100)`; the message names the max achievable   |
| 400    | `loan_to_value too high`                                                                                       | The message names the market's cap                                                             |
| 400    | `Insufficient market liquidity`                                                                                | Reduce size or pick another market                                                             |
| 400    | `Amount too small` / `Loop leg too small` / `Swap pair unroutable`                                             | The position is too small to build a swap leg - it cannot loop at any leverage                 |

**Unloop**

| Status | Case                                                                   | What to do                                                |
| ------ | ---------------------------------------------------------------------- | --------------------------------------------------------- |
| 400    | `Unwind target not reachable`                                          | Retry with `allow_partial: true`, then call again         |
| 400    | `Position health at the safety floor` - nothing can be withdrawn first | Repay directly via `/v2/credit/repay`                     |
| 400    | `Position underwater` - collateral no longer covers debt               | Add collateral or repay directly                          |
| 400    | `Target not below current multiplier` / `Nothing to unwind`            | Read the current multiplier from `looped_positions` first |

**Both**

| Status    | Case                                                                                                | What to do                                                |
| --------- | --------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
| 409       | `Firm quotes expired` mid-build                                                                     | Re-call for a fresh payload                               |
| 409 / 502 | `Firm pricing unavailable` - no firm provider covers the pair right now (only on `pricing: "firm"`) | Retry with `auto` or `market`                             |
| 422       | Protocol not deployed on this chain                                                                 | The message lists the chains that do have it              |
| 422       | Wrong identifier for the protocol (`market_id` on Aave, `emode_category` on Morpho)                 | Send the identifier that matches `protocol`               |
| 422       | Supply or borrow cap reached                                                                        | Reduce size - the message carries the reachable amount    |
| 422       | Rebalance needs more capital than the account holds, or is too large for one transaction            | Fund via `/v2/credit/transfer`, or split across two calls |

Under `pricing: "auto"` the 409 and 502 cases never surface - a firm-provider failure falls back to the aggregator transparently.

## Use Cases

**Levered staking carry:** Loop a liquid-staking token against the asset it stakes. The collateral earns the staking rate, the debt costs the borrow rate, and leverage multiplies the spread.

*Example: 10 wstETH looped to 4x against WETH. Collateral earns 3.1%, WETH borrows at 2.4%. Levered carry on equity: 4 × 3.1% − 3 × 2.4% = 5.2% versus 3.1% unlevered.*

**One-click leverage in a lending UI:** Add a leverage slider next to an existing borrow flow. Call `/loop` with `preview: true` on every slider move to show projected health factor and entry cost, then build once on confirm.

**Automated deleveraging:** Poll `looped_positions`, watch `health_factor`, and call `/unloop` with a `target_multiplier` when a position drifts toward its threshold. Users never see a liquidation.

*Call `/unloop` with `preview: true` first to read `projected_health_factor`, so you can pick the target multiplier that restores the buffer you want.*

**Book-level portfolio moves:** Use `/rebalance` to shift a whole set of loops at once - close a Morpho position and open an Aave one, funded by the same freed collateral, in a single signature.

## API Reference

<CardGroup cols={2}>
  <Card title="Open a Leveraged Loop" icon="arrow-up-right-dots" href="/v2/api-reference/credit/open-a-leveraged-loop">
    Build a levered position in one atomic transaction.
  </Card>

  <Card title="Unwind a Leveraged Loop" icon="arrow-down-right" href="/v2/api-reference/credit/unwind-a-leveraged-loop">
    Delever or fully close a position.
  </Card>

  <Card title="List Looped Positions" icon="list" href="/v2/api-reference/credit/list-looped-leveraged-credit-positions">
    Live leverage, health factor, net APY, and full history.
  </Card>

  <Card title="Rebalance the Book" icon="scale-balanced" href="/v2/api-reference/credit/rebalance-the-leveraged-credit-book">
    Move several positions to their target states at once.
  </Card>
</CardGroup>

## Next Steps

<CardGroup cols={3}>
  <Card title="Crypto-Backed Loans" icon="hand-holding-dollar" href="/v2/Products/Credit">
    Plain borrowing, repayment, and position tracking.
  </Card>

  <Card title="Product Accounts" icon="wallet" href="/v2/Products/Accounts">
    Create and fund the Credit Account a loop runs from.
  </Card>

  <Card title="Gas Sponsorship" icon="gas-pump" href="/v2/Products/gas-sponsorship">
    Sponsor gas so users never need a native token.
  </Card>
</CardGroup>
