← Index
Source: docs/tasks/details/hyperliquid-advanced-market.md (auto-generated by scripts/generate-docs-html.mjs — edit the .md, not this file)

Rabbit — Hyperliquid advanced market and trading

Here, HTS means a Home Trading System-style terminal: a compact page for market data, order entry, positions, open orders, and fills—not a separate exchange or custody service.

1. Outcome

/market becomes one coherent Hyperliquid workspace:

  1. Open ETH-PERP by default. Multi-market selection is a later enhancement, not an HTS v1 requirement.
  2. See a live order book, recent trades, mark/index price, funding, open interest, and data age.
  3. Connect MetaMask and inspect the matching Hyperliquid account, positions, and open orders.
  4. On testnet, place and cancel a correctly rounded ETH-PERP limit order with an explicit MetaMask signature.
  5. After the testnet release gates pass, enable the same direct-signing flow on mainnet with real-USDC warnings and conservative defaults.
  6. Add advanced order controls only after both direct-signing paths are reliable.
  7. Later, optionally approve an API/agent wallet for popup-free trading without adding any withdrawal or transfer surface to Rabbit.

This is a trading interface, not an execution strategy or an autonomous trading bot. It must never silently submit, repeat, resize, or change an order.

2. Baseline: what already exists

Area Current implementation Limitation
Market page app/market/page.tsx Fixed to ETH as of 2026-07-20; no shared HTS workspace state yet
Order book app/market/OrderBook.tsx REST snapshot every 5 seconds; eight visible levels
Public proxy app/api/orderbook/route.ts One l2Book request; no shared metadata or freshness contract
Hyperliquid client lib/hyperliquid.ts Useful read helpers, but uses broad any types and mixes concerns
Perp details /perp + /api/perp Fixed to ETH and separate from /market
Trading Design only No wallet connection, signing, order submission, cancellation, or order state

The July 7 milestone proved that the public Hyperliquid info endpoint and an L2 order book work. A live read-only check on 2026-07-20 confirmed that ETH/USDC perpetuals expose a non-empty book, mark/index prices, one-hour funding, and open interest. Live values are intentionally not stored here because they expire immediately. The page default was changed from ETC to ETH on 2026-07-20; the remaining work turns that read-only panel into the planned HTS.

3. Product boundaries and decisions

3.1 ETH first; testnet first, mainnet after a release gate

3.2 Browser-to-Hyperliquid order flow

3.3 Progressive order capability

Ship the safest useful subset first:

  1. Limit GTC order
  2. Cancel order
  3. Post-only (ALO) and reduce-only
  4. IOC and capped-slippage market order
  5. Modify order
  6. TP/SL
  7. Agent-wallet execution

Do not build TWAP, vault trading, subaccounts, builder fees, portfolio margin, deposits, transfers, or withdrawals in this feature.

3.4 Existing routes

3.5 Integration choice: Hyperliquid API + browser signer

Rabbit does not need a conventional exchange API key for the direct-signer release. A centralized exchange API key is normally an opaque credential copied from an exchange dashboard and stored by the application. Hyperliquid instead separates unauthenticated reads from cryptographically signed writes:

This is still authenticated trading: the wallet signature replaces the conventional API-key credential. The private key never leaves MetaMask, and an ordinary HyperCore order/cancel signature is not an EVM token transfer. Nevertheless, a signature is security-sensitive authorization. Keep it only in memory for the active request and never send it to Rabbit logs, analytics, error reporting, storage, or a server proxy.

3.5.1 Read paths

Use two read paths with one normalized client-side data model:

Primary live path
Rabbit browser → Hyperliquid WebSocket → book, trades, asset context, account updates

Bootstrap/recovery path
Rabbit browser or Rabbit REST fallback → Hyperliquid POST /info → snapshot/reconciliation

The logged-out page can subscribe to ETH book and market data immediately. After the user clicks Connect MetaMask, Rabbit obtains the public address and adds user-scoped reads such as clearinghouse state, frontend open orders, fills, and order status. The address is not a secret, but it is user-linked data; do not place the full address in routine analytics or server logs.

