From 7c61bde4e1689e5f837e4b52ba1738d60bc6f357 Mon Sep 17 00:00:00 2001 From: nktkas Date: Sat, 1 Aug 2026 21:39:38 +0300 Subject: [PATCH] refactor(api): add TWAP trigger and stop price fields Co-authored-by: Claude Opus 5 (1M context) --- src/api/info/_methods/_base/_schemas.ts | 15 +++++++++++++++ src/api/info/_methods/twapHistory.ts | 4 +++- tests/api/exchange/_t.ts | 2 +- tests/api/exchange/twapOrder.test.ts | 2 +- tests/api/info/twapHistory.test.ts | 4 +++- tests/api/subscription/_t.ts | 6 ++++-- tests/api/subscription/twapStates.test.ts | 2 ++ tests/api/subscription/userEvents.test.ts | 4 ++++ tests/api/subscription/userTwapHistory.test.ts | 8 ++++++-- 9 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/api/info/_methods/_base/_schemas.ts b/src/api/info/_methods/_base/_schemas.ts index 77f327675..59e4cef9a 100644 --- a/src/api/info/_methods/_base/_schemas.ts +++ b/src/api/info/_methods/_base/_schemas.ts @@ -243,6 +243,11 @@ export type TwapState = { reduceOnly: boolean; /** Order side ("B" = Bid/Buy, "A" = Ask/Sell). */ side: "B" | "A"; + /** + * Price at which the order is terminated; `null` when unset. + * @pattern ^[0-9]+(\.[0-9]+)?$ + */ + stopPx: string | null; /** * Order size. * @pattern ^[0-9]+(\.[0-9]+)?$ @@ -250,6 +255,16 @@ export type TwapState = { sz: string; /** Start time of the TWAP order (in ms since epoch). */ timestamp: number; + /** Condition that activates the order; `null` when unset. */ + trigger: { + /** + * Trigger price. + * @pattern ^[0-9]+(\.[0-9]+)?$ + */ + px: string; + /** Activates when the mark price is above (`true`) or below (`false`) the trigger price. */ + above: boolean; + } | null; /** * User address. * @pattern ^0x[a-fA-F0-9]{40}$ diff --git a/src/api/info/_methods/twapHistory.ts b/src/api/info/_methods/twapHistory.ts index ac6c8fe1f..81ea3b7f8 100644 --- a/src/api/info/_methods/twapHistory.ts +++ b/src/api/info/_methods/twapHistory.ts @@ -35,11 +35,13 @@ export type TwapHistoryResponse = { * - `"finished"`: Fully executed. * - `"activated"`: Active and executing. * - `"terminated"`: Terminated. + * - `"waitingForTrigger"`: Awaiting the trigger price. + * - `"stopped"`: Terminated by the stop price. * - `"error"`: An error occurred. */ status: { /** Status of the TWAP order. */ - status: "finished" | "activated" | "terminated"; + status: "finished" | "activated" | "terminated" | "waitingForTrigger" | "stopped"; } | { /** Status of the TWAP order. */ status: "error"; diff --git a/tests/api/exchange/_t.ts b/tests/api/exchange/_t.ts index dec875d66..5e1656944 100644 --- a/tests/api/exchange/_t.ts +++ b/tests/api/exchange/_t.ts @@ -242,7 +242,7 @@ export async function createTWAP( const midPx = allMids[symbol]; // Calculate order parameters - const sz = formatSize(55 / Number(midPx), szDecimals); + const sz = formatSize(110 / Number(midPx), szDecimals); // Place TWAP order const result = await client.twapOrder({ diff --git a/tests/api/exchange/twapOrder.test.ts b/tests/api/exchange/twapOrder.test.ts index 8815cf952..a881132e5 100644 --- a/tests/api/exchange/twapOrder.test.ts +++ b/tests/api/exchange/twapOrder.test.ts @@ -19,7 +19,7 @@ runTest({ const szDecimals = symbolConverter.getSzDecimals("SOL")!; const midPx = allMids["SOL"]; - const sz = formatSize(60 / parseFloat(midPx), szDecimals); + const sz = formatSize(110 / parseFloat(midPx), szDecimals); const params: TwapOrderParameters[] = [ // b=true | r=false | t=false diff --git a/tests/api/info/twapHistory.test.ts b/tests/api/info/twapHistory.test.ts index d4032a5a4..1d3615c6b 100644 --- a/tests/api/info/twapHistory.test.ts +++ b/tests/api/info/twapHistory.test.ts @@ -11,9 +11,11 @@ const paramsSchema = valibotToJsonSchema(v.omit(TwapHistoryRequest, ["type"])); runTest({ name: "twapHistory", + isTestnet: false, codeTestFn: async (_t, client) => { const params: TwapHistoryParameters[] = [ - { user: "0xe019d6167E7e324aEd003d94098496b6d986aB05" }, + { user: "0x03ce7863a2b62f4e227fd98605b79beb32618c76" }, + { user: "0x0132157369b0d073dd99011da1777920a025fd77" }, // trigger.above: false ]; const data = await Promise.all(params.map((p) => client.twapHistory(p))); diff --git a/tests/api/subscription/_t.ts b/tests/api/subscription/_t.ts index cf85e9360..82a855fd2 100644 --- a/tests/api/subscription/_t.ts +++ b/tests/api/subscription/_t.ts @@ -22,16 +22,18 @@ const OFFLINE = Deno.args.includes("--offline"); export function runTest(options: { name: string; mode: "api" | "rpc"; + isTestnet?: boolean; fn: (t: Deno.TestContext, client: SubscriptionClient) => Promise; }): void { - const { name, mode, fn } = options; + const { name, mode, isTestnet = true, fn } = options; Deno.test(name, { ignore: OFFLINE }, async (t) => { await new Promise((r) => setTimeout(r, WAIT)); // delay to avoid rate limits // --- Preparation ------------------------------------------------ - const transport = new WebSocketTransport({ url: `wss://${mode}.hyperliquid-testnet.xyz/ws`, isTestnet: true }); + const domain = isTestnet ? "hyperliquid-testnet.xyz" : "hyperliquid.xyz"; + const transport = new WebSocketTransport({ url: `wss://${mode}.${domain}/ws`, isTestnet }); await transport.ready(); const subsClient = new SubscriptionClient({ transport }); diff --git a/tests/api/subscription/twapStates.test.ts b/tests/api/subscription/twapStates.test.ts index 4a84443fe..1436db88b 100644 --- a/tests/api/subscription/twapStates.test.ts +++ b/tests/api/subscription/twapStates.test.ts @@ -33,6 +33,8 @@ runTestWithExchange({ schemaCoverage(paramsSchema, params); schemaCoverage(responseSchema, data, [ "#/properties/states/items/items/1/properties/side/enum/1", + "#/properties/states/items/items/1/properties/stopPx/defined", + "#/properties/states/items/items/1/properties/trigger/defined", ]); }, }); diff --git a/tests/api/subscription/userEvents.test.ts b/tests/api/subscription/userEvents.test.ts index a2a56ee4d..2acd933be 100644 --- a/tests/api/subscription/userEvents.test.ts +++ b/tests/api/subscription/userEvents.test.ts @@ -34,8 +34,12 @@ runTestWithExchange({ "#/anyOf/2", "#/anyOf/3", "#/anyOf/4/properties/twapHistory/items/properties/state/properties/side/enum/1", + "#/anyOf/4/properties/twapHistory/items/properties/state/properties/stopPx/defined", + "#/anyOf/4/properties/twapHistory/items/properties/state/properties/trigger/defined", "#/anyOf/4/properties/twapHistory/items/properties/status/anyOf/0/properties/status/enum/0", "#/anyOf/4/properties/twapHistory/items/properties/status/anyOf/0/properties/status/enum/2", + "#/anyOf/4/properties/twapHistory/items/properties/status/anyOf/0/properties/status/enum/3", + "#/anyOf/4/properties/twapHistory/items/properties/status/anyOf/0/properties/status/enum/4", "#/anyOf/4/properties/twapHistory/items/properties/status/anyOf/1", "#/anyOf/4/properties/twapHistory/items/properties/twapId/missing", "#/anyOf/5/properties/twapSliceFills/items/properties/fill/properties/side/enum/1", diff --git a/tests/api/subscription/userTwapHistory.test.ts b/tests/api/subscription/userTwapHistory.test.ts index 05b9152a2..515d29053 100644 --- a/tests/api/subscription/userTwapHistory.test.ts +++ b/tests/api/subscription/userTwapHistory.test.ts @@ -16,10 +16,13 @@ const paramsSchema = valibotToJsonSchema(v.omit(UserTwapHistoryRequest, ["type"] runTest({ name: "userTwapHistory", mode: "api", + isTestnet: false, fn: async (_t, client) => { const params: UserTwapHistoryParameters[] = [ - { user: "0x563C175E6f11582f65D6d9E360A618699DEe14a9" }, - { user: "0xe019d6167E7e324aEd003d94098496b6d986aB05" }, + { user: "0x03ce7863a2b62f4e227fd98605b79beb32618c76" }, // trigger.above: true + { user: "0x0132157369b0d073dd99011da1777920a025fd77" }, // trigger.above: false + { user: "0x051748895c6ed4fab50828bebe8e62e665134d23" }, // "stopped" + { user: "0x06d5af06a3a7d29909e1cdc7a9deded2fb14ab57" }, // "error" ]; const data = await collectEventsOverTime(async (cb) => { @@ -29,6 +32,7 @@ runTest({ schemaCoverage(paramsSchema, params); schemaCoverage(responseSchema, data, [ "#/properties/isSnapshot/missing", + "#/properties/history/items/properties/twapId/missing", ]); }, });