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.

Reading HyperCore: precompiles

Expert

Read 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.

Lending against a HyperCore position, pricing a vault share or settling on an outcome all need HyperCore facts. Your contract reads balances, prices, positions and lending rates with one staticcall to a precompile.

staticcall the fixed address with calldata abi.encode(args) and no function selector: a selector would be read as part of the first argument, answering about the wrong thing or failing. Output decodes straight into the structs in the docs’ L1Read.sol; 0x809 takes empty calldata.

Per the docs, every read in one EVM block sees the same snapshot: the latest HyperCore state when that block was constructed.

Say you read a USDC spot balance through 0x801: 64 bytes in (user, token) and 96 out, so it costs 2000 + 65 * 160 = 12,400 gas. A 100 USDC balance returns total 10,000,000,000, in HyperCore wei.

Address Name Input Output
0x800 position address user, uint16 perp (int64 szi, uint64 entryNtl, int64 isolatedRawUsd, uint32 leverage, bool isIsolated)
0x801 spotBalance address user, uint64 token (uint64 total, uint64 hold, uint64 entryNtl)
0x802 userVaultEquity address user, address vault (uint64 equity, uint64 lockedUntilTimestamp)
0x803 withdrawable address user (uint64 withdrawable)
0x804 delegations address user (address validator, uint64 amount, uint64 lockedUntilTimestamp)[]
0x805 delegatorSummary address user (uint64 delegated, uint64 undelegated, uint64 totalPendingWithdrawal, uint64 nPendingWithdrawals)
0x806 markPx uint32 index uint64
0x807 oraclePx uint32 index uint64
0x808 spotPx uint32 index uint64
0x809 l1BlockNumber none uint64
0x80a perpAssetInfo uint32 perp (string coin, uint32 marginTableId, uint8 szDecimals, uint8 maxLeverage, bool onlyIsolated)
0x80b spotInfo uint32 spot (string name, uint64[2] tokens)
0x80c tokenInfo uint32 token (string name, uint64[] spots, uint64 deployerTradingFeeShare, address deployer, address evmContract, uint8 szDecimals, uint8 weiDecimals, int8 evmExtraWeiDecimals)
0x80d tokenSupply uint32 token (uint64 maxSupply, uint64 totalSupply, uint64 circulatingSupply, uint64 futureEmissions, (address user, uint64 balance)[] nonCirculatingUserBalances)
0x80e bbo uint32 asset (uint64 bid, uint64 ask)
0x80f accountMarginSummary uint32 perp_dex_index, address user (int64 accountValue, uint64 marginUsed, uint64 ntlPos, int64 rawUsd)
0x810 coreUserExists address user (bool exists)
0x811 borrowLendUserState address user, uint64 token ((uint64 basis, uint64 value) borrow, (uint64 basis, uint64 value) supply)
0x812 borrowLendReserveState uint64 token (uint64 borrowYearlyRateBps, uint64 supplyYearlyRateBps, uint64 balance, uint64 utilizationBps, uint64 oraclePx, uint64 ltvBps, uint64 totalSupplied, uint64 totalBorrowed)
0x813 position2 address user, uint32 perp same as 0x800
0x814 outcomeStatus uint32 outcome (uint8 status, uint64 settledValue, uint32 question)

Sibling widths differ: 0x812 takes the token as uint64, 0x80c and 0x80d as uint32. Each encodes to one 32-byte word, so the width limits a wrapper’s range, not the layout. 0x80e takes a perp index or 10000 plus a spot pair index; 0x80f takes the perp dex index, then the user.

Value Rule
markPx, oraclePx (perp) divide by 10^(6 - szDecimals) of the perp
spotPx divide by 10^(8 - szDecimals) of the base token
Token amounts (0x801, 0x811, 0x812) HyperCore wei of the token; measured 1e8 for USDC
Rates, utilisation, LTV (0x812) basis points
settledValue (0x814) 100,000,000 = 1.0
bbo not stated in the docs; check against the info API before relying on it

A live 0x812 read for USDC returned borrowYearlyRateBps 500, supplyYearlyRateBps 303, utilizationBps 6736 and totalSupplied 44,172,678,296,289,157 (441,726,782.96 USDC at 1e8). Supply rate is borrow rate times utilisation times 0.9, the protocol keeping 10 per cent of borrow interest: 500 * 0.6736 * 0.9 = 303.1, reported as 303. Against the API the identity holds to 9 decimal places.

0x801 (token) and 0x808 (index) also accept an encoded outcome asset id, answering for that side’s outcome token and spot market. Values below 100,000,000 keep their old meaning.

id = 100,000,000 + 10 * outcome + side side 0 or 1

Testnet outcome 20351, for example, is Long 100203510 and Short 100203511 (asset ids). 0x814 takes the raw outcome id:

status Meaning
0 never existed
1 active
2 settled; settledValue readable
3 settled and pruned; settledValue gone for good