Always query account state with the master or actual subaccount address. Hyperliquid's API-wallet documentation warns that querying with an agent/API-wallet signer address returns empty account state. HTS v1 supports the directly connected master account only; subaccount and vault selection remain out of scope.

3.5.2 Direct order path, step by step

For one ETH-PERP limit order:

  1. The user enters side, price, size, time-in-force, and reduce-only state in Rabbit.
  2. Rabbit's browser code resolves ETH's asset index and szDecimals from the selected network's metadata. It does not reuse IDs or precision from the other network.
  3. The browser validates and canonicalizes decimal strings, checks freshness/account state, creates a unique CLOID, and presents the final asset, network, side, normalized price, normalized size, notional, and account in Rabbit's confirmation modal.
  4. Only after confirmation does the SDK build the Hyperliquid order action. The action contains the asset index, buy/sell flag, price, size, reduce-only flag, order type, and CLOID.
  5. The SDK creates a unique millisecond-based nonce and performs Hyperliquid's required serialization, hashing, signing-scheme selection, and EIP-712 typed-data construction. Rabbit must not reproduce these internals.
  6. MetaMask shows a signature request. If the user rejects, Rabbit returns to ready; there must be no POST /exchange request.
  7. After approval, the SDK sends an envelope containing action, nonce, signature, and, when intentionally configured, optional vaultAddress or expiresAfter fields to the selected network's /exchange endpoint.
  8. Rabbit parses every status in the response. A resting or filled response updates the ticket; a rejection maps to a specific UI error. A timeout or lost response becomes outcome_unknown.
  9. Rabbit reconciles the result through user updates and POST /info order-status lookup by CLOID. It never automatically signs or submits a replacement order.

Cancel follows the same boundary: the user chooses one visible order, Rabbit confirms the correct network/asset/order, MetaMask signs a distinct cancel action, and the client sends it directly to Hyperliquid. One MetaMask popup per order or cancel is expected in this release.

order draft
  → Rabbit browser validation and confirmation
  → pinned SDK constructs the action
  → MetaMask signs typed data
  → pinned SDK POSTs to Hyperliquid /exchange
  → WebSocket + /info reconcile the result

Rabbit server is not on this signed-write path.

3.5.3 SDK selection and wrapper boundary

Hyperliquid's official API overview lists @nktkas/hyperliquid as a community TypeScript SDK; it is not maintained or warranted by Hyperliquid. The SDK currently documents all three client surfaces Rabbit needs:

Its browser example uses ethers v6 BrowserProvider(window.ethereum) followed by provider.getSigner(), which matches Rabbit's existing ethers v6 dependency and MetaMask's injected provider. Treat that documented path as the first H0 candidate, not as proof that it works in Rabbit's Next.js client bundle. At the 2026-07-20 review, the latest observed release is 0.33.2; H0 must test and record the exact accepted version before adding it with an exact dependency and lockfile entry.

Keep the third-party surface behind a small Rabbit-owned adapter. UI components should call Rabbit-level operations such as connect, loadAccount, placeLimitOrder, cancelOrder, and getOrderStatus; they should not import SDK signing utilities or construct raw exchange payloads. This boundary gives Rabbit one place to enforce network selection, asset lookup, decimal normalization, CLOIDs, error mapping, and safe upgrade tests.

H0 must prove all of the following in the actual Rabbit browser build:

  1. The selected exact SDK version builds under Rabbit's Next.js/TypeScript/Node toolchain without pulling server-only code into the client bundle.
  2. BrowserProvider obtains the expected MetaMask address and ExchangeClient accepts its ethers v6 signer without exposing the private key.
  3. Testnet endpoint selection affects info, WebSocket, and exchange clients together.
  4. A small resting ETH-PERP testnet order produces one MetaMask prompt, appears in account reads, and can be canceled with a second prompt.
  5. Account change, disconnect, or network-mode change destroys the old exchange client, clears user-scoped state and drafts, and requires fresh confirmation.
  6. Wallet rejection causes no network submission; ambiguous transport failure is reconciled by CLOID without an automatic retry.
  7. Direct browser HTTP and WebSocket access works in the deployed Cloud Run origin, not only on localhost.

