Skip to content
BuildBuild on HyperCore16 pages
Architecture: info, exchange, WebSocketFoundationHyperliquid's API has three surfaces: an unsigned read endpoint, a signed write endpoint, and a WebSocket that streams data and can carry both.Your first requestFoundationReading Hyperliquid needs no key, no account and no risk: three unsigned POST requests return the list of markets, an order book and any address's positions.Asset ids and namingPractitionerOrders name a market by an integer asset id and info requests by a coin string, and perps, spot, HIP-3 and outcomes each derive both by a different rule.Precision: tick size, lot size, significant figuresPractitionerA HyperCore price has at most 5 significant figures and a decimal cap set by szDecimals, a size at most szDecimals decimals, and outcomes use a 0.00001 grid.Signing from first principlesExpertL1 actions sign a keccak hash of their msgpack bytes, user-signed actions sign readable EIP-712 data, and one wrong byte in either recovers a stranger's address.Nonces, expiry and API walletsPractitionerHyperCore keeps each signer's 100 highest nonces and accepts a new one only if unused, above the smallest, and within 2 days before and 1 day after block time.Orders: place, modify, cancel, batch, dead man's switchPractitionerAn order is a signed action carrying a batch of compact orders; modify, cancel, TWAP and the dead man's switch are separate actions, each with its own response.Transfers and withdrawalsPractitionerEvery HyperCore transfer is a signed action: sends and withdrawals need the account's own key, and an API wallet's transfer actions keep value inside the account.Multi-sig actionsExpertA multi-sig action carries enough authorised users' signatures over one nonce and one named leader, in an envelope only the leader signs and submits.WebSocket: subscriptions, post requests, heartbeatsPractitionerOne WebSocket streams book, trade and account updates as blocks execute and carries signed actions, but drops without warning and forgets every subscription.Rate limits and user limitsPractitionerHyperliquid meters each IP by request weight, 1200 per minute, and each address by actions: 10,000 plus one per USDC traded.Priority fees and the activation feeExpertHyperliquid sells latency in two burned auctions, read priority per IP and write priority per order, and charges 1 quote token to activate a new account.Error responsesPractitionerHyperliquid reports errors at four layers, from HTTP status to a string inside a successful response, and several name a cause other than the real one.Debugging signatures and wire shapesExpertA signature over wrong bytes still verifies, so HyperCore recovers a stranger's address and says it does not exist; the fix is almost always the encoding.Optimising latencyExpertMost of an order's round trip is consensus; code wins by streaming reads, cutting serial round trips, using the fast order class and never stalling orders.Data: historical, node and explorerPractitionerThe API keeps only recent history; a full record needs pagination, archived node data or your own node, and each trade counted once by its trade id.
BuildBuild on the HyperEVM9 pages
What the HyperEVM is, and is notFoundationThe HyperEVM is Hyperliquid's smart contract environment: it reads HyperCore state and queues HyperCore actions, but never executes them atomically.Blocks, gas and RPCPractitionerThe HyperEVM produces small blocks every second and big blocks every minute, prices gas with EIP-1559 in HYPE, and serves a JSON-RPC with no historical state.Moving assets between HyperCore and the HyperEVMPractitionerLinked tokens cross between HyperCore and the HyperEVM via per-token system addresses, with a decimal offset and a 200k-gas fee when leaving HyperCore.Linking a tokenExpertLinking binds a HyperCore token to one ERC20 through the spot deployer's request and the contract deployer's finalisation; the protocol checks almost nothing else.Reading HyperCore: precompilesExpertRead precompiles are 21 fixed addresses, 0x800 to 0x814, that return HyperCore state as of the EVM block's construction, and only at the latest block.Writing to HyperCore: CoreWriterExpertCoreWriter turns a contract call into a HyperCore action for the caller's own account, run after the EVM block, with no result returned and no revert on refusal.Execution order and timingExpertEach L1 block runs the EVM block, then EVM to HyperCore transfers, then CoreWriter actions; on mainnet most actions land within half a second, vault transfers in about 4 s.PatternsExpertA contract that touches HyperCore stays solvent by recording an intent, sending the action, and settling only on HyperCore state that visibly moved.Footguns, measuredExpertTwelve ways a HyperEVM contract loses money or silently does nothing when it touches HyperCore, each with the on-chain evidence and the fix.

Asset ids and naming

Practitioner

Orders name a market by an integer asset id and info requests by a coin string, and perps, spot, HIP-3 and outcomes each derive both by a different rule.

