Migrating from v2? See Migrating from V2.
Public subaccount lookup
public/get_subaccountsreturns the subaccount IDs that a wallet owns. It takes the samewalletparam and returns the same shape asprivate/get_subaccounts, but needs no authentication. A wallet with no account returns an empty list.
Maker RFQ history
private/get_maker_rfq_historylists filled, cancelled and expired RFQs that you were eligible to quote as a maker, including RFQs you did not quote. It takes the same filters asprivate/poll_rfqs, butfrom_timestampandto_timestampfilter on RFQ creation time. Withoutfrom_timestamp, it returns the 30 days beforeto_timestamp(or before now). Results have theprivate/poll_rfqsRFQ shape, withfill_rateandrecent_fill_rateset tonull.private/poll_rfqsreturns only open RFQs, including partially filled ones. In v2 it also returned filled, cancelled and expired RFQs. Useprivate/get_maker_rfq_historyfor those.
Forwarded withdrawals to home chains
Withdrawals of bridged tokens can now go back to their home chain in one step. See Forwarded withdrawals.private/register_withdraw_addressreturns a withdraw address for a(recipient, chain_id). Withdraw to it withprivate/withdraw, and Derive bridges the payout to the recipient. Derive pays the bridge fee.private/get_withdrawal_historyentries to a withdraw address carry a newforwardobject with the forward’s status, bridge message id and destination tx.private/get_withdraw_addresseslists your withdraw addresses, where each forwards to, and whether it is on your whitelist.- New errors from
private/register_withdraw_address:withdraw_destination_unsupported(11046) andinvalid_withdraw_recipient(11047).
Breaking
public/get_all_instruments with expired: true lists only expired options
For instrument_type: "option", expired: true now returns only expired options.
Previously it returned live and expired options together. expired: false is unchanged and
returns live options.risk_universe_idnow filters expired options. It was ignored for them before.pagination.countandnum_pagescount only expired options.- An empty listing returns
instrument_not_found(12001), as every other empty listing does. - Perps and spot ignore
expired, as before.
expired: true listing up to a minute before it expires, so it does not
drop out of both listings at expiry. For a few minutes after settlement, its
option_details.settlement_price in this listing can be null. public/get_instrument returns
the settlement price at once.To list live and expired options in a way that works before and after this change, call
expired: false, then expired: true. Merge the results by instrument_name,
and treat instrument_not_found (12001) as an empty result.History endpoints report asset as the currency name
private/get_deposit_history, private/get_withdrawal_history and
private/get_erc20_transfer_history now return asset as the currency name (e.g. "USDC"),
matching v2 and the asset_name these operations are submitted with. Previously the field
carried the protocol spot-asset contract address. private/get_withdrawal_history still reports
the on-chain token under erc20_address.Breaking
public/get_assets is removed
The route reported every asset of one asset_type for a currency, but only ever from the
fallback risk universe, so its perp and lending figures did not describe any tradeable market.
Nothing consumed it.Read assets from the endpoints that are keyed by risk universe instead:public/get_currencyandpublic/get_all_currencies— the option, perp and spot assets registered to a currency, with per-universe OI, discounts and lending.public/get_all_instrumentsandpublic/get_instrument— tradeable instruments and their type-specific details.
Perp margin rates and max leverage are public again
srm_perp_margin_requirements returns, carrying the v2 field set — im_perp_req, mm_perp_req
and max_leverage — as decimal strings. max_leverage is 1 / im_perp_req rounded to 2
decimals.It is reported in two places, both keyed by risk universe:public/get_currencyandpublic/get_all_currencies— on each entry ofperp.universes[].public/get_instrumentandpublic/get_all_instruments— insideperp_details, for the instrument’s own risk universe.
null) where no Standard Risk Manager in that universe prices the
currency, and on option assets. A currency whose perp market has been scaled out reports
im_perp_req of "0" and max_leverage of "0" — v2’s behaviour, retained.The v2 path is not restored: the object no longer sits at the top level of the currency
response. See Migrating from V2.Deposits that arrive over a bridge carry their origin
public/get_pending_deposits and private/get_deposit_history now report where a deposit came
from when it reached your escrow over a bridge. No method was added, and nothing about crediting
is different: a bridge delivers an ordinary ERC-20 transfer into the escrow, which is swept and
credited exactly as a transfer from your own wallet would be.public/get_pending_depositslists every bridge into your escrows inpending_deposits[], one entry per deposit throughout. While the funds are in transit, before any L1 transaction exists, the entry hasstatus: "bridging"; once the L1 deposit is recorded it is that deposit’s entry, with its ownstatus. Key a bridging entry onbridge_to_l1.bridge_id: it gainstx_hashandlog_indexwhen the deposit lands.- Now optional on
pending_deposits[]entries, absent whilebridging:action_id,tx_hash,log_indexandblock_number.amountis also absent until the bridge delivers, then what arrived. They remain present on every L1 entry. - Entries carry an optional
bridge_to_l1object:rail,bridge_id,source_chain_id,source_tx_hash,source_address(your wallet on the source chain),bridge_status,substatus,deposit_address,token,expected_amount(source-chain units, an upper bound before fees),receiving_tx_hash,receiving_token,receiving_amount,delivered_at_msandexpires_at_ms.bridge_statusmovessubmitted→delivered→matched, or endsfailed(refunded on the source chain),expired(unresolved pastexpires_at_ms) orunmatched(delivered but not credited within 24 hours; contact support). private/get_deposit_historyentries gain an optionalbridge_originobject withrail,source_chain_id,source_tx_hashandsource_address, so provenance survives after the deposit is applied.private/get_deposit_historyentries also gain optionall1_tx_hashandl1_sender: the L1 transaction that moved the funds into your deposit address, and who sent it (your wallet, an exchange, or a bridge’s relayer).tx_hashremains the settlement batch’s commit,nulluntil the batch settles. Both are absent when no tracked L1 transfer backs the deposit, or when one action credited several transfers at once.
rail is lifi today; the field exists so a second bridge protocol can be added without a
breaking change. A bridge sent outside Derive’s own quotes is not tracked and appears as a plain
deposit, exactly as before.See Programmatic Onboarding.Breaking
Testnet settlement contracts redeployed
The Derive v3 testnet (Ethereum Sepolia, chain ID11155111) settlement contracts were
redeployed. The sequencer only watches the new deployment: a deposit sent to a previous
OnchainActionManager is not credited, and earlier withdrawal outboxes receive no new payouts.The mock ERC-20 tokens are unchanged, but the protocol spot-asset addresses (the
asset
argument to deposit and depositToNewSubaccount) are new. Read them from
public/get_risk_universes or public/get_all_currencies rather than hardcoding them.If you hardcoded a testnet ACTION_MANAGER, update it — see Contracts
and Programmatic Onboarding. Signing constants (module addresses,
ACTION_TYPEHASH, the EIP-712 domain separator) are unaffected.Breaking
Proven operations share one per-wallet rate-limit budget
Every method that submits a proven operation outside order matching now carries theproven
rate-limit class and decrements a single per-wallet budget of 1 TPS by default (5 points per
5s window on mainnet). Previously most of these methods drew from the far larger non_matching
budget, and a few had their own per-method budget.Affected methods: private/transfer_spot, private/transfer_spot_external,
private/transfer_positions, private/withdraw, private/liquidate,
private/set_session_key, private/delete_subaccount, private/update_whitelisted_recipients,
private/create_vault, private/mint_vault_shares, private/burn_vault_shares,
private/request_vault_deposit, private/request_vault_withdraw,
private/cancel_all_vault_requests, private/force_burn.public/getRateLimitsreports the budget underremaining_per_endpoint.proven.- Rejections use the usual
-32000code with messageRate limit exceeded: {wallet}-proven. - Order, quote, and RFQ execution methods are unaffected.
Breaking
private/create_session_key renamed to private/set_session_key
The route now matches what the action does (register, refresh, or retire a key by writing a
nearer expiry) and its L1 counterpart, the SetSessionKey onchain action. Everything about the
rename is mechanical:- Route:
private/create_session_key→private/set_session_key(and the debug variantprivate/create_session_key_debug→private/set_session_key_debug). The old route names are gone. - Protocol scope:
create_session_key→set_session_keyinprotocol_scopeson registration requests and inprivate/session_keysresponses. - TypeScript SDK:
client.sessionKeys.create(...)→client.sessionKeys.set(...);ProtocolScopeCode.CreateSessionKey→ProtocolScopeCode.SetSessionKey.
Signed action payloads now use canonical Solidity ABI encoding
Every signed action’sdata bytes are now exactly what Solidity abi.encode produces — if you
sign with a standard ABI coder (ethers AbiCoder, viem encodeAbiParameters, abi.encode in a
contract), nothing changes for you. Hand-rolled encoders must drop the previous non-standard
layouts, which are no longer accepted:private/set_session_key and the L1 SetSessionKey action (type 51)The payload is canonical abi.encode(address sessionKey, uint256 expirySec, uint256[] scopes, uint256[] subaccountIds) — dynamic arrays sit behind standard offset words, each prefixed by its
length word. The old hand-packed layout ([key][expiry][scopeCount][subaccountCount][scopes…] [subaccounts…], no offsets) is rejected. In Solidity, build the L1 payload with a plain
abi.encode call instead of packing words manually.private/update_whitelisted_recipientsThe payload is canonical abi.encode(address[] add, address[] remove). The old hand-packed
layout ([addCount][removeCount][adds…][removes…], no offsets) is rejected.Orders (private/order) and liquidations (private/liquidate)Negative int256 values (limit_price, amount, price_limit) are now full 32-byte
two’s-complement words, sign-extended through the high 16 bytes — standard ABI coder output.
Previously the server produced and expected a non-standard form with the value packed into the
low 16 bytes only, which broke standard-signing clients on negative liquidation price limits.
Non-negative values encode identically, so most existing signatures are unaffected.Decoding is also strict across these payloads: offset words must point at their canonical
positions, lengths must match the byte count, and value words must fit their field’s range —
dirty high bytes are rejected instead of silently truncated, matching Solidity abi.decode.See Action signing for the per-module payload layouts.Breaking
Settlement status unified under batch_status
The tx_status field and the TxStatus type are removed. Every route and channel that reported
settlement status now uses batch_status, typed as the shared BatchStatus enum — Batching,
Executing, Proving, Settling, Settled and their …Error counterparts — and null until
the operation is picked up into a batch.This replaces the old granular transaction states (applied, in_batch, proving, submitted)
and the lowercase matching-engine values (requested, pending, settled, reverted,
ignored, timed_out), which are all gone across the entire API. There is now one status
field, one set of values.REST methodspublic/get_trade_history— optionalbatch_statusrequest filter (unset returns trades in every batch state) and on each tradeprivate/get_trade_history—batch_statuson each tradeprivate/order,private/replace—batch_statuson eachtrades[]entry (alwaysnullat placement)private/send_quote,private/cancel_quote,private/replace_quote,private/get_quotes,private/transfer_positions—batch_statuson each quote (present only for executed quotes)private/get_deposit_history,private/get_withdrawal_history,private/get_erc20_transfer_history,private/get_funding_history—batch_statuson each rowpublic/get_transaction—statususes the sameBatchStatusvalues
trades.{instrument_type}.{currency}.{tx_status}and{subaccount_id}.trades.{tx_status}— removed; settlement is no longer streamed. Pollpublic/get_trade_historyforbatch_status/tx_hashinsteadtrades.{instrument_name},trades.{instrument_type}.{currency}— each event now carries the full trade detail (wallet,subaccount_id,liquidity_role, fees, realized PnL){subaccount_id}.trades,{subaccount_id}.quotes—batch_statuson each payload item
instrument_type is now a typed enum
On the trade-history schemas, instrument_type is typed as the AssetType enum
(erc20 / option / perp) instead of a free-form string.See Breaking changes → Settlement status for the full route table
and value reference.