Skip to main content

Node snapshot reads

Read queries on POST /info. The endpoint, the request envelope, the number planes and the error shape are on that page and apply to every query here.

Node snapshot query types

These reads answer from the node's committed state, over the same {type, data} envelope and the same conventions as every read above: money as decimal strings, addresses as 0x-hex, asset ids as unsigned integers, map keys in sorted order. Each is a keyed lookup, not a scan, except where the set is inherently small (markets, vaults, validators).

Perpetual market reads are on the perpetual queries page, and spot, spot-margin and Earn reads on the spot & margin queries page. The reads below are the ones that belong to no single product: exchange status, open-order helpers, liquidation, rate limits, vaults, validators and multi-sig.

Global exchange trading status

Global trading status. No parameters.

{ "type": "exchange_status" }

Response

{
"data": {
"type": "exchange_status",
"chain_identity": "c114514-t1788275280000-g8f6fce34e462c553",
"spot_disabled": false,
"post_only": false,
"mip3_enabled": true,
"frozen": false,
"timestamp": 1735689600000
}
}
FieldTypeMeaning
chain_identitystringWhich chain answered. See chain identity below
spot_disabledboolSpot trading globally disabled
post_onlyboolA post-only window is in force — new orders must be maker-only
frozenboolThe chain is in a pending upgrade halt
timestampuint64Consensus block time, ms — the "as of" for every field above
mip3_enabledbooltrue once any MIP-3 market/pair spec is registered
info

This reports current status only. It does not return the pending upgrade height or the node's replay progress. frozen shows a halt is coming; it does not give a date.

Chain identity

A chain_id does not identify a chain. Two chains can run the same chain_id, and everything else that is stable about them — the endpoint shape, the validator addresses, the eth_chainId answer — can be identical too. Only the moving state differs, and moving state cannot be asserted on.

chain_identity is the value that does identify one. It reads c<chain_id>-t<chain start, ms>-g<first 16 hex of the genesis hash>. The genesis hash folds the validator set, the epoch length and the initial state root, so two chains that agree on all three parts are the same chain.

Assert it at startup and refuse to run on a mismatch. Put the expected value in your configuration next to the endpoint URL, read exchange_status before your first write, compare the two strings, and exit on a difference. Requiring the URL to be configured is not enough on its own: that stops a default endpoint from being used, not a wrong one. Rows written against the wrong chain are byte-identical in shape to correct rows and carry a colliding chain_id as their only provenance, so the mistake cannot be found afterwards.

ValueMeaningWhat to do
c…-t…-g…The chain the node runsCompare to your configured constant
underivableThe node proves no genesisTreat as a mismatch. Never as a match
absentA node older than the fieldTreat as a mismatch

underivable can never collide with a real chain's value, because a derived identity always starts c and a digit.

What it changes on. Only a re-genesis of the chain you are reading. It survives a node restart, a release, a node upgrade and a validator-set change, and a re-genesis of a DIFFERENT chain does not move it. So cache it for the life of your process, and re-read it on every reconnect to a new endpoint.

It also survives any config edit the node still boots on. Two edits are not in that set: removing the genesis file turns the value to underivable, and changing the genesis timestamp under a genesis file stops the boot. Neither can produce a false match — one refuses, the other never starts.

warning

frontend_open_orders is removed (folded into open_orders, wire-v2 phase 2). A request now returns 400 UNKNOWN_TYPE. The TIF / cloid / trigger detail it used to carry is on every open_orders row already — see that entry.

Active TWAP parents for an account

The account's active TWAP parent orders: the live slice schedulers, with total size versus executed size. A completed or cancelled TWAP leaves this set — it is the live set, not history. For slice-fill history, use user_twap_slice_fills. Required: address (0x hex).

{ "type": "user_twaps", "address": "0x<addr>" }

Response

