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.

Writing to HyperCore: CoreWriter

Expert

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

CoreWriter lets your contract act on HyperCore: trade, move funds, stake, supply to the lending reserve, split outcome tokens. Precompiles only read. Your contract emits a log, which HyperCore processes as an action after the EVM block is built, so EVM execution never touches HyperCore state.

CoreWriter sits at 0x3333333333333333333333333333333333333333, with this interface:

interface ICoreWriter {
event RawAction(address indexed user, bytes data);
function sendRawAction(bytes calldata data) external;
}

The docs’ attached source burns gas in an empty 400-iteration loop, then does emit RawAction(msg.sender, data) and nothing else. It never validates data, cannot see HyperCore, and cannot fail on anything HyperCore later decides.

Item Value
sendRawAction(bytes) selector 0x17938e13
RawAction topic 0 0x8c7f585fb295f7eb1e6aeb8fba61b23a4fe60beda405f0045073b185c74412e3
RawAction topic 1 the sender, left-padded
RawAction data ABI bytes: offset word, length word, then the payload
byte 0 version, always 0x01
bytes 1 to 3 action id, big-endian uint24
bytes 4 on abi.encode(fields...), no function selector

The fields are the raw ABI encoding of the action’s tuple; abi.encodePacked(uint8(1), bytes3(actionId), abi.encode(...)) builds the whole payload.

Say your contract supplies 10 USDC. The payload is 0x01, 0x00000f (action 15), then three words: operation 0 (Supply), token 0 (USDC) and wei 1,000,000,000 (1e8 per USDC). The call costs about 47,000 gas, whether HyperCore accepts it or not.

The action runs for msg.sender’s HyperCore account: a contract’s own, an EOA’s own and, for an API wallet key signing a HyperEVM transaction, the API wallet’s address, not its master’s.

Per the docs, the sender must exist on HyperCore and be activated (a first transfer in, costing 1 quote token) before the EVM block is built; funding it in the same block is too late (activation). CoreWriter does not work for multi-sig users, and converting a user to multi-sig leaves its HyperEVM side controllable by the original wallet.

Actions run after the block, with no reply

Section titled “Actions run after the block, with no reply”

Per the docs, the L1 block that produces the EVM block builds it, processes EVM to HyperCore transfers, then CoreWriter actions (measured delays).

sendRawAction never reverts on a HyperCore failure. The HyperEVM is never told. Every refusal returns success, gas is spent, a log is emitted and the money stays put.

Id Action Fields Units and notes
1 Limit order uint32 asset, bool isBuy, uint64 limitPx, uint64 sz, bool reduceOnly, uint8 encodedTif, uint128 cloid limitPx and sz are 1e8 x the human value. TIF 1 Alo, 2 Gtc, 3 Ioc; no FrontendMarket. cloid 0 means none
2 Vault transfer address vault, bool isDeposit, uint64 usd scale of usd not stated in the docs
3 Token delegate address validator, uint64 wei, bool isUndelegate HYPE wei; HYPE has 8 decimals on HyperCore
4 Staking deposit uint64 wei HYPE wei
5 Staking withdraw uint64 wei HYPE wei
6 Spot send address destination, uint64 token, uint64 wei HyperCore wei of token (USDC 1e8)
7 USD class transfer uint64 ntl, bool toPerp scale of ntl not stated in the docs
8 Finalise EVM contract uint64 token, uint8 encodedFinalizeEvmContractVariant, uint64 createNonce 1 Create, 2 FirstStorageSlot, 3 CustomStorageSlot; only Create reads the nonce. See linking
9 Add API wallet address wallet, string name an empty name makes it the main, unnamed agent
10 Cancel by oid uint32 asset, uint64 oid
11 Cancel by cloid uint32 asset, uint128 cloid
12 Approve builder fee uint64 maxFeeRate, address builder tenths of a basis point: 10 = 0.01%
13 Send asset address destination, address subAccount, uint32 source_dex, uint32 destination_dex, uint64 token, uint64 wei type(uint32).max selects spot; a non-zero subAccount sends from that sub-account; HyperCore wei
14 undocumented observed as three words exists on mainnet; semantics unknown (below)
15 Borrow and lend uint8 encodedOperation, uint64 token, uint64 wei 0 Supply, 1 Withdraw; wei 0 applies the operation maximally; HyperCore wei (USDC 1e8)
16 Set abstraction address user, uint8 abstraction 1 disabled (Standard, also called Manual), 2 unifiedAccount, 3 portfolioMargin
17 Outcome operation uint8 encodedOperation, uint32 question, uint32 outcome, uint64 wei 0 SplitOutcome, 1 MergeOutcome, 2 MergeQuestion, 3 NegateOutcome. Unused fields must be 0. wei is 1e8 per contract, a multiple of the size; 0 merges the maximum

