From fd94f334bd54a6d1474e1ab739ba5e5d0b3d8d8b Mon Sep 17 00:00:00 2001 From: vipineth Date: Fri, 19 Jun 2026 09:30:36 +0530 Subject: [PATCH 1/4] feat(hl-react): update hyperliquid api coverage --- .../lib/tests/hyperliquid-registries.test.ts | 8 ++++ packages/hl-react/README.md | 41 +++++++++++++++++-- packages/hl-react/package.json | 5 +++ packages/hl-react/src/hooks/useClients.ts | 2 +- packages/hl-react/src/registries/exchange.ts | 8 ++++ 5 files changed, 60 insertions(+), 4 deletions(-) diff --git a/apps/terminal/src/lib/tests/hyperliquid-registries.test.ts b/apps/terminal/src/lib/tests/hyperliquid-registries.test.ts index 34d28921..30828a75 100644 --- a/apps/terminal/src/lib/tests/hyperliquid-registries.test.ts +++ b/apps/terminal/src/lib/tests/hyperliquid-registries.test.ts @@ -26,12 +26,20 @@ describe("exchange registry", () => { const EXPECTED_USER_SIGNED: ExchangeMethod[] = [ "approveAgent", "approveBuilderFee", + "cDeposit", + "cWithdraw", + "convertToMultiSigUser", + "linkStakingUser", "sendAsset", + "sendToEvmWithData", "spotSend", + "stakingLinkDisableTradingUser", "tokenDelegate", "usdClassTransfer", "usdSend", "userDexAbstraction", + "userPortfolioMargin", + "userSetAbstraction", "withdraw3", ]; diff --git a/packages/hl-react/README.md b/packages/hl-react/README.md index 687eb570..5e822cc2 100644 --- a/packages/hl-react/README.md +++ b/packages/hl-react/README.md @@ -75,6 +75,18 @@ Wrap inside `WagmiProvider` and `QueryClientProvider` — the hooks depend on bo All hooks are strongly typed against the SDK's method signatures — if a method exists on the underlying SDK, a typed hook path exists for it. +Endpoint coverage is intentionally generic rather than one-hook-per-endpoint: + +| SDK client | React hook | Example | +|---|---|---| +| `InfoClient` | `useInfo(method, params, options)` | `useInfo("perpDexs", undefined)` | +| `SubscriptionClient` | `useSubscription(method, params, options)` | `useSubscription("allDexsAssetCtxs", undefined)` | +| `ExchangeClient` | `useExchange(method, options)` | `useExchange("sendToEvmWithData")` | + +As of `@nktkas/hyperliquid` `0.32.2`, newer SDK methods such as prediction-market info (`outcomeMeta`, `settledOutcome`, `perpCategories`), builder/HIP-3 info (`perpDexs`, `perpDexStatus`, `userDexAbstraction`, `perpDexLimits`), richer subscriptions (`allDexsAssetCtxs`, `allDexsClearinghouseState`, `fastAssetCtxs`, `outcomeMetaUpdates`), and exchange actions (`sendToEvmWithData`, `setDisplayName`, `convertToMultiSigUser`, `userSetAbstraction`, `userPortfolioMargin`) are available through the same generic hooks. + +HIP-4 status: Hyperliquid's HIP-4 outcome-market APIs are present in the upstream SDK under outcome-market method names rather than a literal `HIP-4` namespace. Use `useInfo("outcomeMeta", undefined)`, `useInfo("settledOutcome", { outcome })`, `useSubscription("outcomeMetaUpdates", undefined)`, and `useExchange("userOutcome")` for the current SDK surface. + ### `useInfo` — REST one-shot queries Thin wrapper around `InfoClient` methods (`meta`, `userFills`, `clearinghouseState`, `l2Book`, etc.), cached by TanStack Query via `infoKey` / `infoQueryOptions`. @@ -105,7 +117,29 @@ Backing store tracks `status` (`idle | subscribing | active | error`) per key, r ### `useExchange` — signed mutations -Wraps `ExchangeClient` methods (`order`, `cancel`, `modify`, `approveAgent`, `approveBuilderFee`, `withdraw`, …). Returns a TanStack Query mutation. Signing comes from either the agent wallet (default, fast-path trading) or the user wallet (approvals, withdrawals) — `useClients` decides. +Wraps `ExchangeClient` methods (`order`, `cancel`, `modify`, `approveAgent`, `approveBuilderFee`, `withdraw3`, `sendToEvmWithData`, …). Returns a TanStack Query mutation. Signing comes from either the agent wallet (default, fast-path L1 trading actions) or the connected user wallet (EIP-712/user-signed actions) — `registries/exchange.ts` decides. + +User-wallet routed methods currently include: + +```text +approveAgent +approveBuilderFee +cDeposit +cWithdraw +convertToMultiSigUser +linkStakingUser +sendAsset +sendToEvmWithData +spotSend +stakingLinkDisableTradingUser +tokenDelegate +usdClassTransfer +usdSend +userDexAbstraction +userPortfolioMargin +userSetAbstraction +withdraw3 +``` ### `useAgent*` — agent wallet lifecycle @@ -150,7 +184,7 @@ The Zustand store (`store.ts`) drives the lifecycle: `acquireSubscription` bumps ## Peer dependencies ```json -"@nktkas/hyperliquid": "^0.31.0", +"@nktkas/hyperliquid": ">=0.32.2 <1", "@tanstack/react-query": "^5", "react": "^19", "viem": "^2", @@ -167,8 +201,9 @@ Fine-grained entry points for consumers that want only part of the surface: ```ts import { useHyperliquidStore } from "@hypeterminal/hl-react/store"; +import { useExchange } from "@hypeterminal/hl-react/hooks/useExchange"; import type { PerpMarket } from "@hypeterminal/hl-react/markets/types"; -import { subscriptionRegistry } from "@hypeterminal/hl-react/registries/subscription"; +import { getAccumulateConfig } from "@hypeterminal/hl-react/registries/subscription"; import { estimatePayloadSizeBytes } from "@hypeterminal/hl-react/internal/websocket/payload-guard"; import { RingBuffer } from "@hypeterminal/hl-react/internal/circular-buffer/ring-buffer"; ``` diff --git a/packages/hl-react/package.json b/packages/hl-react/package.json index aad23980..5ffaefff 100644 --- a/packages/hl-react/package.json +++ b/packages/hl-react/package.json @@ -11,8 +11,13 @@ "./provider": "./src/provider.tsx", "./clients": "./src/clients.ts", "./create-config": "./src/create-config.ts", + "./hooks/useApiStatus": "./src/hooks/useApiStatus.ts", + "./hooks/useClients": "./src/hooks/useClients.ts", + "./hooks/useExchange": "./src/hooks/useExchange.ts", "./hooks/useInfo": "./src/hooks/useInfo.ts", "./hooks/useSubscription": "./src/hooks/useSubscription.ts", + "./hooks/useTradingGuard": "./src/hooks/useTradingGuard.ts", + "./hooks/useTransport": "./src/hooks/useTransport.ts", "./lru": "./src/lru.ts", "./hooks/utils/useSub": "./src/hooks/utils/useSub.ts", "./markets/types": "./src/markets/types.ts", diff --git a/packages/hl-react/src/hooks/useClients.ts b/packages/hl-react/src/hooks/useClients.ts index 30c5b381..018f30de 100644 --- a/packages/hl-react/src/hooks/useClients.ts +++ b/packages/hl-react/src/hooks/useClients.ts @@ -10,7 +10,7 @@ export interface HyperliquidClients { subscription: SubscriptionClient; /** Exchange client using agent wallet - for L1 trading actions (order, cancel, etc.) */ trading: ExchangeClient | null; - /** Exchange client using user wallet - for user-signed actions (approveAgent, approveBuilderFee, withdraw, etc.) */ + /** Exchange client using user wallet - for EIP-712/user-signed actions such as approvals, transfers, and withdrawals. */ user: ExchangeClient | null; } diff --git a/packages/hl-react/src/registries/exchange.ts b/packages/hl-react/src/registries/exchange.ts index df95a861..383f7e21 100644 --- a/packages/hl-react/src/registries/exchange.ts +++ b/packages/hl-react/src/registries/exchange.ts @@ -11,12 +11,20 @@ interface ExchangeMethodConfig { const USER_SIGNED_METHODS: ReadonlySet = new Set([ "approveAgent", "approveBuilderFee", + "cDeposit", + "cWithdraw", + "convertToMultiSigUser", + "linkStakingUser", "sendAsset", + "sendToEvmWithData", "spotSend", + "stakingLinkDisableTradingUser", "tokenDelegate", "usdClassTransfer", "usdSend", "userDexAbstraction", + "userPortfolioMargin", + "userSetAbstraction", "withdraw3", ]); From 0acaf316f9d342087793eb37aa7d165e2d41d70c Mon Sep 17 00:00:00 2001 From: vipineth Date: Fri, 19 Jun 2026 09:47:33 +0530 Subject: [PATCH 2/4] fix(hl-react): align registry with sdk types --- apps/terminal/src/lib/tests/hyperliquid-registries.test.ts | 1 - packages/hl-react/README.md | 1 - packages/hl-react/src/registries/exchange.ts | 1 - 3 files changed, 3 deletions(-) diff --git a/apps/terminal/src/lib/tests/hyperliquid-registries.test.ts b/apps/terminal/src/lib/tests/hyperliquid-registries.test.ts index 30828a75..a58001c0 100644 --- a/apps/terminal/src/lib/tests/hyperliquid-registries.test.ts +++ b/apps/terminal/src/lib/tests/hyperliquid-registries.test.ts @@ -33,7 +33,6 @@ describe("exchange registry", () => { "sendAsset", "sendToEvmWithData", "spotSend", - "stakingLinkDisableTradingUser", "tokenDelegate", "usdClassTransfer", "usdSend", diff --git a/packages/hl-react/README.md b/packages/hl-react/README.md index 5e822cc2..cf886880 100644 --- a/packages/hl-react/README.md +++ b/packages/hl-react/README.md @@ -131,7 +131,6 @@ linkStakingUser sendAsset sendToEvmWithData spotSend -stakingLinkDisableTradingUser tokenDelegate usdClassTransfer usdSend diff --git a/packages/hl-react/src/registries/exchange.ts b/packages/hl-react/src/registries/exchange.ts index 383f7e21..2f0bb0f0 100644 --- a/packages/hl-react/src/registries/exchange.ts +++ b/packages/hl-react/src/registries/exchange.ts @@ -18,7 +18,6 @@ const USER_SIGNED_METHODS: ReadonlySet = new Set([ "sendAsset", "sendToEvmWithData", "spotSend", - "stakingLinkDisableTradingUser", "tokenDelegate", "usdClassTransfer", "usdSend", From e2ebc34dc6132d4c80580c75030b574b053909e3 Mon Sep 17 00:00:00 2001 From: vipineth Date: Fri, 19 Jun 2026 11:02:05 +0530 Subject: [PATCH 3/4] fix(hl-react): address api coverage feedback --- .../src/lib/tests/hl-react-exports.test.ts | 31 +++++++++++++++++++ .../lib/tests/hyperliquid-registries.test.ts | 8 +++++ packages/hl-react/README.md | 4 +-- packages/hl-react/src/registries/exchange.ts | 4 +++ 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 apps/terminal/src/lib/tests/hl-react-exports.test.ts diff --git a/apps/terminal/src/lib/tests/hl-react-exports.test.ts b/apps/terminal/src/lib/tests/hl-react-exports.test.ts new file mode 100644 index 00000000..d1b9190e --- /dev/null +++ b/apps/terminal/src/lib/tests/hl-react-exports.test.ts @@ -0,0 +1,31 @@ +import { existsSync, readFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { describe, expect, it } from "vitest"; + +interface PackageJson { + exports: Record; +} + +const TEST_DIR = dirname(fileURLToPath(import.meta.url)); +const PACKAGE_DIR = resolve(TEST_DIR, "../../../../../packages/hl-react"); +const packageJson = JSON.parse(readFileSync(resolve(PACKAGE_DIR, "package.json"), "utf8")) as PackageJson; + +describe("@hypeterminal/hl-react package exports", () => { + it("points every subpath export at an existing source file", () => { + for (const [subpath, target] of Object.entries(packageJson.exports)) { + if (target.includes("*")) continue; + expect(existsSync(resolve(PACKAGE_DIR, target)), `${subpath} -> ${target}`).toBe(true); + } + }); + + it("exposes direct hook subpaths", () => { + expect(packageJson.exports).toMatchObject({ + "./hooks/useApiStatus": "./src/hooks/useApiStatus.ts", + "./hooks/useClients": "./src/hooks/useClients.ts", + "./hooks/useExchange": "./src/hooks/useExchange.ts", + "./hooks/useTradingGuard": "./src/hooks/useTradingGuard.ts", + "./hooks/useTransport": "./src/hooks/useTransport.ts", + }); + }); +}); diff --git a/apps/terminal/src/lib/tests/hyperliquid-registries.test.ts b/apps/terminal/src/lib/tests/hyperliquid-registries.test.ts index a58001c0..abd596c2 100644 --- a/apps/terminal/src/lib/tests/hyperliquid-registries.test.ts +++ b/apps/terminal/src/lib/tests/hyperliquid-registries.test.ts @@ -57,6 +57,14 @@ describe("exchange registry", () => { } }); + it("routes representative L1 methods to trading client", () => { + const expectedL1Methods: ExchangeMethod[] = ["agentEnableDexAbstraction", "setDisplayName"]; + for (const method of expectedL1Methods) { + const config = getExchangeMethodConfig(method); + expect(config.client, `${method} should use trading client`).toBe("trading"); + } + }); + it("only injects builder config for order", () => { for (const method of EXCHANGE_METHODS) { const config = getExchangeMethodConfig(method); diff --git a/packages/hl-react/README.md b/packages/hl-react/README.md index cf886880..71ea0a2d 100644 --- a/packages/hl-react/README.md +++ b/packages/hl-react/README.md @@ -83,9 +83,9 @@ Endpoint coverage is intentionally generic rather than one-hook-per-endpoint: | `SubscriptionClient` | `useSubscription(method, params, options)` | `useSubscription("allDexsAssetCtxs", undefined)` | | `ExchangeClient` | `useExchange(method, options)` | `useExchange("sendToEvmWithData")` | -As of `@nktkas/hyperliquid` `0.32.2`, newer SDK methods such as prediction-market info (`outcomeMeta`, `settledOutcome`, `perpCategories`), builder/HIP-3 info (`perpDexs`, `perpDexStatus`, `userDexAbstraction`, `perpDexLimits`), richer subscriptions (`allDexsAssetCtxs`, `allDexsClearinghouseState`, `fastAssetCtxs`, `outcomeMetaUpdates`), and exchange actions (`sendToEvmWithData`, `setDisplayName`, `convertToMultiSigUser`, `userSetAbstraction`, `userPortfolioMargin`) are available through the same generic hooks. +As of `@nktkas/hyperliquid` `0.32.2`, newer SDK methods such as prediction-market info (`outcomeMeta`, `perpCategories`), builder/HIP-3 info (`perpDexs`, `perpDexStatus`, `userDexAbstraction`, `perpDexLimits`), richer subscriptions (`allDexsAssetCtxs`, `allDexsClearinghouseState`), and exchange actions (`sendToEvmWithData`, `setDisplayName`, `convertToMultiSigUser`, `userSetAbstraction`, `userPortfolioMargin`) are available through the same generic hooks. -HIP-4 status: Hyperliquid's HIP-4 outcome-market APIs are present in the upstream SDK under outcome-market method names rather than a literal `HIP-4` namespace. Use `useInfo("outcomeMeta", undefined)`, `useInfo("settledOutcome", { outcome })`, `useSubscription("outcomeMetaUpdates", undefined)`, and `useExchange("userOutcome")` for the current SDK surface. +HIP-4 status: Hyperliquid's HIP-4 outcome-market API is present in the locked `0.32.2` SDK as `useInfo("outcomeMeta", undefined)`. Upstream `main` also contains additional outcome-market methods, but this package documents and tests against the pinned workspace SDK version. ### `useInfo` — REST one-shot queries diff --git a/packages/hl-react/src/registries/exchange.ts b/packages/hl-react/src/registries/exchange.ts index 2f0bb0f0..7137c7e7 100644 --- a/packages/hl-react/src/registries/exchange.ts +++ b/packages/hl-react/src/registries/exchange.ts @@ -27,6 +27,10 @@ const USER_SIGNED_METHODS: ReadonlySet = new Set([ "withdraw3", ]); +// All other ExchangeClient methods are L1 actions and intentionally use the +// agent trading client. Examples include setDisplayName and agentEnableDexAbstraction +// agent-side actions in the SDK, which call executeL1Action rather than +// executeUserSignedAction. const BUILDER_INJECTED_METHODS: ReadonlySet = new Set(["order"]); const CLIENT_KEY_METHODS: ReadonlySet = new Set(["order", "cancel"]); From 40c8fa868b92a50c9d08d8ddb2b4fe0aa5377eb8 Mon Sep 17 00:00:00 2001 From: vipineth Date: Fri, 19 Jun 2026 15:22:37 +0530 Subject: [PATCH 4/4] fix: address hl-react review feedback --- .../src/lib/tests/hl-react-exports.test.ts | 37 +++++++++++++++++-- .../lib/tests/hyperliquid-registries.test.ts | 8 ---- packages/hl-react/README.md | 2 +- packages/hl-react/src/registries/exchange.ts | 6 +-- 4 files changed, 36 insertions(+), 17 deletions(-) diff --git a/apps/terminal/src/lib/tests/hl-react-exports.test.ts b/apps/terminal/src/lib/tests/hl-react-exports.test.ts index d1b9190e..8f2c7c51 100644 --- a/apps/terminal/src/lib/tests/hl-react-exports.test.ts +++ b/apps/terminal/src/lib/tests/hl-react-exports.test.ts @@ -3,19 +3,48 @@ import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { describe, expect, it } from "vitest"; +type PackageJsonExportTarget = + | string + | null + | PackageJsonExportTarget[] + | { [condition: string]: PackageJsonExportTarget }; + interface PackageJson { - exports: Record; + exports: Record; } const TEST_DIR = dirname(fileURLToPath(import.meta.url)); -const PACKAGE_DIR = resolve(TEST_DIR, "../../../../../packages/hl-react"); +const WORKSPACE_ROOT = findWorkspaceRoot(TEST_DIR); +const PACKAGE_DIR = resolve(WORKSPACE_ROOT, "packages/hl-react"); const packageJson = JSON.parse(readFileSync(resolve(PACKAGE_DIR, "package.json"), "utf8")) as PackageJson; +function findWorkspaceRoot(startDir: string): string { + let current = startDir; + + while (true) { + if (existsSync(resolve(current, "pnpm-workspace.yaml"))) return current; + + const parent = dirname(current); + if (parent === current) throw new Error("Could not locate pnpm-workspace.yaml"); + current = parent; + } +} + +function getExportTargetPaths(target: PackageJsonExportTarget): string[] { + if (typeof target === "string") return [target]; + if (target === null) return []; + if (Array.isArray(target)) return target.flatMap(getExportTargetPaths); + + return Object.values(target).flatMap(getExportTargetPaths); +} + describe("@hypeterminal/hl-react package exports", () => { it("points every subpath export at an existing source file", () => { for (const [subpath, target] of Object.entries(packageJson.exports)) { - if (target.includes("*")) continue; - expect(existsSync(resolve(PACKAGE_DIR, target)), `${subpath} -> ${target}`).toBe(true); + for (const targetPath of getExportTargetPaths(target)) { + if (targetPath.includes("*")) continue; + expect(existsSync(resolve(PACKAGE_DIR, targetPath)), `${subpath} -> ${targetPath}`).toBe(true); + } } }); diff --git a/apps/terminal/src/lib/tests/hyperliquid-registries.test.ts b/apps/terminal/src/lib/tests/hyperliquid-registries.test.ts index abd596c2..a58001c0 100644 --- a/apps/terminal/src/lib/tests/hyperliquid-registries.test.ts +++ b/apps/terminal/src/lib/tests/hyperliquid-registries.test.ts @@ -57,14 +57,6 @@ describe("exchange registry", () => { } }); - it("routes representative L1 methods to trading client", () => { - const expectedL1Methods: ExchangeMethod[] = ["agentEnableDexAbstraction", "setDisplayName"]; - for (const method of expectedL1Methods) { - const config = getExchangeMethodConfig(method); - expect(config.client, `${method} should use trading client`).toBe("trading"); - } - }); - it("only injects builder config for order", () => { for (const method of EXCHANGE_METHODS) { const config = getExchangeMethodConfig(method); diff --git a/packages/hl-react/README.md b/packages/hl-react/README.md index 71ea0a2d..17fd8c44 100644 --- a/packages/hl-react/README.md +++ b/packages/hl-react/README.md @@ -85,7 +85,7 @@ Endpoint coverage is intentionally generic rather than one-hook-per-endpoint: As of `@nktkas/hyperliquid` `0.32.2`, newer SDK methods such as prediction-market info (`outcomeMeta`, `perpCategories`), builder/HIP-3 info (`perpDexs`, `perpDexStatus`, `userDexAbstraction`, `perpDexLimits`), richer subscriptions (`allDexsAssetCtxs`, `allDexsClearinghouseState`), and exchange actions (`sendToEvmWithData`, `setDisplayName`, `convertToMultiSigUser`, `userSetAbstraction`, `userPortfolioMargin`) are available through the same generic hooks. -HIP-4 status: Hyperliquid's HIP-4 outcome-market API is present in the locked `0.32.2` SDK as `useInfo("outcomeMeta", undefined)`. Upstream `main` also contains additional outcome-market methods, but this package documents and tests against the pinned workspace SDK version. +HIP-4 status: Hyperliquid's HIP-4 outcome-market API is present in the locked `0.32.2` SDK as `useInfo("outcomeMeta", undefined)`. Upstream `main` also contains additional outcome-market methods, but this package documents and tests against the pinned workspace SDK version. The peer dependency accepts newer 0.x SDK minors for install compatibility; those newer minors are untested until the workspace SDK pin is bumped. ### `useInfo` — REST one-shot queries diff --git a/packages/hl-react/src/registries/exchange.ts b/packages/hl-react/src/registries/exchange.ts index 7137c7e7..3de9de2f 100644 --- a/packages/hl-react/src/registries/exchange.ts +++ b/packages/hl-react/src/registries/exchange.ts @@ -27,10 +27,8 @@ const USER_SIGNED_METHODS: ReadonlySet = new Set([ "withdraw3", ]); -// All other ExchangeClient methods are L1 actions and intentionally use the -// agent trading client. Examples include setDisplayName and agentEnableDexAbstraction -// agent-side actions in the SDK, which call executeL1Action rather than -// executeUserSignedAction. +// All other ExchangeClient methods are L1 actions routed to the trading client. +// They call executeL1Action in the SDK, not executeUserSignedAction. const BUILDER_INJECTED_METHODS: ReadonlySet = new Set(["order"]); const CLIENT_KEY_METHODS: ReadonlySet = new Set(["order", "cancel"]);