Skip to main content
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.
If at any step there is not enough detail, refer to the Guides section or the relevant public SDK repos: Lastly, for granular API details, refer either to the API Reference tab or the machine-readable specs — append the path to the base URL:

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

Porting a v2 encoder? v3 rejects payloads v2 accepted — sub-1e12 precision, dirty address padding, non-canonical int256. If a ported action returns 14014, send its data to public/decode_action: it names the word and field that broke instead of failing as a bad signature.

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:

Deposits

Deposits now happen directly on the ETH L1 instead of a private/deposit RPC call. Refer to the 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 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 or SDKs for more information.

RFQ quote signatures

The maker quote (send_quote) and taker execute (execute_quote) signature_expiry_sec must satisfy:
The lower bound is 31 minutes after RFQ creation (the RFQ’s 30-minute validity window plus a 1-minute buffer). The upper bound is the current server time plus 1 day.

Constants

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

Endpoints

Note, for users with dedicated connections, there is no change in hostnames (the ‘v3’ path prefix can also be ignored). See Endpoints for the canonical host/path list and 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.

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.
Resultant values:

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

See 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

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 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 — 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 is gone. Infer instrument types from which of option, perp and spot are present.
  • srm_perp_margin_requirements moved. It keeps its v2 fields (im_perp_req, mm_perp_req, max_leverage) but is no longer top-level: the rates are per risk universe, so it now sits on each entry of perp.universes[]. public/get_instrument reports the same object inside perp_details for that instrument’s universe.

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 projects to a minute past the account’s own next option expiry, not v2’s fixed 08:01 UTC.

public/get_ticker & public/get_tickers

public/get_ticker now returns the slim ticker. It is the same object as one entry of public/get_tickers and the data of a ticker_slim.{instrument_name}.{interval} update. v2 returned the full ticker, which v3 dropped along with its channel (see Channel changes).
  • Live fields use the slim short keys. best_bid_price / best_ask_price → b / a, best_bid_amount / best_ask_amount → B / A, index_price → I, mark_price → M, min_price / max_price → minp / maxp, timestamp → t. option_pricing uses short keys too (d, g, v, i, …).
  • Instrument metadata moved to public/get_instrument. Tick and amount steps, fees, activation times, option_details, perp_details and erc20_details are no longer on the ticker.
  • Dropped: five_percent_bid_depth, five_percent_ask_depth and the per-margin-type open_interest map. Total open interest is stats.oi. OI caps are per risk universe in public/get_currency.
  • New: 24h stats, and f, the hourly funding rate (perps only).
public/get_tickers is unchanged from v2. It takes the same params and returns the same map of instrument name to slim ticker.

public/get_all_instruments

v2 described expired as “include expired instruments”: with expired: true, options came back live and expired together. In v3 the flag selects one or the other. For options, expired: true returns only expired options, and expired: false returns live options as before. To list both, make both calls and merge by instrument_name.
  • An empty listing returns instrument_not_found (12001), as in v2. Treat it as empty when merging.
  • Perps and spot ignore expired.
  • An option enters the expired: true listing up to a minute before it expires, so it does not drop out of both listings at expiry.

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, Da, Proving, Settling, Settled, and their errored counterparts BatchingError, ExecutingError, DaError, 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.

Market maker protection

The MMP methods keep their names, params and responses. What a freeze stops is narrower:
  • A freeze stops only mmp: true orders and quotes. In v2, a frozen subaccount’s resting orders could not fill in that currency, with or without mmp. In v3, its resting orders without mmp still fill. Set mmp: true on every order and quote that a freeze must stop.
  • A freeze cancels open MMP quotes, not all quotes. In v2, a freeze tripped by an orderbook fill cancelled every open quote. In v3, quotes without mmp stay open.
See Market Maker Protection.

Deprecated endpoints