Asset ids in actions 1, 10 and 11 follow the API’s numbering (asset ids). A market order from a contract is Ioc with a slippage-bounded limitPx.

For action 17, split and merge ignore question, MergeQuestion ignores outcome, and negate uses both. One split with wei = 100,000,000 minted exactly 1 Long and 1 Short and moved 1 USDC (split, merge, negate).

It appears in the histogram below; do not build on it. A decoded example from 0x111111a1a0667d36bd57c0a9f569b98057111111, USDH’s mainnet EVM contract per one SDK, carried 0x0100000e then the words [360, 10752298000000, 0], and 360 is USDH’s mainnet token index: probably USDH related.

The docs’ aligned quote asset page describes a CoreWriter action reflecting a deployer’s minted HyperEVM balance onto HyperCore, with no documented id; whether that is action 14 is not established.

The EVM receipt says nothing. The HyperCore result shows in three places:

Where What it shows
The explorer endpoint: POST {"type":"userDetails","user":"0x..."} to rpc.hyperliquid.xyz/explorer txs, each with a time, a typed action and an error (null when accepted). CoreWriter actions appear under system names; the EVM transactions themselves appear as evmRawTx entries
Info requests The effect: userFills echoes the cloid of an action 1 order; userNonFundingLedgerUpdates shows transfers
Precompiles, in a later block The balance or position that should have moved

Entries from testnet and mainnet, reformatted:

SystemBoleAction {"operation":"supply","token":0,"wei":50000000000} error: null
SystemBoleAction {"operation":"supply","token":0,"wei":10000000000} error: "Borrow/Lend error: Insufficient spot balance for operation"
SystemBoleAction {"operation":"withdraw","token":0,"wei":1000000000} error: "Manual borrow/lend not allowed with portfolio margin"
SystemSendAssetAction {"destination":"0x2000...0000","token":0,"wei":10000000000} error: "Insufficient USDC or HYPE balance for token transfer gas."

The type name gives the origin: action 15 lands as SystemBoleAction (“Bole” is borrow and lend) with an integer wei, while the HTTP borrowLend action lands as borrowLend with a decimal-string amount.

Parameter Value
Address 0x3333333333333333333333333333333333333333
Version byte 0x01
Action id 3 bytes, big-endian
Gas burned before the log about 25,000 (docs text); the attached source says about 20k
Gas for a basic call about 47,000 (docs); measured 47,889 on mainnet
Explorer history per user last 300 actions
Documented ids 1 to 13, 15 to 17

Mainnet action histogram, EVM blocks 43,380,826 to 46,380,826 (3,000,001 blocks, about 35 days, zero gaps). The window also held a one-off fuzz sweep of ids 18 to 29 and six garbage ids.

Id Count Id Count
13 send asset 249,116 12 builder fee 626
1 limit order 9,309 2 vault transfer 217
6 spot send 6,403 16 set abstraction 203
7 USD class transfer 2,305 4 staking deposit 199
9 add API wallet 2,070 5 staking withdraw 188
3 token delegate 1,395 11 cancel by cloid 129
10 cancel by oid 1,235 14 undocumented 25
15 borrow and lend 13 17 outcome operation 4
8 finalise EVM contract 0