{
"data": {
"type": "user_twaps",
"address": "0x<addr>",
"twaps": [
{
"twap_id": 1,
"coin": "BTC",
"side": "B",
"sz": "1.5",
"executed_sz": "0.6",
"slices_total": 10,
"slices_done": 4,
"delay_ms": 3000,
"last_fire_ts": 42000,
"reduce_only": false
}
]
}
}
FieldTypeMeaning
twaps[*].twap_iduint64Parent TWAP id (pass to twap_cancel)
twaps[*].coinstringMarket symbol
twaps[*].side"B" / "A"Side token — the same "B"/"A" form as user_fills
twaps[*].szDecimal stringParent total size (whole units)
twaps[*].executed_szDecimal stringSize already filled by fired slices (whole units)
twaps[*].slices_totaluint32Slice count the parent was scheduled with
twaps[*].slices_doneuint32Slices fired so far
twaps[*].delay_msuint64Inter-slice delay (ms)
twaps[*].last_fire_tsuint64Last slice fire timestamp (consensus ms)
twaps[*].reduce_onlyboolParent is reduce-only

Rows are listed in ascending twap_id order. There is no duration field: compute it as slices_total × delay_ms. The wire carries only the independent values.

Summary of all vaults

All vaults summary. No parameters.

{ "type": "vault_summaries" }

Response

{
"data": {
"type": "vault_summaries",
"vaults": [
{ "id": 7, "address": "0x<vault>", "leader": "0x<leader>", "name": "MLP", "tvl": "10000000000", "follower_count": 2, "kind": "user" }
]
}
}
FieldTypeMeaning
vaults[*].iduint64Vault id
vaults[*].address / leaderhex addressVault on-chain address / leader
vaults[*].namestringDisplay name of the vault. Present on every row
vaults[*].tvldecimal stringMark-to-market NAV, whole-USDC — same figure as vault_state.tvl
vaults[*].follower_countuint64Number of share holders
vaults[*].kind"user" | "metaliquidity"Vault kind

Every vault appears, and each row names its leader. To list the vaults led by one address, filter these rows on leader — there is no per-leader read.

A user's action stats

A user's action counters. Required: address (0x hex).

Despite the name, this does not report a rate-limit budget. It returns nonce and action counters only, not bucket state. No read exposes remaining budget — track your own spend against rate limits.

{ "type": "user_rate_limit", "address": "0x<addr>" }

Response

{
"data": { "type": "user_rate_limit", "address": "0x<addr>", "last_nonce": 9, "pending_count": 2, "lifetime_count": 123 }
}
FieldTypeMeaning
last_nonceuint64Last accepted action nonce
pending_countuint32Pending (in-flight) action count
lifetime_countuint64Lifetime actions submitted

An address with no record reads as all zeros.

All approved broker-fee grants

Every builder-fee grant an account has approved, and the bps ceiling on each. Required: address (0x hex). To check one (address, builder) pair, look the builder up in this list — an address that is absent is not approved, which is the same answer as a "0" ceiling.

{ "type": "approved_brokers", "address": "0x<addr>" }

Response

{
"data": {
"type": "approved_brokers",
"address": "0x<addr>",
"builders": [
{ "builder": "0x<builder_a>", "max_fee_bps": "25" },
{ "builder": "0x<builder_b>", "max_fee_bps": "50" }
]
}
}
FieldTypeMeaning
builders[*].builderhex addressApproved builder address
builders[*].max_fee_bpsstringApproved bps ceiling as a decimal string of whole basis points

Builders list in ascending address order; an account with no approvals returns an empty array.

Current per-validator oracle vote metadata

Current validator L1 votes. No parameters.

{ "type": "validator_l1_votes" }

Response

{
"data": {
"type": "validator_l1_votes",
"latest_round": 0,
"votes": [ { "round": 43000000, "validator": "0x<validator>", "submitted_at": 1700000000000 } ]
}
}
FieldTypeMeaning
latest_rounduint64A governance proposal-id counter. It is NOT the latest vote round, and it is not the maximum round in votes. The governance proposal path increments it; nothing derives it from the votes. On a chain that has opened no proposal it stays 0 while votes[*].round runs into the millions. Never use it to page or to date the votes
votes[*].rounduint64Vote round
votes[*].validatorhex addressCasting validator
votes[*].submitted_atuint64Submission timestamp (consensus ms)

The vote payload is opaque oracle bytes, decoded internally. This read reports metadata only, not the raw payload.

Per-validator stake and status snapshot

Per-validator snapshot: stake, status, and delegation for every validator in the active validator registry (a small, bounded set), in ascending key order.

