Skip to content

v0.2.0

Choose a tag to compare

@joeblau joeblau released this 02 Aug 23:32
· 7 commits to main since this release
dd4bbbc

Upgrading from 0.1.x — read this first

Two changes alter the behaviour of working 0.1.6 programs. Both convert a silent server-side failure into an immediate local one: the server already refused this traffic, as a 10 s timeout carrying no echoed request to match it to.

1. WebSocket budgets are now shared per IP. Hyperliquid scopes every documented WebSocket limit to the client IP, not the connection. Subscription and unique-user counts were tracked per transport, so N transports admitted N×1000 subscriptions against a limit of 1000. They now share one WebSocketQuota per network by default. If you deliberately want isolated budgets:

import { WebSocketQuota, WebSocketTransport } from "@bloxwap/hyperliquid";

const transport = new WebSocketTransport({ quota: new WebSocketQuota() });

2. The unique-user cap is 14, not 15. A live mainnet probe subscribed distinct users one at a time with the guard disabled, twice on two independent connections: both accepted exactly 14 and had the 15th refused by an error frame reading Cannot track more than 15 total users. — the server enforces one fewer than its own message states. The old value let the 15th subscription through to be dropped without an echo, producing the unmatched timeout the guard exists to prevent.

The same probe settled the scope: with one connection holding 14 users, a second connection from the same host was refused a 15th distinct user while still being allowed one the first already held. Per IP, not per connection — sharding user channels across sockets buys nothing.

Faster

WebSocket requests under load. Every request relayed the socket's single shared terminationSignal, putting one listener per in-flight request on one AbortSignal. EventTarget scans that list linearly on add and remove, making a burst O(n²) — 195 ns per add/remove pair with the list empty, 13.1 µs with 5000 resident. Now O(1):

before after
2000 in-flight requests 9.2–10.3 ms 4.4–5.0 ms (−50%)
1000-subscription reconnect −18.9%

Cold start. @bloxwap/hyperliquid/utils pulled 80+ Info method modules through a single barrel import — 91 modules, now 10: 23.0–31.5 → 6.2–6.3 ms on Node, 6.2–8.2 → 3.5–3.7 ms on Bun.

New

Narrow entrypoints. The root barrel evaluates all four clients plus both transports. Seven additive exports keys let a read-only consumer skip that — ./transport, ./transport/http, ./transport/websocket, and ./api/{info,exchange,subscription,explorer}/client. An info-only process: 69.5 → 41.0 ms on Node, 22.0 → 8.3 ms on Bun.

import { InfoClient } from "@bloxwap/hyperliquid/api/info/client";
import { HttpTransport } from "@bloxwap/hyperliquid/transport";

Opt-in WebSocket rate limiting for the documented 2000 messages/minute per-IP budget. It paces subscribe/unsubscribe only — post frames and keep-alive pings debit the budget but never wait, so per-wallet nonce ordering and half-open-socket detection are untouched.

const quota = new WebSocketQuota({ rateLimit: { capacity: 2000, refillPerMinute: 2000 } });
const transport = new WebSocketTransport({ quota });

Also

  • ExchangeClient's constructor now documents createFastLocalWallet71.3 µs vs 106.6 µs per order (−33.6%), byte-identical signatures.
  • New docs on placing many orders: one batched action costs 3 rate-limit weight versus 100 for the same orders fanned out, and grouping: "na" is not atomic.
  • New check:imports gate budgets each entry point's runtime module graph.
  • Documented WebSocket limits, and two new known-drift entries (outcomeMeta.deployer, validatorL1Votes.registerTemplate).

Full changelog: v0.1.6...v0.2.0