If this documented ethers v6 path fails, first determine whether the problem is the pinned SDK version, browser bundling, injected-provider adaptation, or Hyperliquid API compatibility. Make only the smallest adapter around the SDK's supported signer interface. If correct signing would require Rabbit to manually implement msgpack field ordering, action hashing, phantom-agent construction, or EIP-712 domain logic, stop H0 and revise the integration choice; do not hand-build signing.

3.5.4 Trust boundary

Component May handle Must not handle
MetaMask Private key, typed-data request, user approval Rabbit application secrets or silent approvals
Rabbit browser Public address, draft, normalized action, transient signature/result Seed phrase, exported master private key, persisted raw signature
Hyperliquid API Public reads, signed action envelope Rabbit authentication session or application database credentials
Rabbit server Static/page delivery, public REST fallback, sanitized operational errors Master/agent key, direct-signer action/signature, order submission
Logs/analytics Network, action category, latency, sanitized error category Full address, exact order contents, CLOID, nonce, or signature

The later agent/API-wallet release is a different signing mode, not a conventional dashboard API key. It introduces a separate approved keypair that can sign on behalf of the master account and therefore requires its own storage, expiry, rotation, and revocation threat model. Do not let its future requirements weaken the direct MetaMask boundary described here.

3.6 Testnet trading: end-to-end user flow

Testnet is the first executable release and uses mock funds.

  1. Open the official Hyperliquid testnet app with the same MetaMask account intended for Rabbit.
  2. Enable trading if Hyperliquid requests the gasless activation signature.
  3. Obtain mock USDC through the official testnet Drip/faucet. Faucet rules can change, so link to the official testnet onboarding page instead of reproducing eligibility assumptions in Rabbit.
  4. Open Rabbit /market; it defaults to ETH-PERP and shows TESTNET · MOCK USDC beside every order control.
  5. Connect MetaMask. Rabbit reads the master account address for balances, positions, orders, and fills.
  6. Fetch testnet metadata and resolve ETH's asset ID and szDecimals; never reuse mainnet IDs.
  7. Enter a small ETH limit order. Rabbit normalizes price/size, shows notional and account, then requests one signature.
  8. The SDK sends the signed order action directly to the testnet /exchange endpoint.
  9. Confirm the resting/filled result through order updates and reconciliation; then cancel the resting remainder through a second explicit signature.
  10. If a test fills, close the position deliberately and confirm that position size returns to zero before ending the test.

No Sepolia ETH or HyperEVM gas is required for ordinary HyperCore order signatures. The official testnet app/faucet remains the onboarding source of truth.

3.7 Mainnet trading: controlled release flow

Mainnet uses real collateral and real PnL. Rabbit should add it only after H2 testnet behavior is stable.

  1. Keep the first mainnet release on direct MetaMask signing; agent-wallet automation remains disabled.
  2. Complete Hyperliquid's normal wallet onboarding/Enable Trading flow in the official mainnet app if the account is new.
  3. Fund the account outside Rabbit. The canonical USDC path is native USDC on Arbitrum plus ETH for the deposit transaction, then deposit through the official Hyperliquid UI. Hyperliquid's bridge documentation currently states a 5 USDC minimum; confirm the current UI before sending.
  4. Do not add deposit, withdrawal, transfer, or bridging controls to Rabbit's HTS. Rabbit trades only after collateral is already visible in the Hyperliquid account.
  5. Enable a build setting such as NEXT_PUBLIC_HL_MAINNET_TRADING_ENABLED=true. This is a UI release gate, not a cryptographic security boundary.
  6. When the user selects mainnet, reload all metadata, ETH book/context, account state, orders, and endpoints from mainnet. Clear every testnet draft and confirmation.
  7. Show MAINNET · REAL USDC in the header, ticket, confirmation modal, and submit button.
  8. Require an additional first-session acknowledgement such as I understand this uses real funds; never persist a global “skip all confirmations” setting.
  9. Start with a small passive ETH limit order and direct signature, verify it appears on the official Hyperliquid mainnet UI, then cancel it. Enable more aggressive types only afterward.

Recommended mainnet v1 guardrails:

3.8 Environment and release settings

# Public endpoint selection; no secret is stored here.
NEXT_PUBLIC_HL_DEFAULT_NETWORK=testnet

