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

# Migration skill for your coding agent

> Use this doc as a SKILL.MD for your coding agent to migration your current code to the v3 API.

<Note>
  This page covers changes in the v3 API in detail. We recommend using the drop down button in the top right to directly copy this page into your LLM / coding agent as a `SKILL.MD`.
</Note>

If at any step there is not enough detail, refer to the [Guides](/guides) section or the relevant public SDK repos:

* [TypeScript SDK](https://github.com/derivexyz/derive-ts)
* [Python SDK](https://github.com/derivexyz/derive-py)
* [Rust SDK](https://github.com/derivexyz/derive-rs)

Lastly, for granular API details, refer either to the **API Reference** tab or the machine-readable specs — append the path to the base URL:

* [`/openapi.json`](https://v3.docs.derive.xyz/openapi.json) — REST methods
* [`/websocket.asyncapi.json`](https://v3.docs.derive.xyz/websocket.asyncapi.json) — WebSocket-only RPC methods
* [`/subscriptions.asyncapi.json`](https://v3.docs.derive.xyz/subscriptions.asyncapi.json) — subscription channels and their payloads

## No more intermediate "Derive Wallet"

There is no longer a distinction between your "wallet" and a separate "Derive Wallet" or "Smart Contract Wallet".
Every `wallet` / `owner` field is your own EOA or multisig.

Note that during the v2 -> v3 state migration all "Derive Wallets" will be automatically transferred to the user's EOA or multisig.

## Signed-action changes

### Nonces

In v3, a nonce is a **UTC timestamp in nanoseconds** (\~19 digits for 2025,
e.g. `1751558400000000000`) instead of ms in v2. A millisecond- or microsecond-scale nonce will be rejected.

Beyond the unit change, nonces now have varying validitiy windows depending on action type:

| Action type                                             | Nonce window (relative to server clock / creation) | Must increase? |
| ------------------------------------------------------- | -------------------------------------------------- | -------------- |
| withdraw, transfer, session key, whitelist, liquidation | ± 1 hour                                           | Yes            |
| order                                                   | 90 days before → 1 hour after                      | No             |
| rfq                                                     | ± 1 hour                                           | No             |
| vaults                                                  | 30 days before → 1 hour after                      | Yes            |

### Deposits

Deposits now happen directly on the ETH L1 instead of a `private/deposit` RPC call.

Refer to the [Depositing](/getting-started/depositing) guide or SDKs for more information.

### Transfers

* `private/withdraw` - gains `max_fee_usd`, `force_batch`, and `recipient` fields to allow for withdrawals to non owner accounts.
* `private/transfer_spot` - replaces `private/transfer_erc20`, and adds a `max_fee_usd` field to allow for automatic creation of new subaccounts.
* \[deprecated] `private/transfer_position` - same effect can be achieved with `private/transfer_positions` (single-leg).
* \[new] `private/transfer_spot_external` — transfers spot to other wallets.

Refer to [Transfers & Withdrawals](/trading/transfers-withdrawals#withdrawals) or SDKs for more information.

### Session keys

* `private/set_session_key` — replaces onchain session key registeration calls and `private/register_scoped_session_key`
* `private/edit_session_key` — existed in v2, but now edits only off-chain fields
  (`label`, `ip_whitelist`, `offchain_scopes`). Its v2 `disable` flag was removed.
* To retire a key, re-register the same key via `private/set_session_key` with a past or zero
  expiry. Replaces the `deregister_session_key` endpoint.

Refer to [Access scopes](/authentication/access-scopes) or SDKs for more information.

### RFQ quote signatures

Maker and taker quote (`send_quote`) `signature_expiry_sec` must satisfy roughly:

```
rfq_creation + ~11 min  ≤  signature_expiry_sec  ≤  now + 1 day
```

The lower bound is \~11 minutes after RFQ creation (a 600s validity window plus a 60s buffer);
the upper bound is the current server time plus 1 day. Exact bounds may vary by environment.

## Constants

The hosts and signing constants that changed between v2 and v3.

### Endpoints

| v2                              | v3                                  |
| ------------------------------- | ----------------------------------- |
| `https://api.lyra.finance`      | `https://api.derive.xyz/v3`         |
| `https://api-demo.lyra.finance` | `https://testnet.api.derive.xyz/v3` |

Note, for users with dedicated connections, there is no change in hostnames (the 'v3' path prefix can also be ignored).

See [Endpoints](/getting-started/introduction#endpoints) for the canonical host/path list and [Connecting](/connecting)
for the WebSocket handshake.

### Module addresses are now shared across deployments

The per-action module contracts — the `module` field selected inside a signed `Action` — are
**identical on testnet and mainnet** in v3. They are fixed protocol constants; hardcoding them
is safe.

| Module                         | Address                                      | Purpose                                        |
| ------------------------------ | -------------------------------------------- | ---------------------------------------------- |
| `TRADE_MODULE`                 | `0xB8D20c2B7a1Ad2EE33Bc50eF10876eD3035b5e7b` | Orders and trades                              |
| `TRANSFER_MODULE`              | `0x01259207A40925b794C8ac320456F7F6c8FE2636` | Spot transfers between subaccounts             |
| `WITHDRAW_MODULE`              | `0x9d0E8f5b25384C7310CB8C6aE32C8fbeb645d083` | Withdrawals to L1                              |
| `RFQ_MODULE`                   | `0x9371352CCef6f5b36EfDFE90942fFE622Ab77F1D` | RFQ execution and position transfers           |
| `EXTERNAL_TRANSFER_MODULE`     | `0x8F9B8f12ddA05FB1F0DDDDe8f5af8cECF54f8aC9` | Spot transfers to an external subaccount       |
| `WHITELISTED_RECIPIENT_MODULE` | `0xB86D6DE1b76c9839e4BA860848CD98A1dABd6B54` | Managing the recipient allow-list              |
| `VAULT_MODULE`                 | `0x2885c174ebf5524aED9c721d60c12b1537685186` | Vault actions (deposit, withdraw, curator ops) |

### Domain separator is recomputed per chain

Unlike the module addresses, the EIP-712 domain separator is **not** shared: v3 recomputes it
per chain. Note, since v3 is now on Ethereum Sepolia & Mainnet, the domain separators must be recomputer.
The `verifyingContract` is constant across every network (the v2 mainnet `Matching`,
`0xeB8d770ec18DB98Db922E9D83260A585b9F0DeAD`); only `chainId` differs, so testnet and mainnet
resolve to different separators.

```text theme={null}
domainTypeHash  = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
domainSeparator = keccak256(abi.encode(
    domainTypeHash,
    keccak256("Matching"),                        // name
    keccak256("1.0"),                             // version
    chainId,                                      // 1 mainnet, 11155111 testnet
    0xeB8d770ec18DB98Db922E9D83260A585b9F0DeAD    // verifyingContract (constant across networks)
))
```

Resultant values:

| Environment       | `chainId`  | `domainSeparator`                                                    |
| ----------------- | ---------- | -------------------------------------------------------------------- |
| Mainnet           | `1`        | `0xda616dfabb88681b08e1592820a41d55ddc62d68de110e327ae99d734506fe19` |
| Testnet (Sepolia) | `11155111` | `0x24d674cd5f2b9d564691c51e9d88f649b99246a2244dd74ce27b96578d773e85` |

### Auth headers: `X-Lyra*` → `X-Derive*`

| v2 header         | v3 header           |
| ----------------- | ------------------- |
| `X-LyraWallet`    | `X-DeriveWallet`    |
| `X-LyraTimestamp` | `X-DeriveTimestamp` |
| `X-LyraSignature` | `X-DeriveSignature` |
| `X-LyraScope`     | deprecated          |

See [Action signing](/authentication/action-signing) or SDKs for more information.

## Settlement changes

In v2, every action was a transaction on the Derive chain. In v3, every action is a zkVM op which is batched and settled together as one Ethereum L1 transaction.

* `transaction_id` -> `op_uuid`
* `tx_hash` on Derive Chain -> `tx_hash` on Ethereum L1 for whole batch
* `tx_status` -> `batch_status`

The full lineage is `op_uuid → operation → batch → L1 tx`, and `tx_hash` (plus `l1_block`) is populated from the batch once it settles.

### `BatchStatus`

| Stage     | Healthy     | Errored          | Explanation                                   |
| --------- | ----------- | ---------------- | --------------------------------------------- |
| Batching  | `Batching`  | `BatchingError`  | Ops processed by the sequencer                |
| Executing | `Executing` | `ExecutingError` | Batch run in settler to produce the new state |
| Proving   | `Proving`   | `ProvingError`   | zkVM proof being generated for the batch      |
| Settling  | `Settling`  | `SettlingError`  | Proof submitted to L1, awaiting confirmation  |
| Settled   | `Settled`   | `SettledError`   | L1 tx confirmed, `tx_hash` and `l1_block` set |

Note, all routes that return `tx_status` in v2 now return `batch_status` in v3.

## Other changes

Note for all of these, refer to the [API Reference](https://v3.docs.derive.xyz/api-reference) and json specs listed at the beginning of this document.

### `public/get_currency` & `public/get_all_currencies`

v3 introduces **risk universes**. A universe is a set of assets, managers and lending pools
that share one security module. Everything risk-related is keyed by `(asset, risk_universe_id)`,
so the same asset can carry different collateral discounts, OI caps and lending terms in
different universes. An insolvency is full contained to one universe.

Start with [Managers & Risk Universes](/trading/managers-and-risk-universes) — it covers
manager ids, universes, and how they decide what a subaccount can trade and hold.

Both routes keep their names and params. The per-currency object is restructured around that
model:

* **Risk data is per universe, not per manager address.** `managers[]` now carries a numeric
  `risk_universe_id` plus `sm` / `pm` manager ids. Discounts, OI caps and lending sit under
  `option`/`perp`/`spot[]` → `universes[]`. Match on the `risk_universe_id` returned by
  `private/get_subaccount`.
* **`spot` is a list.** A currency can have several collateral assets, each with its own
  address, `erc20` details, discounts and lending. This is the most likely break for clients
  that assumed one spot asset per currency.
* **Lending is no longer USDC-only**, and `instrument_types` and
  `srm_perp_margin_requirements` are gone. Infer instrument types from which of `option`,
  `perp` and `spot` are present.

### `private/get_subaccount`

New fields: **`manager_id`** (`u32`) and **`risk_universe_id`** (`u32`, the key into the
per-universe currency data above). Also:

* `currency` changed from a single string to a **`string[]`** — a breaking shape change.
* `margin_type` widened from a fixed enum (`PM` | `SM` | `PM2`) to an open string.
* New `failed_to_fetch` (bool) and `vault_deposit_holds` (array) fields.
* `projected_margin_change` is currently always `"0"` (not yet implemented in v3, unlike v2
  which computed a real value).

### History routes

* `public/get_trade_history` no longer accepts a `tx_hash` query param.
* **Status field:** deposit / withdrawal / erc20-transfer history **and** trade history (public
  and private) expose the settlement status as **`batch_status`** (v2's `tx_status` is renamed).
  All use the same set of status values (PascalCase strings) — success stages
  `Batching`, `Executing`, `Proving`, `Settling`, `Settled`, and their errored counterparts
  `BatchingError`, `ExecutingError`, `ProvingError`, `SettlingError`, `SettledError`. The
  value is `null` until the operation is picked up for settlement.
* **Deposit / withdrawal / erc20-transfer history:** each row now carries `operation_id`
  (string UUID), `batch_uuid`, and `batch_status`. `tx_hash` is **still present** (nullable)
  — it is not removed, but it now resolves to the settling L1 transaction.

### Channel changes

The subscription surface went from 16 channels in v2 to 13 in v3. The full ticker feed and
the settlement-filtered trade channels were removed; every other channel carried over.

| Change   | Channel                                                                                                                                                                                                                                                     |
| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Removed  | `ticker.{instrument_name}.{interval}` (full ticker feed)                                                                                                                                                                                                    |
| Retained | `ticker_slim.{instrument_name}.{interval}` (slim ticker feed)                                                                                                                                                                                               |
| Removed  | `trades.{instrument_type}.{currency}.{tx_status}` — settlement is no longer streamed. The live `trades.*` channels now carry the full trade detail (wallet, subaccount, fees, realized PnL); poll `public/get_trade_history` for `batch_status` / `tx_hash` |
| Removed  | `{subaccount_id}.trades.{tx_status}` — use `{subaccount_id}.trades` for fills and `private/get_trade_history` for settlement status                                                                                                                         |

### Deprecated endpoints

| Area                | v2 methods not currently exposed                                                                                 |   |
| ------------------- | ---------------------------------------------------------------------------------------------------------------- | - |
| Subaccount creation | `private/create_subaccount`, `public/create_subaccount_debug`                                                    |   |
| Deposits            | `private/deposit`, `public/deposit_debug` (deposits are now on-chain — see below)                                |   |
| Settlement history  | `public/get_option_settlement_history` (public variant dropped; `private/get_option_settlement_history` remains) |   |
| Feeds               | `public/get_spot_feed_history`, `public/get_spot_feed_history_candles`, `public/get_perp_impact_twap`            |   |
| Instruments         | `public/get_instruments` (use `get_all_instruments` / `get_all_live_instruments`)                                |   |
| Assets / misc       | `public/get_asset` (singular; `get_assets` remains), `public/get_bridge_balances`                                |   |
| Order creation      | `private/algo_order`, `private/trigger_order` (use `private/order` with algo/trigger fields)                     |   |
| Order queries       | `private/get_orders` (use `get_open_orders` / `get_order_history`)                                               |   |


## Related topics

- [DevEx improvements](/migrating/v3-improvements.md)
- [Introduction](/getting-started/introduction.md)
- [Quickstart](/getting-started/quickstart.md)