question is type(uint32).max when the outcome is standalone or the question unavailable. A contract settling against an outcome must store settledValue before pruning (settlement).

gas = 2000 + 65 * (input_len + output_len) lengths in bytes
Call Input Output Formula gas
0x810 coreUserExists 32 32 6,160
0x801 spotBalance 64 96 12,400
0x811 borrowLendUserState 64 128 14,480
0x812 borrowLendReserveState 32 256 20,720

An invalid input (an unknown asset, a bad vault address) errors and consumes all gas passed into its call frame: measured InvalidTransaction(PrecompileOutOfGas(50000000)), the whole 50M allowance. One stale id in a loop burns the transaction, so cap every call; one production library caps each read at twice its formula cost and gives dynamic outputs such as tokenInfo a flat 120,000.

Parameter Value
Assigned addresses 0x800 to 0x814 on mainnet and testnet
Unassigned range checked 0x815 to 0x8ff, empty success (2026-08-17)
Gas 2000 + 65 * (input_len + output_len)
Bad input consumes all forwarded gas
Outcome asset id 100,000,000 + 10 * outcome + side
settledValue scale 100,000,000 = 1.0
Perp price scale 10^(6 - szDecimals)
Spot price scale 10^(8 - base szDecimals)
Block tag latest only

A HyperEVM protocol pricing your collateral from 0x806 to 0x808 sees HyperCore as of its own block’s construction, not a live tick. It cannot re-read a past price or exchange rate unless it stored it at the time.

A minimal capped read that fails soft instead of reverting:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract SpotBalanceReader {
address constant SPOT_BALANCE = 0x0000000000000000000000000000000000000801;
// Formula: 2000 + 65 * (64 bytes in + 96 bytes out) = 12,400. Cap at about twice that.
uint256 constant SPOT_BALANCE_GAS = 25_000;
struct SpotBalance {
uint64 total; // HyperCore wei of the token: USDC is 1e8
uint64 hold;
uint64 entryNtl;
}
/// Returns ok = false for a bad input, an unassigned address or a short answer,
/// having spent at most SPOT_BALANCE_GAS. Raw abi.encode, no selector.
function trySpotBalance(address user, uint64 token)
external
view
returns (bool ok, SpotBalance memory bal)
{
(bool success, bytes memory out) =
SPOT_BALANCE.staticcall{gas: SPOT_BALANCE_GAS}(abi.encode(user, token));
if (success && out.length == 96) {
bal = abi.decode(out, (SpotBalance));
ok = true;
}
}
}

The same call from the command line (the docs’ own example reads oraclePx for index 3 on testnet):

Terminal window
cast call 0x0000000000000000000000000000000000000807 \
0x0000000000000000000000000000000000000000000000000000000000000003 \
--rpc-url https://rpc.hyperliquid-testnet.xyz/evm
  • The docs’ L1Read.sol forwards all remaining gas and requires success. It describes the interface, not production code; route every call through one capped function.
  • Deployer stake is 0x805, not 0x804. delegated + undelegated in the deployer’s own staking account meets the requirement, and totalPendingWithdrawal > 0 warns that stake is leaving (deployer economics).
  • Never price shares off 0x812 balance. It is not totalSupplied - totalBorrowed: the gap, the protocol’s retained buffer, was 46.56877103 USDC and growing across three reads 3 seconds apart. Use the account’s own 0x811 supply.value.
  • Off-chain decoding is slicing 32-byte words; every struct is static except 0x804 and 0x80a to 0x80d.
Behaviour Evidence What to do
0x811 reverts when the account has no borrow or lend record, rather than returning zeros PrecompileError on both networks, even for a HyperCore user holding 31,989 USDC of spot Treat a failure as a zero position until the first supply lands; a fresh vault reading it unguarded in totalAssets() cannot take its first deposit
No history: answers only at latest An eth_call at any explicit block number, even the current head, fails with PrecompileOutOfGas(50000000); measured at offsets of 0 to 10,000 blocks, on archive nodes that serve historical eth_getCode Sample forward; there is no backfill
Unassigned addresses return empty success On 2026-08-17, 0x815 and above returned 0x with success; a live precompile returns data or PrecompileError Check the output length, or a typo’d address decodes as garbage
Revisions arrive as sibling addresses 0x813 position2 widens perp to uint32 (0x800’s uint16 stops at 65,535) and 0x800 is unchanged; 0x814 appeared on testnet before it was documented Watch for 0x815. Never narrow an index below uint32: outcome asset ids start at 100,000,000
Stale by execution A read is as of block construction and a CoreWriter action lands after the block, so a read and an action in one transaction never see the same HyperCore See execution order
No precompile returns the account abstraction mode Use the userAbstraction info request (account modes)

The docs agree there is no history: read precompile results are recorded only for calls actually made in each block, and anything else needs a full L1 replay.

For what 0x811 and 0x812 describe, see borrowing and lending.