# false until testnet H2 and the mainnet H3 checklist pass.
NEXT_PUBLIC_HL_MAINNET_TRADING_ENABLED=false

These flags control Rabbit's UI. They do not protect funds by themselves: the wallet signature, clear confirmation, correct endpoint, correct asset metadata, and user review are still required.

4. Target experience

4.1 Simple HTS desktop layout

┌ ETH-PERP ─ mark ─ funding ─ OI ─ TESTNET/MAINNET ─ Connect wallet ────────┐
├────────────────────┬──────────────────────────────┬─────────────────────────┤
│ ETH order book     │ Simple ETH chart / trades    │ ETH trade ticket        │
│ asks · spread      │ mark/index + feed age        │ Long/Short · px · size  │
│ bids · depth       │ Live / reconnect / stale     │ notional · confirmation │
├────────────────────┴──────────────────────────────┴─────────────────────────┤
│ Positions · Open orders · Recent fills                                     │
└──────────────────────────────────────────────────────────────────────────────┘

On narrow screens, stack context → ticket → positions/orders → book/trades. The submit button and network label must remain visible without relying on color alone. “Simple HTS” means a compact trading terminal, not a clone of every Hyperliquid screen.

4.2 Market header

4.3 Market data

Show:

Funding is a signed rate, not a generic percentage change. Label its interval. Open interest must be labeled in both asset units and USD when both values are available.

4.4 Trade ticket

Initial fields:

Field Rules
Side Buy/Long or Sell/Short; selected explicitly
Order type Limit first; Market remains hidden until capped-slippage IOC is implemented
Time in force GTC first, then ALO and IOC
Price Prefill from the same-network best ask for Buy or best bid for Sell
Size Asset units; rounded down to the selected asset's szDecimals
Reduce only Off by default; clearly describe that it cannot increase the position
Notional price × size, shown before signature

The confirmation area repeats network, market, side, type, price, size, notional, reduce-only state, and connected address. A submission requires one deliberate click followed by the wallet signature; pressing Enter in a text field must not submit an order.

4.5 Positions, orders, and fills

4.6 HTS v1: required versus deferred

Required for the first simple HTS Deferred until the direct flow is stable
ETH-PERP only Multi-market selector
Network and real/mock-money badges User-customizable layouts
Mark, index, funding, OI, feed age Advanced analytics and indicators
Live book and recent trades Full TradingView-style chart tooling
Limit GTC ticket and cancel Market, ALO, IOC, modify, TP/SL
Account value, ETH position, open orders, fills Portfolio margin, subaccounts, vaults
Explicit MetaMask signature Agent/API wallet

Use Hyperliquid data directly rather than adding another paid market-data vendor. A minimal candle chart can use the official candle stream/snapshot after the book and trade ticket work; a plain price line is sufficient for HTS v1.

5. Data and execution architecture

Browser `/market`
  ├─ Market metadata/context ── REST info endpoint (initial + reconciliation)
  ├─ Book/trades/candles ────── Hyperliquid WebSocket (primary)
  ├─ Book snapshot fallback ─── Rabbit `/api/orderbook` → info endpoint
  ├─ User state streams ─────── Hyperliquid WebSocket after wallet connect
  └─ Signed actions ─────────── TypeScript SDK → Hyperliquid `/exchange`

Rabbit server
  ├─ serves the app and public REST fallback
  ├─ validates public query parameters and bounds
  └─ stores no trading key and submits no user order in the direct-signing phase

5.1 Network configuration

Expose a small enum rather than an arbitrary client-supplied host:

type HyperliquidNetwork = "mainnet" | "testnet";

const HL_ENDPOINTS = {
  mainnet: {
    http: "https://api.hyperliquid.xyz",
    ws: "wss://api.hyperliquid.xyz/ws",
  },
  testnet: {
    http: "https://api.hyperliquid-testnet.xyz",
    ws: "wss://api.hyperliquid-testnet.xyz/ws",
  },
} as const;

Do not accept a URL from search parameters. The selected network may be stored as a harmless user preference, but it must not enable mainnet trading when the build flag disables it.

5.2 Hyperliquid calls used by the HTS

