# AGENTS.md — Spiral Stake

Spiral Stake is a **context + execution layer** for leveraged yield across Ethereum mainnet (chainId 1) and Robinhood Chain (chainId 4663). This
file orients agents (coding assistants and autonomous agents) integrating with Spiral.

as-of 2026-10-14T08:53:09.349Z — live snapshot. Live data (source of truth): https://api.spiralstake.xyz/v1/strategies

## What a Spiral strategy is

A one-transaction **leveraged loop** on [Morpho](https://morpho.org). You supply a yield-bearing
collateral, borrow against it, and recycle the borrow back into more collateral — repeated to a
chosen leverage. Three shapes:

- **Correlated (max-APY loop):** collateral and loan track the same value (e.g. sUSDS/AUSD,
  PT-sUSDE/RLUSD, syrupUSDG/USDG). Leverage multiplies the underlying yield rather than adding price
  exposure. No `spiralHints.profile`.
- **Non-correlated (leveraged perp):** e.g. wstETH/USDC, XAUt/USDT on mainnet; PONS/USDG, CASHCAT/USDG
  on Robinhood Chain. Leverage increases directional exposure to the collateral; the loop's carry is
  a cost. `spiralHints.profile.value = "leveraged_perp"`.
- **Stock + yield equity vault (Robinhood Chain):** `id` starts with `equity-`,
  `collateral.category = "stocks"`, `spiralHints.profile.value = "equity_yield_vault"`. A USDG deposit
  is swapped to a tokenized stock (SPY, NVDA, TSLA, SPCX, AAPL) held 1x as the user's own collateral
  on a partner market (`curator`: Longbow or NetNet Credit); a share of its value is borrowed back
  and farmed in a Spiral yield loop. `leverageApyPct` is the net dollar APY on the deposit, not a
  user-selectable leverage. Two vaults can share a ticker — `curator` and
  `collateral.description` tell them apart.

**We surface facts; the agent decides.** Every cost, leverage step, and risk fact is raw and
unit-labeled. The only Spiral opinion is namespaced under `spiralHints`, and it always ships the
thresholds behind it so you can recompute or override.

## The `/strategies` contract

- Endpoint (source of truth): `https://api.spiralstake.xyz/v1/strategies`
- Envelope: `{ asOf, chainId, count, strategies[] }`. `asOf` is snapshot assembly time.
- Per-strategy raw facts include: `collateral`/`loan` (address, symbol, decimals, category),
  `collateralApyPct`, `borrowApyPct`/`netBorrowApyPct`, `maxLeverage`, a discrete
  `leverageLadder` (`leverage` → `ltvPct` → `leverageApyPct`, 1x…max), `defaultLeverage`,
  `ltvPct` (liquidation/max), `oracle` (`type`: `nav` | `market`), `exitLiquidity`
  (measured `slippagePct` by USD size, sold into the chain's stable — see `direction`),
  `collateral.description` (what the collateral is, in plain English), `curator` (present only on
  partner-curated markets: the equity vaults and Longbow's perps), and `links.market` (the
  market's own page: Morpho, longbow.cash or NetNet Credit).
- **Conventions:** percentages are strings suffixed `Pct` (2-dp). USD amounts are numbers suffixed
  `Usd`. Timestamps are ISO-8601 UTC.
- **null vs absent (critical):** `null` = measured, no value (e.g. no swap route at that size).
  A field being **absent** = unmeasured — treat as unknown, **never** as a safe 0.
- **Freshness:** each field group in `freshness` carries its own `asOf` + `staleAfterSec`. A stale
  group is served last-good (never dropped, never 0) with its age visible.
- **`spiralHints`:** optional, namespaced, overridable opinion — never interleaved with raw facts,
  always ships its thresholds.

## Reading the risk facts

Mechanics, not advice — what the fields mean so you can weigh them yourself.

- **Carry = `collateralApyPct` − `netBorrowApyPct`.** This is the per-unit edge leverage
  multiplies. Positive carry: more leverage raises APY. Negative carry: more leverage *lowers* it —
  but the 1x base yield can keep `leverageApyPct` positive, so **negative carry does not by itself
  mean a negative levered APY**. Read the `leverageLadder`; don't infer it.

- **`oracle.type` decides what can liquidate the position.**
  - `nav` — priced off the redemption/exchange rate. A DEX depeg does not move it, so a correlated
    loop is not liquidated by a transient market dislocation.
  - `market` — priced off the traded price. A depeg or thin-book dislocation moves the oracle and
    **can liquidate the position even when the collateral still redeems 1:1 at maturity.** Weigh it
    against `ltvPct.liquidation` headroom, especially on fixed-term (PT) collateral.

- **`exitLiquidity.slippagePct` is measured, per USD notional.** `null` at a size = **no route at
  that size**. A **negative** value = price improvement (you receive more than you put in), not an
  error. `noSwapRoute: true` means the position cannot currently be unwound via a swap at all —
  the headline APY is unreachable in practice.

- **`maxLeverage` is bounded by available borrow liquidity, not by safety.** Higher rungs of the
  ladder sit closer to `ltvPct.liquidation`; the ladder tells you the APY, not the headroom.

- **`leverageApyPct` is always the honest signed carry — check `correlated` for what it means.**
  For **correlated** strategies it is the levered yield:
  `(collateralApyPct + collateralIncentive.aprPct) × leverage − netBorrowApyPct × (leverage − 1)`.
  For **non-correlated** (perp) strategies it is the same formula on a collateral that earns ~0, so
  it is the annualised **financing carry**, typically *negative* — the cost you pay to hold the long.
  Your return there comes from the collateral's price movement, which Spiral does not forecast.
  (The app displays a perp's carry with the sign flipped as a "Borrow APY" — the API does not.)

- **`collateralApySource` qualifies `collateralApyPct`.** `none` means no yield source resolved
  for that collateral — the accompanying `0.00` is *"not resolved"*, not a measured 0%. Treat those
  strategies (and any `leverageApyPct` derived from them) as **unknown**, not as zero-yield.

## Chain & protocol

- Networks: Ethereum mainnet (chainId 1) and Robinhood Chain (chainId 4663). One host serves every chain — select one with `?chainId` on the
  endpoint (default is the primary chain); each strategy also carries its own `chainId`.
- Lending protocol: Morpho (isolated markets; `id` is the Morpho market id).

## Connect via MCP

Native tool access over the Model Context Protocol (Streamable HTTP): **`https://api.spiralstake.xyz/mcp`**. Any
spec-compliant MCP client (Claude, Cursor, …) can add this URL. Tools:
- `list_strategies({ category?, chainId? })` — eligible strategies with full raw facts.
  `category` ∈ stable | ETH | BTC | stable-PT | stocks | Other.
- `get_strategy({ id, chainId? })` — one strategy by id.
- `get_prices({ chainId? })` — USD prices of the chain's loan and collateral tokens.
- `get_positions({ userAddress, chainId? })` — a wallet's loop positions and open equity vaults.
- `simulate_leverage` / `build_leverage_tx` / `build_manage_tx` — preview, open, adjust or close a
  loop or perp position (unsigned `{ approvals[], tx }`).
- `simulate_equity_deposit` / `build_equity_deposit_tx` / `build_equity_exit_tx` — preview, enter or
  fully unwind an equity vault (unsigned atomic `calls[]` batch: submit via EIP-5792
  `wallet_sendCalls` or a Safe; always send the final revoke call).
Execution is non-custodial: the server never signs, sends or holds keys.

No MCP client? The same data is plain JSON at `https://api.spiralstake.xyz/v1/strategies`.

## Links

- App: https://app.spiralstake.xyz
- MCP endpoint: `https://api.spiralstake.xyz/mcp`
- Strategy map (agents): https://app.spiralstake.xyz/llms.txt · full facts: /llms-full.txt
- Per-strategy page: `https://app.spiralstake.xyz/{chainId}/strategies/{id}/{collateral}-{loan}`
- Live JSON: `https://api.spiralstake.xyz/v1/strategies`

_This file is generated from the same `/strategies` snapshot as /llms.txt and the prerendered
pages. The static numbers are a discovery breadcrumb; poll the live JSON for current values._