A HyperEVM app trading for you places real HyperCore orders from its contract’s own account, as Ioc with a price limit (no FrontendMarket). On mainnet they executed 0.35 to 0.51 s after their EVM block; a refused one still shows a successful EVM transaction.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
interface ICoreWriter {
function sendRawAction(bytes calldata data) external;
}
/// Acts for this contract's own HyperCore account. No access control here:
/// gate these functions in real code. Nothing below can tell whether HyperCore accepted the action.
contract CoreWriterExample {
ICoreWriter constant CORE_WRITER = ICoreWriter(0x3333333333333333333333333333333333333333);
uint8 constant TIF_IOC = 3;
uint8 constant OP_SUPPLY = 0;
/// Version byte 0x01, then the action id as a big-endian uint24, then the ABI fields.
function _send(uint24 actionId, bytes memory fields) internal {
CORE_WRITER.sendRawAction(abi.encodePacked(uint8(1), bytes3(actionId), fields));
}
/// Action 1. limitPx and sz are 1e8 x the human value. No FrontendMarket:
/// a market order is Ioc with a bounded limit price.
function buyIoc(uint32 asset, uint64 limitPx1e8, uint64 sz1e8, uint128 cloid) external {
_send(1, abi.encode(asset, true, limitPx1e8, sz1e8, false, TIF_IOC, cloid));
}
/// Action 15, supply. wei is HyperCore wei of the token: USDC is 1e8.
function supply(uint64 token, uint64 coreWei) external {
_send(15, abi.encode(OP_SUPPLY, token, coreWei));
}
}

Golden vectors from mainnet payloads, which your encoder must reproduce byte for byte:

Supply 4,014.92350600 USDC (block 45,041,472)
0100000f
0000000000000000000000000000000000000000000000000000000000000000 op 0, Supply
0000000000000000000000000000000000000000000000000000000000000000 token 0, USDC
0000000000000000000000000000000000000000000000000000005d7acf1688 wei 401,492,350,600
Withdraw 10 USDC (block 46,253,547)
0100000f
0000000000000000000000000000000000000000000000000000000000000001 op 1, Withdraw
0000000000000000000000000000000000000000000000000000000000000000 token 0, USDC
000000000000000000000000000000000000000000000000000000003b9aca00 wei 1,000,000,000

HyperCore refused the second (its sender was in portfolio margin mode); the vector tests the encoder, not the outcome.

  • Record an intent before calling, and settle it only after reading HyperCore state in a later block (patterns).
  • Set the abstraction mode with action 16 before the contract holds any position.
  • Put a cloid on every action 1 order so its fill can be matched later.
Behaviour Detail
Silent refusals EVM status 1 with a HyperCore error on real transactions: the errors above, and “Invalid number of decimals” (footguns)
Action 17 with a non-zero unused field Dropped, per the docs, not rejected
Spot orders on one testnet account Silently dropped even above the minimum notional, while the identical perp order succeeded (small sample). On mainnet, cloid-matched fills in the delay study included spot pairs, so spot orders do execute there
Portfolio margin Rejects action 15
Leaving a unified account Refused with open positions, open orders or active TWAPs: “Cannot disable unified account with open positions, open orders, or active TWAP orders” (observed twice)
Action 16 targets The docs allow the master or a sub-account; all 123 distinct senders of the 203 action 16 emissions in the scan targeted themselves, and the 30 most recent were all contracts whose mode took effect
Explorer history Capped at 300 actions per user; one busy account recorded 278 actions in half an hour. Log the result yourself when you see it
Delayed actions Per the docs, the explorer lists them twice: the enqueuing, then the execution

For when each action lands, see execution order and timing.