Need Hyperliquid surface Rabbit usage
Markets and precision /infometa / metaAndAssetCtxs Resolve ETH asset ID, szDecimals, leverage/context
Initial/fallback book /infol2Book Snapshot and REST fallback
Live book/trades/chart WebSocket l2Book, trades, later candle Primary public HTS feed
Account and positions /info account state using master address Balance, ETH position, margin data
Orders and fills info reads + orderUpdates / userFills subscriptions Tables and reconciliation
Place order /exchange order action through SDK Direct MetaMask/API-wallet signature
Cancel order /exchange cancel action through SDK Explicit signature and CLOID/OID tracking
Resolve ambiguity /info order status by OID/CLOID Prevent duplicate retries

The same call shapes are used on both networks; only the fixed HTTP/WebSocket base URLs, metadata, account state, and signing domain/network value differ. Never send a testnet-signed workflow to a mainnet endpoint or vice versa.

5.3 Typed domain model

Normalize Hyperliquid's string-encoded numbers at the boundary, but retain the original decimal strings for signing. JavaScript number is acceptable for display calculations, not for producing signed price or size strings.

type MarketMeta = {
  coin: string;
  assetId: number;
  szDecimals: number;
  maxLeverage: number;
};

type OrderDraft = {
  coin: string;
  side: "buy" | "sell";
  type: "limit";
  tif: "Gtc" | "Alo" | "Ioc";
  price: string;
  size: string;
  reduceOnly: boolean;
};

The metadata universe determines assetId; never keep a permanent handwritten coin-to-index map because mainnet and testnet universes can differ.

5.4 WebSocket lifecycle

Subscribe only to what the visible workspace needs:

Required behavior:

  1. Connect and wait for subscription acknowledgements.
  2. Record exchange timestamps and local receipt times.
  3. Send a ping before 60 seconds of outbound silence and accept pong.
  4. On disconnect, mark data stale immediately and reconnect with bounded exponential backoff plus jitter.
  5. After reconnect, accept the snapshot acknowledgement and run REST reconciliation for open orders and account state.
  6. Unsubscribe or close when the selected network/market/user changes.
  7. Fall back to the current REST book polling after repeated failures; never present fallback data as Live.

5.5 Rate-limit budget

Hyperliquid currently documents a shared REST weight limit, connection/subscription limits, and address-based action limits. Rabbit should stay comfortably below them:

6. Precision and pre-trade validation

Validation must happen before asking for a signature and again through the SDK/exchange response.

6.1 Size

6.2 Price

For perpetuals, the official rule is:

Display the normalized value before signature. Do not mutate the price after the user confirms it.

6.3 Notional and account checks

6.4 Market orders

Hyperliquid order actions are expressed using limit-order parameters. Rabbit's future Market control must therefore create an IOC limit with an explicit maximum slippage cap derived from the same-network reference price. Show that worst acceptable price before signing. Never submit an unbounded price.

7. Signing and order state

7.1 SDK compatibility spike

Before building the full ticket:

  1. Pin a selected TypeScript SDK version.
  2. Confirm it accepts an ethers v6 browser signer or add the smallest adapter.
  3. On Hyperliquid testnet, place one resting order and cancel it.
  4. Capture sanitized request/response fixtures for tests; do not store addresses or signatures if they are not needed by the fixture.
  5. Confirm mainnet/testnet domains, nonce handling, asset lookup, and number serialization.

If the SDK cannot support MetaMask direct signing cleanly, stop and revise the signer design. Do not replace it with a hand-built signing implementation merely to keep the milestone moving.

7.2 Client order IDs and duplicate protection

7.3 State machine

draft
  → invalid
  → ready
  → awaiting_signature
  → submitting
  → resting | filled | rejected | outcome_unknown

resting
  → cancel_awaiting_signature
  → cancel_submitting
  → canceled | filled | cancel_rejected | outcome_unknown

The UI must distinguish exchange rejection from transport failure. outcome_unknown requires reconciliation, not optimistic success or automatic resubmission.

7.4 Direct signer: first release

7.5 Agent/API wallet: later release

The master wallet approves an agent address once; the agent then signs the allowed trading actions. Rabbit's product policy is narrower than the protocol surface:

