Documentation Index
Fetch the complete documentation index at: https://hypernode-docs.polynode.dev/llms.txt
Use this file to discover all available pages before exploring further.
Two consensus layers
hypernode merges two data sources into one stream. Every event is tagged with its consensus state:
| Layer | consensus | What you see | Latency |
|---|
| Pre-consensus | "pre" | Cancel and modify actions before block commit | ~1s ahead of public API |
| Confirmed | "confirmed" | Every order, fill, cancellation, rejection after block commit | ~0.5s behind live |
Pre-consensus events are raw user-submitted actions intercepted from the gossip protocol before validators commit them. Confirmed events are the validator output after matching, filling, or rejecting.
Event structure
{
"block": 70852325,
"timestamp": 1776147484247,
"type": "cancel",
"consensus": "pre",
"user": "0x31ca8395cf837de08b24da3f660e77761dfb974b",
"asset": "ALGO",
"asset_id": 158,
"data": {
"cancels": [
{"asset": 158, "oid": 380721371885}
]
},
"network": "mainnet"
}
Fields
| Field | Type | Description |
|---|
type | string | Event type (see table below) |
consensus | string | "pre" (gossip) or "confirmed" (node) |
timestamp | number | Unix milliseconds |
block | number | L1 block number. Populated for pre-consensus events; 0 for confirmed events |
user | string | Wallet address |
asset | string | Human-readable asset name. Absent on scheduleCancel and setReferrer |
asset_id | number | Numeric asset identifier. Present on pre-consensus events; may be absent on some confirmed events |
data | any | Event-specific payload. Usually an object, but a number for scheduleCancel and a string for setReferrer |
network | string | "mainnet" or "testnet" |
Event types
Confirmed events
These appear after the validator processes the action. Every order placed on HyperLiquid produces one of these:
| Type | Description | Key data fields |
|---|
order | New resting limit order (data.status is "open") | coin, side, px, sz, oid, tif |
order_filled | Order filled immediately | coin, side, px, sz, oid |
order_canceled | Order was canceled | coin, side, px, sz, oid |
order_rejected | Order rejected (see rejection reasons below) | coin, side, px, sz, status |
fill | Trade execution | coin, side (A/B), px, sz, dir, closedPnl, fee, liquidation |
triggered | Trigger/stop order activated (rare) | coin, side, px, sz |
siblingFilledCanceled | Sibling order canceled due to fill (rare) | coin, side, px, sz, oid |
perpMaxPositionRejected | Rejected due to max position limit (rare) | coin, side, px, sz |
The "order" type name covers new resting orders where data.status is "open". There is no separate order_open type on the wire. To identify new resting orders, filter for "order" and check data.status === "open".
Rejection reasons in data.status: badAloPxRejected, perpMarginRejected, insufficientSpotBalanceRejected, iocCancelRejected, reduceOnlyCanceled, reduceOnlyRejected, minTradeNtlRejected, selfTradeCanceled.
Pre-consensus events (from gossip protocol)
These arrive before the validator commits them to a block:
| Type | Description | Key data fields |
|---|
cancel | Cancel by order ID | cancels[].asset, cancels[].oid |
cancelByCloid | Cancel by client order ID | cancels[].asset, cancels[].cloid |
batchModify | Modify existing orders | modifies[].oid, modifies[].order.price/size |
scheduleCancel | Dead man’s switch | data is a number (cancel timestamp) |
setReferrer | Set referral code | data is a string (referral code) |
evmRawTx | Pre-block HyperEVM transaction | data (raw RLP-encoded signed tx hex string) |
Confirmed order example
{
"block": 0,
"timestamp": 1776147483883,
"type": "order",
"consensus": "confirmed",
"user": "0xfc667adba8d4e79a22cac8e8f6d2a5b5c17cfa94",
"asset": "BTC",
"data": {
"status": "open",
"coin": "BTC",
"side": "B",
"px": "74400.0",
"sz": "0.04158",
"oid": 380721383669,
"orderType": "Limit",
"tif": "Alo",
"reduceOnly": false,
"cloid": "0x000000334e47524d534c363834483736",
"hash": "0x9d660b786cd389ac9edf04391b7dd901...",
"builder": null,
"origSz": "0.04158"
},
"network": "mainnet"
}
Fill example
{
"block": 0,
"timestamp": 1776147483883,
"type": "fill",
"consensus": "confirmed",
"user": "0x9690bcfc16394e2bd25b00e83d6b06c9ab3f5de5",
"asset": "HYPE",
"data": {
"coin": "HYPE",
"side": "B",
"px": "22.608",
"sz": "442.0",
"dir": "Close Short",
"closedPnl": "0.920686",
"fee": "0.035973",
"feeToken": "USDC",
"oid": 380721384070,
"tid": 910601406,
"hash": "0xa9a32c7c2b7fb452ab1c...",
"crossed": true,
"builder": null,
"builderFee": null,
"liquidation": null
},
"network": "mainnet"
}
Fill dir field values
| Value | Meaning |
|---|
Open Long | New long position opened |
Open Short | New short position opened |
Close Long | Existing long position closed |
Close Short | Existing short position closed |
Long > Short | Position flipped from long to short |
Short > Long | Position flipped from short to long |
Buy / Sell | Spot market fills |
The side field is separate: B = bid (buy order), A = ask (sell order).
Liquidation fill example
The data.liquidation field is always present on fill events. It is null when the fill is not part of a liquidation, and an object when it is. Do NOT check "liquidation" in data — that will be true for every fill. Check data.liquidation !== null instead.
Liquidation fills carry a populated data.liquidation object in addition to the standard fill fields. Use the liquidations_only filter to subscribe to only these events.
{
"block": 0,
"timestamp": 1776147612530,
"type": "fill",
"consensus": "confirmed",
"user": "0xf27f22d0988f958837c4393284297377a00387ed",
"asset": "SUI",
"data": {
"coin": "SUI",
"side": "A",
"px": "88.588",
"sz": "142.0",
"dir": "Close Long",
"closedPnl": "-123.45",
"fee": "0.0012",
"feeToken": "USDC",
"oid": 381234567890,
"tid": 912345678,
"hash": "0x3a4b5c...",
"crossed": true,
"liquidation": {
"liquidatedUser": "0xf27f22d0988f958837c4393284297377a00387ed",
"markPx": "88.588",
"method": "market"
}
},
"network": "mainnet"
}
Both sides of a liquidation (the liquidated user and the liquidator) appear as separate fill events with the same tid and hash. Correlate them using these shared fields to see the full liquidation flow.
Liquidation object fields:
| Field | Description |
|---|
liquidatedUser | Address of the account being liquidated |
markPx | Mark price at the moment of liquidation |
method | Liquidation method (currently "market" for all observed liquidations) |
evmRawTx example (pre-block HyperEVM)
Every HyperEVM smart contract transaction appears as an evmRawTx event before it is included in a block. The data field is a hex string containing the full EIP-2718 envelope: a 1-byte tx type prefix followed by RLP-encoded transaction fields. There are no top-level decoded fields — you decode the hex to extract to, calldata, chainId, gas, etc.
{
"block": 70861403,
"timestamp": 1776225421823,
"type": "evmRawTx",
"consensus": "pre",
"user": "0x000000362b9ddd0000000000333ce9b04c000000",
"data": "02f8d48203e782071084b2d05e0085746a52...",
"network": "mainnet"
}
The user field on evmRawTx events is a gossip internal identifier, not the transaction sender. To get the real sender, you must recover it from the signature during decoding.
Decoding data (Python)
The hex starts with the EIP-2718 tx type byte (0x02 for EIP-1559). Everything after that is RLP:
import rlp
from eth_hash.auto import keccak
def decode_evm_raw_tx(hex_data: str) -> dict:
raw = bytes.fromhex(hex_data)
tx_type = raw[0]
if tx_type == 0x02:
# EIP-1559: [chainId, nonce, maxPrioFee, maxFee, gas, to, value, data, accessList, v, r, s]
decoded = rlp.decode(raw[1:])
return {
"tx_hash": "0x" + keccak(raw).hex(),
"tx_type": tx_type,
"chain_id": int.from_bytes(decoded[0], "big"),
"nonce": int.from_bytes(decoded[1], "big"),
"max_fee_per_gas": int.from_bytes(decoded[3], "big"),
"gas_limit": int.from_bytes(decoded[4], "big"),
"to": "0x" + decoded[5].hex() if decoded[5] else None,
"value": int.from_bytes(decoded[6], "big"),
"calldata": decoded[7].hex(),
"function_selector": "0x" + decoded[7][:4].hex() if len(decoded[7]) >= 4 else None,
}
else:
raise ValueError(f"Unsupported tx type: {tx_type}")
# Usage
event = {"type": "evmRawTx", "data": "02f8d4820..."}
decoded = decode_evm_raw_tx(event["data"])
print(decoded["to"], decoded["function_selector"], decoded["chain_id"])
# 0xa4ea0e42eaab32e638703db88da1192fb42517f4 0xf3dc4762 999
All observed evmRawTx events have been EIP-1559 (tx type 0x02). chainId is always 999 (HyperEVM mainnet). Measured lead time vs HyperLiquid’s public RPC: 250ms to 3 seconds, averaging ~1 second.
See EVM RPC for pointer-back info on why the HL RPC cannot serve this data.
Pre-consensus cancel example
{
"block": 70852325,
"timestamp": 1776147484247,
"type": "cancel",
"consensus": "pre",
"user": "0x31ca8395cf837de08b24da3f660e77761dfb974b",
"asset": "ALGO",
"asset_id": 158,
"data": {
"cancels": [
{"asset": 158, "oid": 380721371885},
{"asset": 158, "oid": 380721371886}
]
},
"network": "mainnet"
}
Prediction market events
Events for HIP-4 outcome tokens include a prediction object in the data:
{
"type": "order",
"consensus": "confirmed",
"asset": "#39890",
"data": {
"coin": "#39890",
"side": "B",
"px": "0.95",
"sz": "200",
"prediction": {
"outcome_id": 3989,
"side_index": 0,
"asset_id": 100039890
}
}
}
Outcome token naming: #{10 * outcome_id + side_index}. See Prediction Markets for details.
Timestamp
Unix milliseconds. To convert:
const date = new Date(event.timestamp);
from datetime import datetime
dt = datetime.fromtimestamp(event["timestamp"] / 1000)