Optional: address (0x hex). Naming an address adds that caller's own stake to every row; it changes nothing else.

{ "type": "validator_summaries", "address": "0x<addr>" }

Response

{
"data": {
"type": "validator_summaries",
"total_stake": "1400",
"n_active": 1,
"validators": [
{
"validator": "0x1111…", "signer": "0xa1a1…", "validator_index": 0,
"display_name": "alice.mtf",
"stake": "1000", "self_stake": "100", "delegated_stake": "900",
"your_stake": "7", "commission_bps": "500",
"is_active": true, "is_jailed": false, "jailed_at": null,
"unjail_at": null, "first_active_epoch": 2
}
]
}
}
FieldTypeMeaning
total_stakedecimal stringΣ stake across all validators
n_activeuint64Size of the active set
validators[*].validator0x addressValidator primary address
validators[*].signer0x addressOperational signer (hot key)
validators[*].validator_indexuint32Consensus index
validators[*].display_namestring | nullThe operator's chosen handle (set_display_name), or null when it set none
validators[*].stakedecimal stringTotal stake: self plus everyone else's
validators[*].self_stakedecimal stringValidator's own contribution
validators[*].delegated_stakedecimal stringstake − self_stake: everything staked by someone OTHER than the validator
validators[*].your_stakedecimal string | nullThe stake the REQUESTING address has delegated to this validator
validators[*].commission_bpsstringCommission in whole basis points, as a decimal string
validators[*].is_activeboolIn the active set this epoch
validators[*].is_jailedboolCurrently jailed
validators[*].jailed_atuint64 | nullJail start ts (null if not jailed)
validators[*].unjail_atuint64 | nullEarliest unjail ts (null if not jailed)
validators[*].first_active_epochuint64First epoch the validator was active

display_name of null means UNSET, never "unknown". Fall back to the address. Do not invent a name, and do not treat null as a node that predates the field.

your_stake distinguishes two different blanks. "0" means the request named an address and that address has delegated nothing to THIS validator. null means the request named no address, so the field is about nobody — render the column as empty, not as a zero balance.

delegated_stake is derived, not stored. It is exactly stake − self_stake, so it can never drift from the two figures beside it. Do not sum it across rows to get total_stake: that sum excludes every validator's self-stake.

There is no epoch key, and there was never a live one. The chain's current-epoch counter has no production writer, so any value served would be a constant rather than the chain's real epoch. Read first_active_epoch per validator instead.

n_recent_blocks is not tracked on-chain — omitted rather than fabricated.

Advertised peer roster

The nodes this deployment advertises for peer discovery. No parameters. Network topology, not committed state.

Live

A live node answers the peers shape below. The previous shape, { "root_ips": ["host:port", ...] }, is removed — there is no root_ips key.

{ "type": "gossip_root_ips" }

Response

{
"data": {
"type": "gossip_root_ips",
"peers": [
{
"id": 3,
"gossip": "203.0.113.7:4001",
"peer_rpc": "203.0.113.7:4002",
"auth": "203.0.113.7:4003",
"pubkey_hex": "02ab..."
}
]
}
}
FieldTypeMeaning
peersobject[]One row per advertised node. Empty when the deployment advertises nothing.
peers[*].iduint16The node's numeric id
peers[*].gossipstringPublic gossip endpoint, host:port
peers[*].peer_rpcstringPublic peer-RPC endpoint, host:port
peers[*].authstringPublic auth endpoint, host:port
peers[*].pubkey_hexstring (optional)Compressed secp256k1 public key for the peer's TCP auth. The key is absent when the operator did not publish it.

Why a row holds all three ports. A row is a copy-shaped peer config: the five fields map one-to-one onto a joining node's own peer entry, so you paste a row and dial it.

Where the rows come from. Each node serves an operator-curated roster from its own config. The roster states public reachability. It is not the node's internal dial list, and no address from that dial list can appear here.

A node that advertises nothing is absent from the rows. There is no fallback. A validator can run, vote and serve while publishing no address — it does not appear. An empty peers array is therefore the honest answer for a deployment that advertises nothing, not an error and not a sign of an unhealthy node.

The roster reflects node config published at startup. It is not committed state and is not folded into the AppHash.