Recommended first persistence design: generate the agent key in the browser, encrypt it with a user-supplied passphrase using WebCrypto, and store only the encrypted blob in IndexedDB. Keep the decrypted key in memory for the active session. This requires a separate threat-model review before implementation; direct signing remains the safe fallback.

8. Error, stale-data, and recovery UX

Condition UI behavior
WebSocket disconnected Mark book/trades stale, freeze age indicator, start reconnect
REST fallback active Show REST fallback and its slower refresh interval
Data older than threshold Disable price-prefill refresh and require manual review before signing
Wallet account changed Cancel the local draft confirmation and reload user state
Network mode changed Clear book, context, user state, and draft before reconnecting
Signature rejected Return to editable draft; do not call the exchange
Exchange rejects order Keep draft and show the mapped exchange reason
Submit result unknown Lock retry, query status by CLOID, then resolve or allow a new attempt
Partial fill Show filled and remaining sizes; cancellation applies only to the remainder

Error messages must be actionable and must not display raw signed payloads, signatures, or private keys. Developer logging may include network, coin, CLOID, action type, latency, and sanitized error category.

9. Suggested file plan

Keep the first change small; split lib/hyperliquid.ts only when the new responsibilities arrive.

app/market/
  MarketWorkspace.tsx       network, market, wallet, and shared feed state
  MarketSelector.tsx
  MarketContext.tsx
  OrderBook.tsx             evolve existing component
  RecentTrades.tsx
  TradeTicket.tsx
  PositionsTable.tsx
  OpenOrdersTable.tsx
  RecentFillsTable.tsx

lib/hyperliquid/
  endpoints.ts              fixed network → HTTP/WS mapping
  types.ts                  normalized domain types
  info.ts                   metadata, context, book, account reconciliation
  websocket.ts              one connection + subscriptions + reconnect
  precision.ts              deterministic string normalization/validation
  exchange.ts               narrow SDK adapter: order/cancel/status
  errors.ts                 exchange error → UI category

During migration, preserve @/lib/hyperliquid exports or update consumers in the same milestone. Do not move unrelated market/index code.

10. Delivery milestones

H0 — signing and metadata spike (0.5–1 day)

Exit: one scripted/manual testnet round trip with sanitized fixtures and no handwritten signing.

H1 — simple ETH HTS, read-only (1–1.5 days)

Exit: ETH book, context, compact chart/trades, and bottom tables share one network state; disconnect/reconnect does not duplicate subscriptions; stale and fallback states are visible.

H2 — direct-signed testnet trading (1.5–2 days)

Exit: place, observe, partially or fully fill when practical, cancel, and deliberately close an ETH testnet position; a rejected signature or lost response never creates a duplicate order.

H3 — guarded mainnet direct trading (0.5–1 day)

Exit: one intentionally small ETH mainnet order can be placed and canceled without any testnet/mainnet state leakage; disabling the flag removes the mainnet submit capability.

H4 — safe advanced orders (1–1.5 days)

Exit: every advanced control has pre-trade confirmation, an exchange-error mapping, and a testnet scenario.

H5 — agent wallet (1–2 days, optional)

Exit: no plaintext key persists, direct signing remains available, and the UI exposes no withdrawal or transfer action.

H6 — release hardening (0.5–1 day)

11. Verification matrix

Unit

Fixture/contract

Component

Testnet integration

Mainnet canary

Release gates

12. Risks and mitigations

Risk Mitigation
Community TypeScript SDK changes Pin version, narrow adapter, fixture tests, compatibility spike first
Wrong network or asset ID Derive metadata per endpoint; one workspace network; clear state on switch
Precision/signing mismatch Preserve decimal strings; centralized formatter; SDK signing only
Duplicate order after timeout CLOID, explicit state machine, status reconciliation, no automatic retry
Stale book used for price Timestamp/age state; same-network feed; disable stale prefill
WebSocket disconnect Heartbeat, backoff+jitter, snapshot/reconciliation, labeled REST fallback
Agent key theft Direct signer first; encrypted local blob; memory-only unlocked key; revoke path
Mainnet loss during development Release flag, testnet first, real-money acknowledgement, passive limit canary, no advanced orders
UI scope becomes a full exchange clone Enforce milestone order and explicit out-of-scope list

13. Official references