One wrong number sends your order to another market, or returns another market’s data. Validator-operated perps, spot pairs, HIP-3 perps and HIP-4 outcomes each derive the order’s a and the coin string from a metadata list.

Say you want HYPE spot on mainnet. The HYPE/USDC pair has index 107, so reads use @107 and orders use asset id 10000 + 107 = 10107. Use HYPE’s token index, 150, instead and you get 10150: pair 150, another market.

Perps on the first dex are the simple case: BTC is asset 0 on mainnet. Every formula is under The numbers; the sections below cover the traps.

Spot pairs are indexed apart from their tokens

Section titled “Spot pairs are indexed apart from their tokens”

Pairs are @<index>, except legacy pairs that keep a name: PURR/USDC is index 0, asset id 10000.

Network HYPE token index HYPE/USDC pair Coin Asset id
Mainnet 150 107 @107 10107
Testnet 1105 1035 @1035 11035

USDH: token 360, pair 230 on mainnet; token 1452, pair 1338 on testnet. spotMeta.universe[].tokens lists [base, quote] token indices: @107 is [150, 0], HYPE and USDC.

Transfers use neither index: a token is NAME:0x<32 hex characters>, from name and tokenId in spotMeta.tokens, for example PURR:0xc1fb593aeffbeb02f85e0308e9956a90 (see transfers).

perpDexs[0] is null: that slot is the first perp dex, which uses plain indices. The docs’ example: test:ABC on testnet has perpDexIndex 1 and index 0 in meta with {"dex": "<name>"}, so asset id 110000. That meta already returns qualified names such as xyz:XYZ100.

Outcomes pack outcome and side into one number

Section titled “Outcomes pack outcome and side into one number”

Sides 0 and 1 are positions in sideSpecs in outcomeMeta; on a plain binary market side 0 is Yes and side 1 No. Every identifier derives from one encoding:

encoding = 10 * outcome + side
Where Form Outcome 1, side 0
Order asset id 100000000 + encoding 100000010
Coin in info requests, WebSocket and fills #<encoding> #10
Token name in spotClearinghouseState balances +<encoding> +10

Outcome coins appear in spotMetaAndAssetCtxs, keyed by # coin, not in meta.universe; positions are spot balances. Payoffs are on what an outcome market is.

Market Asset id Coin string Where the index comes from
Perp, first dex index name, e.g. BTC position in meta.universe
Spot 10000 + index @<index>, or a legacy name like PURR/USDC the pair’s index field in spotMeta.universe
HIP-3 perp 100000 + perpDexIndex * 10000 + index dex:COIN position in perpDexs; position in meta for that dex
Outcome side 100000000 + 10 * outcome + side #<10 * outcome + side>; balances +<...> outcome and sideSpecs in outcomeMeta

Compare prices across tools by pair index or asset id, not display name. The docs’ example: BTC/USDC in the app is UBTC/USDC on HyperCore, the name the explorer’s token details page shows.

// Inputs come from meta, spotMeta, perpDexs and outcomeMeta, read per network.
const perpAsset = (indexInMeta: number) => indexInMeta;
const spotAsset = (pairIndex: number) => 10_000 + pairIndex; // spotMeta.universe[].index
const hip3Asset = (perpDexIndex: number, indexInMeta: number) =>
100_000 + perpDexIndex * 10_000 + indexInMeta; // perpDexIndex is 1 or more
const encoding = (outcome: number, side: 0 | 1) => 10 * outcome + side;
const outcomeAsset = (outcome: number, side: 0 | 1) => 100_000_000 + encoding(outcome, side);
const outcomeCoin = (outcome: number, side: 0 | 1) => `#${encoding(outcome, side)}`; // info, WS, fills
const outcomeToken = (outcome: number, side: 0 | 1) => `+${encoding(outcome, side)}`; // spot balances

Build the tables per network, keyed by the index field, not array position: on mainnet @367 points at token 479 in a 464-entry tokens array. Outcome asset ids start at 100,000,000, beyond a 16-bit field: store asset ids in at least 32 bits.

  • Outcomes are never @<outcomeId>, which names an unrelated spot pair. One SDK made this mistake.
  • Do not map sides by name. Labelled outcomes do not call their sides Yes and No; use the sideSpecs position. Reject unparseable side input: silently falling back to side 0 is a known bug class.
  • Do not prefix HIP-3 names twice. xyz:xyz:MU matches nothing.
  • Ids are per network, HIP-3 dexes included. A cache keyed by coin alone serves testnet ids to mainnet orders.

For these rules on one card, with system addresses, see Asset encoding cheat sheet. For where the asset id goes, see Orders.