opentool 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1930 @@
1
+ import { encode } from '@msgpack/msgpack';
2
+ import { keccak_256 } from '@noble/hashes/sha3';
3
+ import { hexToBytes, concatBytes, bytesToHex } from '@noble/hashes/utils';
4
+ import { parseUnits, encodeFunctionData, erc20Abi } from 'viem';
5
+
6
+ // src/adapters/hyperliquid/base.ts
7
+ var CACHE_TTL_MS = 5 * 60 * 1e3;
8
+ var API_BASES = {
9
+ mainnet: "https://api.hyperliquid.xyz",
10
+ testnet: "https://api.hyperliquid-testnet.xyz"
11
+ };
12
+ var HL_ENDPOINT = {
13
+ mainnet: "https://api.hyperliquid.xyz",
14
+ testnet: "https://api.hyperliquid-testnet.xyz"
15
+ };
16
+ var HL_CHAIN_LABEL = {
17
+ mainnet: "Mainnet",
18
+ testnet: "Testnet"
19
+ };
20
+ var HL_BRIDGE_ADDRESSES = {
21
+ mainnet: "0x2df1c51e09aecf9cacb7bc98cb1742757f163df7",
22
+ testnet: "0x08cfc1b6b2dcf36a1480b99353a354aa8ac56f89"
23
+ };
24
+ var HL_USDC_ADDRESSES = {
25
+ mainnet: "0xaf88d065e77c8cc2239327c5edb3a432268e5831",
26
+ testnet: "0x1baAbB04529D43a73232B713C0FE471f7c7334d5"
27
+ };
28
+ var HL_SIGNATURE_CHAIN_ID = {
29
+ mainnet: "0xa4b1",
30
+ testnet: "0x66eee"
31
+ };
32
+ var EXCHANGE_TYPED_DATA_DOMAIN = {
33
+ name: "Exchange",
34
+ version: "1",
35
+ chainId: 1337,
36
+ verifyingContract: "0x0000000000000000000000000000000000000000"
37
+ };
38
+ var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
39
+ var MIN_DEPOSIT_USDC = 5;
40
+ var BUILDER_CODE = {
41
+ address: "0x4b2aec4F91612849d6e20C9c1881FabB1A48cd12",
42
+ fee: 100
43
+ };
44
+ var metaCache = /* @__PURE__ */ new Map();
45
+ var spotMetaCache = /* @__PURE__ */ new Map();
46
+ var perpDexsCache = /* @__PURE__ */ new Map();
47
+ var UNKNOWN_SYMBOL = "UNKNOWN";
48
+ var extractDexPrefix = (value) => {
49
+ if (!value) return null;
50
+ const trimmed = value.trim();
51
+ if (!trimmed) return null;
52
+ if (!trimmed.includes(":")) return null;
53
+ if (trimmed.startsWith("@")) return null;
54
+ const [prefix] = trimmed.split(":");
55
+ const dex = prefix?.trim().toLowerCase() ?? "";
56
+ return dex || null;
57
+ };
58
+ var normalizeHyperliquidBase = (value) => {
59
+ if (!value) return null;
60
+ const trimmed = value.trim();
61
+ if (!trimmed) return null;
62
+ const withoutDex = trimmed.includes(":") ? trimmed.split(":").slice(1).join(":") : trimmed;
63
+ const base = withoutDex.split("-")[0] ?? withoutDex;
64
+ const normalized = (base.split("/")[0] ?? base).trim().toUpperCase();
65
+ if (!normalized || normalized === UNKNOWN_SYMBOL) return null;
66
+ return normalized;
67
+ };
68
+ var normalizeSpotTokenName = (value) => {
69
+ const raw = (value ?? "").trim().toUpperCase();
70
+ if (!raw) return "";
71
+ if (raw.endsWith("0") && raw.length > 1) {
72
+ return raw.slice(0, -1);
73
+ }
74
+ return raw;
75
+ };
76
+ var parseHyperliquidPair = (value) => {
77
+ if (!value) return null;
78
+ const trimmed = value.trim();
79
+ if (!trimmed) return null;
80
+ const withoutDex = trimmed.includes(":") ? trimmed.split(":").slice(1).join(":") : trimmed;
81
+ const separator = withoutDex.includes("/") ? "/" : withoutDex.includes("-") ? "-" : null;
82
+ if (!separator) return null;
83
+ const [baseRaw, ...rest] = withoutDex.split(separator);
84
+ const quoteRaw = rest.join(separator);
85
+ if (!baseRaw || !quoteRaw) return null;
86
+ const base = baseRaw.trim().toUpperCase();
87
+ const quote = quoteRaw.trim().toUpperCase();
88
+ if (!base || !quote) return null;
89
+ return { base, quote };
90
+ };
91
+ function buildHyperliquidMarketIdentity(input) {
92
+ const rawSymbol = input.rawSymbol ?? input.symbol;
93
+ const dex = extractDexPrefix(rawSymbol);
94
+ const pair = parseHyperliquidPair(rawSymbol) ?? parseHyperliquidPair(input.symbol);
95
+ const isSpot = input.isSpot ?? (Boolean(pair) || rawSymbol.startsWith("@") || input.symbol.includes("/"));
96
+ const base = (input.base ? input.base.trim().toUpperCase() : null) ?? pair?.base ?? normalizeHyperliquidBase(input.symbol) ?? normalizeHyperliquidBase(rawSymbol);
97
+ if (!base) return null;
98
+ if (isSpot) {
99
+ const quote = (input.quote ? input.quote.trim().toUpperCase() : null) ?? pair?.quote ?? null;
100
+ if (!quote) return null;
101
+ return {
102
+ market_type: "spot",
103
+ venue: "hyperliquid",
104
+ environment: input.environment,
105
+ base,
106
+ quote,
107
+ dex,
108
+ raw_symbol: rawSymbol ?? null,
109
+ canonical_symbol: `spot:hyperliquid:${base}-${quote}`
110
+ };
111
+ }
112
+ return {
113
+ market_type: "perp",
114
+ venue: "hyperliquid",
115
+ environment: input.environment,
116
+ base,
117
+ dex,
118
+ raw_symbol: rawSymbol ?? null,
119
+ canonical_symbol: `perp:hyperliquid:${base}`
120
+ };
121
+ }
122
+ function resolveHyperliquidAbstractionFromMode(mode) {
123
+ switch (mode) {
124
+ case "standard":
125
+ return "disabled";
126
+ case "unified":
127
+ return "unifiedAccount";
128
+ case "portfolio":
129
+ return "portfolioMargin";
130
+ default: {
131
+ const _exhaustive = mode;
132
+ return _exhaustive;
133
+ }
134
+ }
135
+ }
136
+ var DEFAULT_HYPERLIQUID_MARKET_SLIPPAGE_BPS = 30;
137
+ function formatRoundedDecimal(value, decimals) {
138
+ const precision = Math.max(0, Math.min(12, Math.floor(decimals)));
139
+ const factor = 10 ** precision;
140
+ const rounded = Math.round(value * factor) / factor;
141
+ if (!Number.isFinite(rounded) || rounded <= 0) {
142
+ throw new Error("Price must be positive.");
143
+ }
144
+ const fixed = rounded.toFixed(precision);
145
+ return fixed.replace(/\.?0+$/, "");
146
+ }
147
+ function computeHyperliquidMarketIocLimitPrice(params) {
148
+ const bps = params.slippageBps ?? DEFAULT_HYPERLIQUID_MARKET_SLIPPAGE_BPS;
149
+ const decimals = params.decimals ?? 6;
150
+ if (!Number.isFinite(params.markPrice) || params.markPrice <= 0) {
151
+ throw new Error("markPrice must be a positive number.");
152
+ }
153
+ if (!Number.isFinite(bps) || bps < 0) {
154
+ throw new Error("slippageBps must be a non-negative number.");
155
+ }
156
+ const slippage = bps / 1e4;
157
+ const multiplier = params.side === "buy" ? 1 + slippage : 1 - slippage;
158
+ const price = params.markPrice * multiplier;
159
+ return formatRoundedDecimal(price, decimals);
160
+ }
161
+ var HyperliquidApiError = class extends Error {
162
+ constructor(message, response) {
163
+ super(message);
164
+ this.response = response;
165
+ this.name = "HyperliquidApiError";
166
+ }
167
+ };
168
+ var HyperliquidGuardError = class extends Error {
169
+ constructor(message) {
170
+ super(message);
171
+ this.name = "HyperliquidGuardError";
172
+ }
173
+ };
174
+ var HyperliquidTermsError = class extends HyperliquidGuardError {
175
+ constructor(message = "Hyperliquid terms must be accepted before proceeding.") {
176
+ super(message);
177
+ this.name = "HyperliquidTermsError";
178
+ }
179
+ };
180
+ var HyperliquidBuilderApprovalError = class extends HyperliquidGuardError {
181
+ constructor(message = "Hyperliquid builder approval is required before using builder codes.") {
182
+ super(message);
183
+ this.name = "HyperliquidBuilderApprovalError";
184
+ }
185
+ };
186
+ function createMonotonicNonceFactory(start = Date.now()) {
187
+ let last = start;
188
+ return () => {
189
+ const now = Date.now();
190
+ if (now > last) {
191
+ last = now;
192
+ } else {
193
+ last += 1;
194
+ }
195
+ return last;
196
+ };
197
+ }
198
+ async function getUniverse(args) {
199
+ const dexKey = args.dex ? args.dex.trim().toLowerCase() : "";
200
+ const cacheKey = `${args.environment}:${args.baseUrl}:${dexKey}`;
201
+ const cached = metaCache.get(cacheKey);
202
+ if (cached && Date.now() - cached.fetchedAt < CACHE_TTL_MS) {
203
+ return cached.universe;
204
+ }
205
+ const response = await args.fetcher(`${args.baseUrl}/info`, {
206
+ method: "POST",
207
+ headers: { "content-type": "application/json" },
208
+ body: JSON.stringify(dexKey ? { type: "meta", dex: dexKey } : { type: "meta" })
209
+ });
210
+ const json = await response.json().catch(() => null);
211
+ if (!response.ok || !json?.universe) {
212
+ throw new HyperliquidApiError(
213
+ "Unable to load Hyperliquid metadata.",
214
+ json ?? { status: response.status }
215
+ );
216
+ }
217
+ metaCache.set(cacheKey, { fetchedAt: Date.now(), universe: json.universe });
218
+ return json.universe;
219
+ }
220
+ async function getSpotMeta(args) {
221
+ const cacheKey = `${args.environment}:${args.baseUrl}`;
222
+ const cached = spotMetaCache.get(cacheKey);
223
+ if (cached && Date.now() - cached.fetchedAt < CACHE_TTL_MS) {
224
+ return { universe: cached.universe, tokens: cached.tokens };
225
+ }
226
+ const response = await args.fetcher(`${args.baseUrl}/info`, {
227
+ method: "POST",
228
+ headers: { "content-type": "application/json" },
229
+ body: JSON.stringify({ type: "spotMeta" })
230
+ });
231
+ const json = await response.json().catch(() => null);
232
+ if (!response.ok || !json?.universe) {
233
+ throw new HyperliquidApiError(
234
+ "Unable to load Hyperliquid spot metadata.",
235
+ json ?? { status: response.status }
236
+ );
237
+ }
238
+ const universe = json.universe ?? [];
239
+ const tokens = json.tokens ?? [];
240
+ spotMetaCache.set(cacheKey, { fetchedAt: Date.now(), universe, tokens });
241
+ return { universe, tokens };
242
+ }
243
+ function resolveAssetIndex(symbol, universe) {
244
+ const [raw] = symbol.split("-");
245
+ const target = raw.trim();
246
+ const index = universe.findIndex((entry) => entry.name.toUpperCase() === target.toUpperCase());
247
+ if (index === -1) {
248
+ throw new Error(`Unknown Hyperliquid asset symbol: ${symbol}`);
249
+ }
250
+ return index;
251
+ }
252
+ async function getPerpDexs(args) {
253
+ const cacheKey = `${args.environment}:${args.baseUrl}`;
254
+ const cached = perpDexsCache.get(cacheKey);
255
+ if (cached && Date.now() - cached.fetchedAt < CACHE_TTL_MS) {
256
+ return cached.dexs;
257
+ }
258
+ const response = await args.fetcher(`${args.baseUrl}/info`, {
259
+ method: "POST",
260
+ headers: { "content-type": "application/json" },
261
+ body: JSON.stringify({ type: "perpDexs" })
262
+ });
263
+ const json = await response.json().catch(() => null);
264
+ if (!response.ok || !Array.isArray(json)) {
265
+ throw new HyperliquidApiError(
266
+ "Unable to load Hyperliquid perp dex metadata.",
267
+ json ?? { status: response.status }
268
+ );
269
+ }
270
+ perpDexsCache.set(cacheKey, { fetchedAt: Date.now(), dexs: json });
271
+ return json;
272
+ }
273
+ async function resolveDexIndex(args) {
274
+ const dexs = await getPerpDexs(args);
275
+ const target = args.dex.trim().toLowerCase();
276
+ const index = dexs.findIndex((entry) => entry?.name?.toLowerCase() === target);
277
+ if (index === -1) {
278
+ throw new Error(`Unknown Hyperliquid perp dex: ${args.dex}`);
279
+ }
280
+ return index;
281
+ }
282
+ function buildSpotTokenIndexMap(tokens) {
283
+ const map = /* @__PURE__ */ new Map();
284
+ for (const token of tokens) {
285
+ const name = normalizeSpotTokenName(token?.name);
286
+ const index = typeof token?.index === "number" && Number.isFinite(token.index) ? token.index : null;
287
+ if (!name || index == null) continue;
288
+ if (!map.has(name) || token?.isCanonical) {
289
+ map.set(name, index);
290
+ }
291
+ }
292
+ return map;
293
+ }
294
+ function resolveSpotTokenIndex(tokenMap, value) {
295
+ const normalized = normalizeSpotTokenName(value);
296
+ if (!normalized) return null;
297
+ const direct = tokenMap.get(normalized);
298
+ if (direct != null) return direct;
299
+ if (!normalized.startsWith("U")) {
300
+ const prefixed = tokenMap.get(`U${normalized}`);
301
+ if (prefixed != null) return prefixed;
302
+ }
303
+ return null;
304
+ }
305
+ function resolveSpotMarketIndex(args) {
306
+ for (let i = 0; i < args.universe.length; i += 1) {
307
+ const entry = args.universe[i];
308
+ const tokens = Array.isArray(entry?.tokens) ? entry.tokens : null;
309
+ const baseToken = tokens?.[0] ?? entry?.baseToken ?? null;
310
+ const quoteToken = tokens?.[1] ?? entry?.quoteToken ?? null;
311
+ if (baseToken === args.baseToken && quoteToken === args.quoteToken) {
312
+ if (typeof entry?.index === "number" && Number.isFinite(entry.index)) {
313
+ return entry.index;
314
+ }
315
+ return i;
316
+ }
317
+ }
318
+ return null;
319
+ }
320
+ async function resolveHyperliquidAssetIndex(args) {
321
+ const trimmed = args.symbol.trim();
322
+ if (!trimmed) {
323
+ throw new Error("Hyperliquid symbol must be a non-empty string.");
324
+ }
325
+ if (trimmed.startsWith("@")) {
326
+ const rawIndex = trimmed.slice(1).trim();
327
+ const index = Number(rawIndex);
328
+ if (!Number.isFinite(index)) {
329
+ throw new Error(`Hyperliquid spot market index is invalid: ${trimmed}`);
330
+ }
331
+ return 1e4 + index;
332
+ }
333
+ const separator = trimmed.indexOf(":");
334
+ if (separator > 0) {
335
+ const dex = trimmed.slice(0, separator).trim();
336
+ if (!dex) {
337
+ throw new Error("Hyperliquid dex name is required.");
338
+ }
339
+ const dexIndex = await resolveDexIndex({
340
+ baseUrl: args.baseUrl,
341
+ environment: args.environment,
342
+ fetcher: args.fetcher,
343
+ dex
344
+ });
345
+ const universe2 = await getUniverse({
346
+ baseUrl: args.baseUrl,
347
+ environment: args.environment,
348
+ fetcher: args.fetcher,
349
+ dex
350
+ });
351
+ const assetIndex = universe2.findIndex(
352
+ (entry) => entry.name.toUpperCase() === trimmed.toUpperCase()
353
+ );
354
+ if (assetIndex === -1) {
355
+ throw new Error(`Unknown Hyperliquid asset symbol: ${trimmed}`);
356
+ }
357
+ return 1e5 + dexIndex * 1e4 + assetIndex;
358
+ }
359
+ const pair = parseHyperliquidPair(trimmed);
360
+ if (pair) {
361
+ const { universe: universe2, tokens } = await getSpotMeta({
362
+ baseUrl: args.baseUrl,
363
+ environment: args.environment,
364
+ fetcher: args.fetcher
365
+ });
366
+ const tokenMap = buildSpotTokenIndexMap(tokens);
367
+ const baseToken = resolveSpotTokenIndex(tokenMap, pair.base);
368
+ const quoteToken = resolveSpotTokenIndex(tokenMap, pair.quote);
369
+ if (baseToken == null || quoteToken == null) {
370
+ throw new Error(`Unknown Hyperliquid spot symbol: ${trimmed}`);
371
+ }
372
+ const marketIndex = resolveSpotMarketIndex({
373
+ universe: universe2,
374
+ baseToken,
375
+ quoteToken
376
+ });
377
+ if (marketIndex == null) {
378
+ throw new Error(`Unknown Hyperliquid spot symbol: ${trimmed}`);
379
+ }
380
+ return 1e4 + marketIndex;
381
+ }
382
+ const universe = await getUniverse({
383
+ baseUrl: args.baseUrl,
384
+ environment: args.environment,
385
+ fetcher: args.fetcher
386
+ });
387
+ return resolveAssetIndex(trimmed, universe);
388
+ }
389
+ function toApiDecimal(value) {
390
+ if (typeof value === "string") {
391
+ return value;
392
+ }
393
+ if (typeof value === "bigint") {
394
+ return value.toString();
395
+ }
396
+ if (!Number.isFinite(value)) {
397
+ throw new Error("Numeric values must be finite.");
398
+ }
399
+ const asString = value.toString();
400
+ if (/e/i.test(asString)) {
401
+ const [mantissa, exponentPart] = asString.split(/e/i);
402
+ const exponent = Number(exponentPart);
403
+ const [integerPart, fractionalPart = ""] = mantissa.split(".");
404
+ if (exponent >= 0) {
405
+ return integerPart + fractionalPart.padEnd(exponent + fractionalPart.length, "0");
406
+ }
407
+ const zeros = "0".repeat(Math.abs(exponent) - 1);
408
+ return `0.${zeros}${integerPart}${fractionalPart}`.replace(/\.0+$/, "");
409
+ }
410
+ return asString;
411
+ }
412
+ var NORMALIZED_HEX_PATTERN = /^0x[0-9a-f]+$/;
413
+ var ADDRESS_HEX_LENGTH = 42;
414
+ var CLOID_HEX_LENGTH = 34;
415
+ function normalizeHex(value) {
416
+ const lower = value.trim().toLowerCase();
417
+ if (!NORMALIZED_HEX_PATTERN.test(lower)) {
418
+ throw new Error(`Invalid hex value: ${value}`);
419
+ }
420
+ return lower;
421
+ }
422
+ function normalizeAddress(value) {
423
+ const normalized = normalizeHex(value);
424
+ if (normalized.length !== ADDRESS_HEX_LENGTH) {
425
+ throw new Error(`Invalid address length: ${normalized}`);
426
+ }
427
+ return normalized;
428
+ }
429
+ function normalizeCloid(value) {
430
+ const normalized = normalizeHex(value);
431
+ if (normalized.length !== CLOID_HEX_LENGTH) {
432
+ throw new Error(`Invalid cloid length: ${normalized}`);
433
+ }
434
+ return normalized;
435
+ }
436
+ async function signL1Action(args) {
437
+ const { wallet, action, nonce, vaultAddress, expiresAfter, isTestnet } = args;
438
+ const actionHash = createL1ActionHash({
439
+ action,
440
+ nonce,
441
+ vaultAddress,
442
+ expiresAfter
443
+ });
444
+ const message = {
445
+ source: isTestnet ? "b" : "a",
446
+ connectionId: actionHash
447
+ };
448
+ const signatureHex = await wallet.walletClient.signTypedData({
449
+ account: wallet.account,
450
+ domain: EXCHANGE_TYPED_DATA_DOMAIN,
451
+ types: {
452
+ Agent: [
453
+ { name: "source", type: "string" },
454
+ { name: "connectionId", type: "bytes32" }
455
+ ]
456
+ },
457
+ primaryType: "Agent",
458
+ message
459
+ });
460
+ return splitSignature(signatureHex);
461
+ }
462
+ async function signSpotSend(args) {
463
+ const { wallet, hyperliquidChain, signatureChainId, destination, token, amount, time } = args;
464
+ const domain = {
465
+ name: "HyperliquidSignTransaction",
466
+ version: "1",
467
+ chainId: Number.parseInt(signatureChainId, 16),
468
+ verifyingContract: ZERO_ADDRESS
469
+ };
470
+ const message = {
471
+ hyperliquidChain,
472
+ destination,
473
+ token,
474
+ amount,
475
+ time
476
+ };
477
+ const types = {
478
+ "HyperliquidTransaction:SpotSend": [
479
+ { name: "hyperliquidChain", type: "string" },
480
+ { name: "destination", type: "string" },
481
+ { name: "token", type: "string" },
482
+ { name: "amount", type: "string" },
483
+ { name: "time", type: "uint64" }
484
+ ]
485
+ };
486
+ const signatureHex = await wallet.walletClient.signTypedData({
487
+ account: wallet.account,
488
+ domain,
489
+ types,
490
+ primaryType: "HyperliquidTransaction:SpotSend",
491
+ message
492
+ });
493
+ return splitSignature(signatureHex);
494
+ }
495
+ async function signApproveBuilderFee(args) {
496
+ const { wallet, maxFeeRate, nonce, signatureChainId, isTestnet } = args;
497
+ const hyperliquidChain = isTestnet ? "Testnet" : "Mainnet";
498
+ const domain = {
499
+ name: "HyperliquidSignTransaction",
500
+ version: "1",
501
+ chainId: Number.parseInt(signatureChainId, 16),
502
+ verifyingContract: ZERO_ADDRESS
503
+ };
504
+ const message = {
505
+ hyperliquidChain,
506
+ maxFeeRate,
507
+ builder: BUILDER_CODE.address,
508
+ nonce
509
+ };
510
+ const types = {
511
+ "HyperliquidTransaction:ApproveBuilderFee": [
512
+ { name: "hyperliquidChain", type: "string" },
513
+ { name: "maxFeeRate", type: "string" },
514
+ { name: "builder", type: "address" },
515
+ { name: "nonce", type: "uint64" }
516
+ ]
517
+ };
518
+ const signatureHex = await wallet.walletClient.signTypedData({
519
+ account: wallet.account,
520
+ domain,
521
+ types,
522
+ primaryType: "HyperliquidTransaction:ApproveBuilderFee",
523
+ message
524
+ });
525
+ return splitSignature(signatureHex);
526
+ }
527
+ async function signUserPortfolioMargin(args) {
528
+ const { wallet, action } = args;
529
+ const domain = {
530
+ name: "HyperliquidSignTransaction",
531
+ version: "1",
532
+ chainId: Number.parseInt(action.signatureChainId, 16),
533
+ verifyingContract: ZERO_ADDRESS
534
+ };
535
+ const message = {
536
+ enabled: action.enabled,
537
+ hyperliquidChain: action.hyperliquidChain,
538
+ user: action.user,
539
+ nonce: BigInt(action.nonce)
540
+ };
541
+ const types = {
542
+ "HyperliquidTransaction:UserPortfolioMargin": [
543
+ { name: "enabled", type: "bool" },
544
+ { name: "hyperliquidChain", type: "string" },
545
+ { name: "user", type: "address" },
546
+ { name: "nonce", type: "uint64" }
547
+ ]
548
+ };
549
+ const signatureHex = await wallet.walletClient.signTypedData({
550
+ account: wallet.account,
551
+ domain,
552
+ types,
553
+ primaryType: "HyperliquidTransaction:UserPortfolioMargin",
554
+ message
555
+ });
556
+ return splitSignature(signatureHex);
557
+ }
558
+ async function signUserDexAbstraction(args) {
559
+ const { wallet, action } = args;
560
+ const domain = {
561
+ name: "HyperliquidSignTransaction",
562
+ version: "1",
563
+ chainId: Number.parseInt(action.signatureChainId, 16),
564
+ verifyingContract: ZERO_ADDRESS
565
+ };
566
+ const message = {
567
+ hyperliquidChain: action.hyperliquidChain,
568
+ user: action.user,
569
+ enabled: action.enabled,
570
+ nonce: BigInt(action.nonce)
571
+ };
572
+ const types = {
573
+ "HyperliquidTransaction:UserDexAbstraction": [
574
+ { name: "hyperliquidChain", type: "string" },
575
+ { name: "user", type: "address" },
576
+ { name: "enabled", type: "bool" },
577
+ { name: "nonce", type: "uint64" }
578
+ ]
579
+ };
580
+ const signatureHex = await wallet.walletClient.signTypedData({
581
+ account: wallet.account,
582
+ domain,
583
+ types,
584
+ primaryType: "HyperliquidTransaction:UserDexAbstraction",
585
+ message
586
+ });
587
+ return splitSignature(signatureHex);
588
+ }
589
+ async function signUserSetAbstraction(args) {
590
+ const { wallet, action } = args;
591
+ const domain = {
592
+ name: "HyperliquidSignTransaction",
593
+ version: "1",
594
+ chainId: Number.parseInt(action.signatureChainId, 16),
595
+ verifyingContract: ZERO_ADDRESS
596
+ };
597
+ const message = {
598
+ hyperliquidChain: action.hyperliquidChain,
599
+ user: action.user,
600
+ abstraction: action.abstraction,
601
+ nonce: BigInt(action.nonce)
602
+ };
603
+ const types = {
604
+ "HyperliquidTransaction:UserSetAbstraction": [
605
+ { name: "hyperliquidChain", type: "string" },
606
+ { name: "user", type: "address" },
607
+ { name: "abstraction", type: "string" },
608
+ { name: "nonce", type: "uint64" }
609
+ ]
610
+ };
611
+ const signatureHex = await wallet.walletClient.signTypedData({
612
+ account: wallet.account,
613
+ domain,
614
+ types,
615
+ primaryType: "HyperliquidTransaction:UserSetAbstraction",
616
+ message
617
+ });
618
+ return splitSignature(signatureHex);
619
+ }
620
+ function splitSignature(signature) {
621
+ const cleaned = signature.slice(2);
622
+ const rHex = `0x${cleaned.slice(0, 64)}`;
623
+ const sHex = `0x${cleaned.slice(64, 128)}`;
624
+ let v = parseInt(cleaned.slice(128, 130), 16);
625
+ if (Number.isNaN(v)) {
626
+ throw new Error("Invalid signature returned by wallet client.");
627
+ }
628
+ if (v < 27) {
629
+ v += 27;
630
+ }
631
+ const normalizedV = v === 27 || v === 28 ? v : v % 2 ? 27 : 28;
632
+ return {
633
+ r: normalizeHex(rHex),
634
+ s: normalizeHex(sHex),
635
+ v: normalizedV
636
+ };
637
+ }
638
+ function createL1ActionHash(args) {
639
+ const { action, nonce, vaultAddress, expiresAfter } = args;
640
+ const actionBytes = encode(action, { ignoreUndefined: true });
641
+ const nonceBytes = toUint64Bytes(nonce);
642
+ const vaultMarker = vaultAddress ? new Uint8Array([1]) : new Uint8Array([0]);
643
+ const vaultBytes = vaultAddress ? hexToBytes(vaultAddress.slice(2)) : new Uint8Array();
644
+ const hasExpiresAfter = typeof expiresAfter === "number";
645
+ const expiresMarker = hasExpiresAfter ? new Uint8Array([0]) : new Uint8Array();
646
+ const expiresBytes = hasExpiresAfter && expiresAfter !== void 0 ? toUint64Bytes(expiresAfter) : new Uint8Array();
647
+ const bytes = concatBytes(
648
+ actionBytes,
649
+ nonceBytes,
650
+ vaultMarker,
651
+ vaultBytes,
652
+ expiresMarker,
653
+ expiresBytes
654
+ );
655
+ const hash = keccak_256(bytes);
656
+ return `0x${bytesToHex(hash)}`;
657
+ }
658
+ function toUint64Bytes(value) {
659
+ const bytes = new Uint8Array(8);
660
+ new DataView(bytes.buffer).setBigUint64(0, BigInt(value));
661
+ return bytes;
662
+ }
663
+ function getBridgeAddress(env) {
664
+ const override = process.env.HYPERLIQUID_BRIDGE_ADDRESS;
665
+ if (override?.trim()) {
666
+ return normalizeAddress(override);
667
+ }
668
+ return HL_BRIDGE_ADDRESSES[env];
669
+ }
670
+ function getUsdcAddress(env) {
671
+ const override = process.env.HYPERLIQUID_USDC_ADDRESS;
672
+ if (override?.trim()) {
673
+ return normalizeAddress(override);
674
+ }
675
+ return HL_USDC_ADDRESSES[env];
676
+ }
677
+ function getSignatureChainId(env) {
678
+ const override = process.env.HYPERLIQUID_SIGNATURE_CHAIN_ID;
679
+ const selected = override?.trim() || HL_SIGNATURE_CHAIN_ID[env];
680
+ return normalizeHex(selected);
681
+ }
682
+ function assertPositiveNumber(value, label) {
683
+ if (!Number.isFinite(value) || value <= 0) {
684
+ throw new Error(`${label} must be a positive number.`);
685
+ }
686
+ }
687
+
688
+ // src/adapters/hyperliquid/info.ts
689
+ async function postInfo(environment, payload) {
690
+ const baseUrl = API_BASES[environment];
691
+ const response = await fetch(`${baseUrl}/info`, {
692
+ method: "POST",
693
+ headers: { "content-type": "application/json" },
694
+ body: JSON.stringify(payload)
695
+ });
696
+ const data = await response.json().catch(() => null);
697
+ if (!response.ok) {
698
+ throw new HyperliquidApiError(
699
+ "Hyperliquid info request failed.",
700
+ data ?? { status: response.status }
701
+ );
702
+ }
703
+ return data;
704
+ }
705
+ var HyperliquidInfoClient = class {
706
+ constructor(environment = "mainnet") {
707
+ this.environment = environment;
708
+ }
709
+ meta() {
710
+ return fetchHyperliquidMeta(this.environment);
711
+ }
712
+ metaAndAssetCtxs() {
713
+ return fetchHyperliquidMetaAndAssetCtxs(this.environment);
714
+ }
715
+ spotMeta() {
716
+ return fetchHyperliquidSpotMeta(this.environment);
717
+ }
718
+ spotMetaAndAssetCtxs() {
719
+ return fetchHyperliquidSpotMetaAndAssetCtxs(this.environment);
720
+ }
721
+ assetCtxs() {
722
+ return fetchHyperliquidAssetCtxs(this.environment);
723
+ }
724
+ spotAssetCtxs() {
725
+ return fetchHyperliquidSpotAssetCtxs(this.environment);
726
+ }
727
+ openOrders(user) {
728
+ return fetchHyperliquidOpenOrders({ user, environment: this.environment });
729
+ }
730
+ frontendOpenOrders(user) {
731
+ return fetchHyperliquidFrontendOpenOrders({
732
+ user,
733
+ environment: this.environment
734
+ });
735
+ }
736
+ orderStatus(user, oid) {
737
+ return fetchHyperliquidOrderStatus({
738
+ user,
739
+ oid,
740
+ environment: this.environment
741
+ });
742
+ }
743
+ historicalOrders(user) {
744
+ return fetchHyperliquidHistoricalOrders({
745
+ user,
746
+ environment: this.environment
747
+ });
748
+ }
749
+ userFills(user) {
750
+ return fetchHyperliquidUserFills({ user, environment: this.environment });
751
+ }
752
+ userFillsByTime(user, startTime, endTime) {
753
+ return fetchHyperliquidUserFillsByTime({
754
+ user,
755
+ startTime,
756
+ endTime,
757
+ environment: this.environment
758
+ });
759
+ }
760
+ userRateLimit(user) {
761
+ return fetchHyperliquidUserRateLimit({
762
+ user,
763
+ environment: this.environment
764
+ });
765
+ }
766
+ preTransferCheck(user, source) {
767
+ return fetchHyperliquidPreTransferCheck({
768
+ user,
769
+ source,
770
+ environment: this.environment
771
+ });
772
+ }
773
+ spotClearinghouseState(user) {
774
+ return fetchHyperliquidSpotClearinghouseState({
775
+ user,
776
+ environment: this.environment
777
+ });
778
+ }
779
+ };
780
+ async function fetchHyperliquidMeta(environment = "mainnet") {
781
+ return postInfo(environment, { type: "meta" });
782
+ }
783
+ async function fetchHyperliquidMetaAndAssetCtxs(environment = "mainnet") {
784
+ return postInfo(environment, { type: "metaAndAssetCtxs" });
785
+ }
786
+ async function fetchHyperliquidSpotMeta(environment = "mainnet") {
787
+ return postInfo(environment, { type: "spotMeta" });
788
+ }
789
+ async function fetchHyperliquidSpotMetaAndAssetCtxs(environment = "mainnet") {
790
+ return postInfo(environment, { type: "spotMetaAndAssetCtxs" });
791
+ }
792
+ async function fetchHyperliquidAssetCtxs(environment = "mainnet") {
793
+ return postInfo(environment, { type: "assetCtxs" });
794
+ }
795
+ async function fetchHyperliquidSpotAssetCtxs(environment = "mainnet") {
796
+ return postInfo(environment, { type: "spotAssetCtxs" });
797
+ }
798
+ async function fetchHyperliquidOpenOrders(params) {
799
+ const env = params.environment ?? "mainnet";
800
+ return postInfo(env, { type: "openOrders", user: normalizeAddress(params.user) });
801
+ }
802
+ async function fetchHyperliquidFrontendOpenOrders(params) {
803
+ const env = params.environment ?? "mainnet";
804
+ return postInfo(env, {
805
+ type: "frontendOpenOrders",
806
+ user: normalizeAddress(params.user)
807
+ });
808
+ }
809
+ async function fetchHyperliquidOrderStatus(params) {
810
+ const env = params.environment ?? "mainnet";
811
+ return postInfo(env, {
812
+ type: "orderStatus",
813
+ user: normalizeAddress(params.user),
814
+ oid: params.oid
815
+ });
816
+ }
817
+ async function fetchHyperliquidHistoricalOrders(params) {
818
+ const env = params.environment ?? "mainnet";
819
+ return postInfo(env, {
820
+ type: "historicalOrders",
821
+ user: normalizeAddress(params.user)
822
+ });
823
+ }
824
+ async function fetchHyperliquidUserFills(params) {
825
+ const env = params.environment ?? "mainnet";
826
+ return postInfo(env, {
827
+ type: "userFills",
828
+ user: normalizeAddress(params.user)
829
+ });
830
+ }
831
+ async function fetchHyperliquidUserFillsByTime(params) {
832
+ const env = params.environment ?? "mainnet";
833
+ return postInfo(env, {
834
+ type: "userFillsByTime",
835
+ user: normalizeAddress(params.user),
836
+ startTime: params.startTime,
837
+ endTime: params.endTime
838
+ });
839
+ }
840
+ async function fetchHyperliquidUserRateLimit(params) {
841
+ const env = params.environment ?? "mainnet";
842
+ return postInfo(env, {
843
+ type: "userRateLimit",
844
+ user: normalizeAddress(params.user)
845
+ });
846
+ }
847
+ async function fetchHyperliquidPreTransferCheck(params) {
848
+ const env = params.environment ?? "mainnet";
849
+ return postInfo(env, {
850
+ type: "preTransferCheck",
851
+ user: normalizeAddress(params.user),
852
+ source: normalizeAddress(params.source)
853
+ });
854
+ }
855
+ async function fetchHyperliquidSpotClearinghouseState(params) {
856
+ const env = params.environment ?? "mainnet";
857
+ return postInfo(env, {
858
+ type: "spotClearinghouseState",
859
+ user: normalizeAddress(params.user)
860
+ });
861
+ }
862
+
863
+ // src/adapters/hyperliquid/exchange.ts
864
+ function resolveRequiredExchangeNonce(options) {
865
+ if (typeof options.nonce === "number") {
866
+ return options.nonce;
867
+ }
868
+ const resolved = options.walletNonceProvider?.() ?? options.wallet.nonceSource?.() ?? options.nonceSource?.();
869
+ if (resolved === void 0) {
870
+ throw new Error(`${options.action} requires an explicit nonce or wallet nonce source.`);
871
+ }
872
+ return resolved;
873
+ }
874
+ var HyperliquidExchangeClient = class {
875
+ constructor(args) {
876
+ this.wallet = args.wallet;
877
+ this.environment = args.environment ?? "mainnet";
878
+ this.vaultAddress = args.vaultAddress;
879
+ this.expiresAfter = args.expiresAfter;
880
+ const resolvedNonceSource = args.walletNonceProvider ?? args.wallet.nonceSource ?? args.nonceSource;
881
+ if (!resolvedNonceSource) {
882
+ throw new Error("Wallet nonce source is required for Hyperliquid exchange actions.");
883
+ }
884
+ this.nonceSource = resolvedNonceSource;
885
+ }
886
+ cancel(cancels) {
887
+ return cancelHyperliquidOrders({
888
+ wallet: this.wallet,
889
+ cancels,
890
+ environment: this.environment,
891
+ vaultAddress: this.vaultAddress,
892
+ expiresAfter: this.expiresAfter,
893
+ nonceSource: this.nonceSource
894
+ });
895
+ }
896
+ cancelByCloid(cancels) {
897
+ return cancelHyperliquidOrdersByCloid({
898
+ wallet: this.wallet,
899
+ cancels,
900
+ environment: this.environment,
901
+ vaultAddress: this.vaultAddress,
902
+ expiresAfter: this.expiresAfter,
903
+ nonceSource: this.nonceSource
904
+ });
905
+ }
906
+ cancelAll() {
907
+ return cancelAllHyperliquidOrders({
908
+ wallet: this.wallet,
909
+ environment: this.environment,
910
+ vaultAddress: this.vaultAddress,
911
+ expiresAfter: this.expiresAfter,
912
+ nonceSource: this.nonceSource
913
+ });
914
+ }
915
+ scheduleCancel(time) {
916
+ return scheduleHyperliquidCancel({
917
+ wallet: this.wallet,
918
+ time,
919
+ environment: this.environment,
920
+ vaultAddress: this.vaultAddress,
921
+ expiresAfter: this.expiresAfter,
922
+ nonceSource: this.nonceSource
923
+ });
924
+ }
925
+ modify(modification) {
926
+ return modifyHyperliquidOrder({
927
+ wallet: this.wallet,
928
+ modification,
929
+ environment: this.environment,
930
+ vaultAddress: this.vaultAddress,
931
+ expiresAfter: this.expiresAfter,
932
+ nonceSource: this.nonceSource
933
+ });
934
+ }
935
+ batchModify(modifications) {
936
+ return batchModifyHyperliquidOrders({
937
+ wallet: this.wallet,
938
+ modifications,
939
+ environment: this.environment,
940
+ vaultAddress: this.vaultAddress,
941
+ expiresAfter: this.expiresAfter,
942
+ nonceSource: this.nonceSource
943
+ });
944
+ }
945
+ twapOrder(twap) {
946
+ return placeHyperliquidTwapOrder({
947
+ wallet: this.wallet,
948
+ twap,
949
+ environment: this.environment,
950
+ vaultAddress: this.vaultAddress,
951
+ expiresAfter: this.expiresAfter,
952
+ nonceSource: this.nonceSource
953
+ });
954
+ }
955
+ twapCancel(cancel) {
956
+ return cancelHyperliquidTwapOrder({
957
+ wallet: this.wallet,
958
+ cancel,
959
+ environment: this.environment,
960
+ vaultAddress: this.vaultAddress,
961
+ expiresAfter: this.expiresAfter,
962
+ nonceSource: this.nonceSource
963
+ });
964
+ }
965
+ updateLeverage(input) {
966
+ return updateHyperliquidLeverage({
967
+ wallet: this.wallet,
968
+ input,
969
+ environment: this.environment,
970
+ vaultAddress: this.vaultAddress,
971
+ expiresAfter: this.expiresAfter,
972
+ nonceSource: this.nonceSource
973
+ });
974
+ }
975
+ updateIsolatedMargin(input) {
976
+ return updateHyperliquidIsolatedMargin({
977
+ wallet: this.wallet,
978
+ input,
979
+ environment: this.environment,
980
+ vaultAddress: this.vaultAddress,
981
+ expiresAfter: this.expiresAfter,
982
+ nonceSource: this.nonceSource
983
+ });
984
+ }
985
+ reserveRequestWeight(weight) {
986
+ return reserveHyperliquidRequestWeight({
987
+ wallet: this.wallet,
988
+ weight,
989
+ environment: this.environment,
990
+ vaultAddress: this.vaultAddress,
991
+ expiresAfter: this.expiresAfter,
992
+ nonceSource: this.nonceSource
993
+ });
994
+ }
995
+ spotSend(params) {
996
+ return sendHyperliquidSpot({
997
+ wallet: this.wallet,
998
+ environment: this.environment,
999
+ nonceSource: this.nonceSource,
1000
+ ...params
1001
+ });
1002
+ }
1003
+ setDexAbstraction(params) {
1004
+ const base = {
1005
+ wallet: this.wallet,
1006
+ enabled: params.enabled,
1007
+ environment: this.environment,
1008
+ vaultAddress: this.vaultAddress,
1009
+ expiresAfter: this.expiresAfter,
1010
+ nonceSource: this.nonceSource
1011
+ };
1012
+ return setHyperliquidDexAbstraction(params.user ? { ...base, user: params.user } : base);
1013
+ }
1014
+ setAccountAbstractionMode(params) {
1015
+ const base = {
1016
+ wallet: this.wallet,
1017
+ mode: params.mode,
1018
+ environment: this.environment,
1019
+ vaultAddress: this.vaultAddress,
1020
+ expiresAfter: this.expiresAfter,
1021
+ nonceSource: this.nonceSource
1022
+ };
1023
+ return setHyperliquidAccountAbstractionMode(
1024
+ params.user ? { ...base, user: params.user } : base
1025
+ );
1026
+ }
1027
+ setPortfolioMargin(params) {
1028
+ const base = {
1029
+ wallet: this.wallet,
1030
+ enabled: params.enabled,
1031
+ environment: this.environment,
1032
+ vaultAddress: this.vaultAddress,
1033
+ expiresAfter: this.expiresAfter,
1034
+ nonceSource: this.nonceSource
1035
+ };
1036
+ return setHyperliquidPortfolioMargin(params.user ? { ...base, user: params.user } : base);
1037
+ }
1038
+ };
1039
+ async function setHyperliquidPortfolioMargin(options) {
1040
+ const env = options.environment ?? "mainnet";
1041
+ if (!options.wallet?.account || !options.wallet.walletClient) {
1042
+ throw new Error("Wallet with signing capability is required for portfolio margin.");
1043
+ }
1044
+ const nonce = resolveRequiredExchangeNonce({
1045
+ nonce: options.nonce,
1046
+ nonceSource: options.nonceSource,
1047
+ walletNonceProvider: options.walletNonceProvider,
1048
+ wallet: options.wallet,
1049
+ action: "Hyperliquid portfolio margin"
1050
+ });
1051
+ const signatureChainId = getSignatureChainId(env);
1052
+ const hyperliquidChain = HL_CHAIN_LABEL[env];
1053
+ const user = normalizeAddress(options.user ?? options.wallet.address);
1054
+ const action = {
1055
+ type: "userPortfolioMargin",
1056
+ enabled: Boolean(options.enabled),
1057
+ hyperliquidChain,
1058
+ signatureChainId,
1059
+ user,
1060
+ nonce
1061
+ };
1062
+ const signature = await signUserPortfolioMargin({
1063
+ wallet: options.wallet,
1064
+ action
1065
+ });
1066
+ const body = {
1067
+ action,
1068
+ nonce,
1069
+ signature
1070
+ };
1071
+ if (options.vaultAddress) {
1072
+ body.vaultAddress = normalizeAddress(options.vaultAddress);
1073
+ }
1074
+ if (typeof options.expiresAfter === "number") {
1075
+ body.expiresAfter = options.expiresAfter;
1076
+ }
1077
+ return postExchange(env, body);
1078
+ }
1079
+ async function setHyperliquidDexAbstraction(options) {
1080
+ const env = options.environment ?? "mainnet";
1081
+ if (!options.wallet?.account || !options.wallet.walletClient) {
1082
+ throw new Error("Wallet with signing capability is required for dex abstraction.");
1083
+ }
1084
+ const nonce = resolveRequiredExchangeNonce({
1085
+ nonce: options.nonce,
1086
+ nonceSource: options.nonceSource,
1087
+ walletNonceProvider: options.walletNonceProvider,
1088
+ wallet: options.wallet,
1089
+ action: "Hyperliquid dex abstraction"
1090
+ });
1091
+ const signatureChainId = getSignatureChainId(env);
1092
+ const hyperliquidChain = HL_CHAIN_LABEL[env];
1093
+ const user = normalizeAddress(options.user ?? options.wallet.address);
1094
+ const action = {
1095
+ type: "userDexAbstraction",
1096
+ enabled: Boolean(options.enabled),
1097
+ hyperliquidChain,
1098
+ signatureChainId,
1099
+ user,
1100
+ nonce
1101
+ };
1102
+ const signature = await signUserDexAbstraction({
1103
+ wallet: options.wallet,
1104
+ action
1105
+ });
1106
+ const body = {
1107
+ action,
1108
+ nonce,
1109
+ signature
1110
+ };
1111
+ if (options.vaultAddress) {
1112
+ body.vaultAddress = normalizeAddress(options.vaultAddress);
1113
+ }
1114
+ if (typeof options.expiresAfter === "number") {
1115
+ body.expiresAfter = options.expiresAfter;
1116
+ }
1117
+ return postExchange(env, body);
1118
+ }
1119
+ async function setHyperliquidAccountAbstractionMode(options) {
1120
+ const env = options.environment ?? "mainnet";
1121
+ if (!options.wallet?.account || !options.wallet.walletClient) {
1122
+ throw new Error("Wallet with signing capability is required for account abstraction mode.");
1123
+ }
1124
+ const nonce = resolveRequiredExchangeNonce({
1125
+ nonce: options.nonce,
1126
+ nonceSource: options.nonceSource,
1127
+ walletNonceProvider: options.walletNonceProvider,
1128
+ wallet: options.wallet,
1129
+ action: "Hyperliquid account abstraction mode"
1130
+ });
1131
+ const signatureChainId = getSignatureChainId(env);
1132
+ const hyperliquidChain = HL_CHAIN_LABEL[env];
1133
+ const user = normalizeAddress(options.user ?? options.wallet.address);
1134
+ const abstraction = resolveHyperliquidAbstractionFromMode(options.mode);
1135
+ const action = {
1136
+ type: "userSetAbstraction",
1137
+ abstraction,
1138
+ hyperliquidChain,
1139
+ signatureChainId,
1140
+ user,
1141
+ nonce
1142
+ };
1143
+ const signature = await signUserSetAbstraction({
1144
+ wallet: options.wallet,
1145
+ action
1146
+ });
1147
+ const body = {
1148
+ action,
1149
+ nonce,
1150
+ signature
1151
+ };
1152
+ if (options.vaultAddress) {
1153
+ body.vaultAddress = normalizeAddress(options.vaultAddress);
1154
+ }
1155
+ if (typeof options.expiresAfter === "number") {
1156
+ body.expiresAfter = options.expiresAfter;
1157
+ }
1158
+ return postExchange(env, body);
1159
+ }
1160
+ async function cancelHyperliquidOrders(options) {
1161
+ options.cancels.forEach((c) => assertSymbol(c.symbol));
1162
+ const action = {
1163
+ type: "cancel",
1164
+ cancels: await withAssetIndexes(options, options.cancels, (idx, entry) => ({
1165
+ a: idx,
1166
+ o: entry.oid
1167
+ }))
1168
+ };
1169
+ return submitExchangeAction(options, action);
1170
+ }
1171
+ async function cancelHyperliquidOrdersByCloid(options) {
1172
+ options.cancels.forEach((c) => assertSymbol(c.symbol));
1173
+ const action = {
1174
+ type: "cancelByCloid",
1175
+ cancels: await withAssetIndexes(options, options.cancels, (idx, entry) => ({
1176
+ asset: idx,
1177
+ cloid: normalizeCloid(entry.cloid)
1178
+ }))
1179
+ };
1180
+ return submitExchangeAction(options, action);
1181
+ }
1182
+ async function cancelAllHyperliquidOrders(options) {
1183
+ const action = { type: "cancelAll" };
1184
+ return submitExchangeAction(options, action);
1185
+ }
1186
+ async function scheduleHyperliquidCancel(options) {
1187
+ if (options.time != null) {
1188
+ assertPositiveNumber(options.time, "time");
1189
+ }
1190
+ const action = options.time == null ? { type: "scheduleCancel" } : { type: "scheduleCancel", time: options.time };
1191
+ return submitExchangeAction(options, action);
1192
+ }
1193
+ async function modifyHyperliquidOrder(options) {
1194
+ const { modification } = options;
1195
+ const order = await buildOrder(modification.order, options);
1196
+ const action = {
1197
+ type: "modify",
1198
+ oid: modification.oid,
1199
+ order
1200
+ };
1201
+ return submitExchangeAction(options, action);
1202
+ }
1203
+ async function batchModifyHyperliquidOrders(options) {
1204
+ options.modifications.forEach((m) => assertSymbol(m.order.symbol));
1205
+ const modifies = await Promise.all(
1206
+ options.modifications.map(async (mod) => ({
1207
+ oid: mod.oid,
1208
+ order: await buildOrder(mod.order, options)
1209
+ }))
1210
+ );
1211
+ const action = {
1212
+ type: "batchModify",
1213
+ modifies
1214
+ };
1215
+ return submitExchangeAction(options, action);
1216
+ }
1217
+ async function placeHyperliquidTwapOrder(options) {
1218
+ const { twap } = options;
1219
+ assertSymbol(twap.symbol);
1220
+ assertPositiveDecimal(twap.size, "size");
1221
+ assertPositiveNumber(twap.minutes, "minutes");
1222
+ const env = options.environment ?? "mainnet";
1223
+ const asset = await resolveHyperliquidAssetIndex({
1224
+ symbol: twap.symbol,
1225
+ baseUrl: API_BASES[env],
1226
+ environment: env,
1227
+ fetcher: fetch
1228
+ });
1229
+ const action = {
1230
+ type: "twapOrder",
1231
+ twap: {
1232
+ a: asset,
1233
+ b: twap.side === "buy",
1234
+ s: toApiDecimal(twap.size),
1235
+ r: Boolean(twap.reduceOnly),
1236
+ m: twap.minutes,
1237
+ t: Boolean(twap.randomize)
1238
+ }
1239
+ };
1240
+ return submitExchangeAction(options, action);
1241
+ }
1242
+ async function cancelHyperliquidTwapOrder(options) {
1243
+ assertSymbol(options.cancel.symbol);
1244
+ const env = options.environment ?? "mainnet";
1245
+ const asset = await resolveHyperliquidAssetIndex({
1246
+ symbol: options.cancel.symbol,
1247
+ baseUrl: API_BASES[env],
1248
+ environment: env,
1249
+ fetcher: fetch
1250
+ });
1251
+ const action = {
1252
+ type: "twapCancel",
1253
+ a: asset,
1254
+ t: options.cancel.twapId
1255
+ };
1256
+ return submitExchangeAction(options, action);
1257
+ }
1258
+ async function updateHyperliquidLeverage(options) {
1259
+ assertSymbol(options.input.symbol);
1260
+ assertPositiveNumber(options.input.leverage, "leverage");
1261
+ const env = options.environment ?? "mainnet";
1262
+ const asset = await resolveHyperliquidAssetIndex({
1263
+ symbol: options.input.symbol,
1264
+ baseUrl: API_BASES[env],
1265
+ environment: env,
1266
+ fetcher: fetch
1267
+ });
1268
+ const action = {
1269
+ type: "updateLeverage",
1270
+ asset,
1271
+ isCross: options.input.leverageMode === "cross",
1272
+ leverage: options.input.leverage
1273
+ };
1274
+ return submitExchangeAction(options, action);
1275
+ }
1276
+ async function updateHyperliquidIsolatedMargin(options) {
1277
+ assertSymbol(options.input.symbol);
1278
+ assertPositiveNumber(options.input.ntli, "ntli");
1279
+ const env = options.environment ?? "mainnet";
1280
+ const asset = await resolveHyperliquidAssetIndex({
1281
+ symbol: options.input.symbol,
1282
+ baseUrl: API_BASES[env],
1283
+ environment: env,
1284
+ fetcher: fetch
1285
+ });
1286
+ const action = {
1287
+ type: "updateIsolatedMargin",
1288
+ asset,
1289
+ isBuy: options.input.isBuy,
1290
+ ntli: options.input.ntli
1291
+ };
1292
+ return submitExchangeAction(options, action);
1293
+ }
1294
+ async function reserveHyperliquidRequestWeight(options) {
1295
+ assertPositiveNumber(options.weight, "weight");
1296
+ const action = {
1297
+ type: "reserveRequestWeight",
1298
+ weight: options.weight
1299
+ };
1300
+ return submitExchangeAction(options, action);
1301
+ }
1302
+ async function createHyperliquidSubAccount(options) {
1303
+ assertString(options.name, "name");
1304
+ const action = {
1305
+ type: "createSubAccount",
1306
+ name: options.name
1307
+ };
1308
+ return submitExchangeAction(options, action);
1309
+ }
1310
+ async function transferHyperliquidSubAccount(options) {
1311
+ assertString(options.subAccountUser, "subAccountUser");
1312
+ const usdScaled = normalizeUsdToInt(options.usd);
1313
+ const action = {
1314
+ type: "subAccountTransfer",
1315
+ subAccountUser: normalizeAddress(options.subAccountUser),
1316
+ isDeposit: Boolean(options.isDeposit),
1317
+ usd: usdScaled
1318
+ };
1319
+ return submitExchangeAction(options, action);
1320
+ }
1321
+ async function sendHyperliquidSpot(options) {
1322
+ const env = options.environment ?? "mainnet";
1323
+ if (!options.wallet.account || !options.wallet.walletClient) {
1324
+ throw new Error("Wallet with signing capability is required for spotSend.");
1325
+ }
1326
+ assertString(options.token, "token");
1327
+ assertPositiveDecimal(options.amount, "amount");
1328
+ const signatureChainId = getSignatureChainId(env);
1329
+ const hyperliquidChain = HL_CHAIN_LABEL[env];
1330
+ const nonce = resolveRequiredExchangeNonce({
1331
+ nonce: options.nonce,
1332
+ nonceSource: options.nonceSource,
1333
+ wallet: options.wallet,
1334
+ action: "Hyperliquid spot send"
1335
+ });
1336
+ const time = BigInt(nonce);
1337
+ const signature = await signSpotSend({
1338
+ wallet: options.wallet,
1339
+ hyperliquidChain,
1340
+ signatureChainId,
1341
+ destination: normalizeAddress(options.destination),
1342
+ token: options.token,
1343
+ amount: toApiDecimal(options.amount),
1344
+ time
1345
+ });
1346
+ const action = {
1347
+ type: "spotSend",
1348
+ hyperliquidChain,
1349
+ signatureChainId,
1350
+ destination: normalizeAddress(options.destination),
1351
+ token: options.token,
1352
+ amount: toApiDecimal(options.amount),
1353
+ time: nonce
1354
+ };
1355
+ return postExchange(env, { action, nonce, signature });
1356
+ }
1357
+ async function submitExchangeAction(options, action) {
1358
+ if (!options.wallet?.account || !options.wallet.walletClient) {
1359
+ throw new Error("Hyperliquid exchange actions require a signing wallet.");
1360
+ }
1361
+ const env = options.environment ?? "mainnet";
1362
+ const nonceSource = options.walletNonceProvider ?? options.wallet.nonceSource ?? options.nonceSource;
1363
+ if (!nonceSource && options.nonce === void 0) {
1364
+ throw new Error("Wallet nonce source is required for Hyperliquid exchange actions.");
1365
+ }
1366
+ const effectiveNonce = options.nonce ?? nonceSource?.();
1367
+ if (effectiveNonce === void 0) {
1368
+ throw new Error("Hyperliquid exchange actions require a nonce.");
1369
+ }
1370
+ const signature = await signL1Action({
1371
+ wallet: options.wallet,
1372
+ action,
1373
+ nonce: effectiveNonce,
1374
+ vaultAddress: options.vaultAddress ? normalizeAddress(options.vaultAddress) : void 0,
1375
+ expiresAfter: options.expiresAfter,
1376
+ isTestnet: env === "testnet"
1377
+ });
1378
+ const body = {
1379
+ action,
1380
+ nonce: effectiveNonce,
1381
+ signature
1382
+ };
1383
+ if (options.vaultAddress) {
1384
+ body.vaultAddress = normalizeAddress(options.vaultAddress);
1385
+ }
1386
+ if (typeof options.expiresAfter === "number") {
1387
+ body.expiresAfter = options.expiresAfter;
1388
+ }
1389
+ return postExchange(env, body);
1390
+ }
1391
+ async function withAssetIndexes(options, entries, mapper) {
1392
+ const env = options.environment ?? "mainnet";
1393
+ return Promise.all(
1394
+ entries.map(async (entry) => {
1395
+ const assetIndex = await resolveHyperliquidAssetIndex({
1396
+ symbol: entry.symbol,
1397
+ baseUrl: API_BASES[env],
1398
+ environment: env,
1399
+ fetcher: fetch
1400
+ });
1401
+ return mapper(assetIndex, entry);
1402
+ })
1403
+ );
1404
+ }
1405
+ async function buildOrder(intent, options) {
1406
+ assertSymbol(intent.symbol);
1407
+ assertPositiveDecimal(intent.price, "price");
1408
+ assertPositiveDecimal(intent.size, "size");
1409
+ const env = options.environment ?? "mainnet";
1410
+ const assetIndex = await resolveHyperliquidAssetIndex({
1411
+ symbol: intent.symbol,
1412
+ baseUrl: API_BASES[env],
1413
+ environment: env,
1414
+ fetcher: fetch
1415
+ });
1416
+ const limitOrTrigger = intent.trigger ? mapTrigger(intent.trigger) : {
1417
+ limit: {
1418
+ tif: intent.tif ?? "Ioc"
1419
+ }
1420
+ };
1421
+ return {
1422
+ a: assetIndex,
1423
+ b: intent.side === "buy",
1424
+ p: toApiDecimal(intent.price),
1425
+ s: toApiDecimal(intent.size),
1426
+ r: intent.reduceOnly ?? false,
1427
+ t: limitOrTrigger,
1428
+ ...intent.clientId ? {
1429
+ c: normalizeCloid(intent.clientId)
1430
+ } : {}
1431
+ };
1432
+ }
1433
+ function mapTrigger(trigger) {
1434
+ assertPositiveDecimal(trigger.triggerPx, "triggerPx");
1435
+ return {
1436
+ trigger: {
1437
+ isMarket: Boolean(trigger.isMarket),
1438
+ triggerPx: toApiDecimal(trigger.triggerPx),
1439
+ tpsl: trigger.tpsl
1440
+ }
1441
+ };
1442
+ }
1443
+ function assertSymbol(value) {
1444
+ assertString(value, "symbol");
1445
+ }
1446
+ function normalizeUsdToInt(value) {
1447
+ if (typeof value === "bigint") {
1448
+ if (value < 0n) {
1449
+ throw new Error("usd must be non-negative.");
1450
+ }
1451
+ return Number(value);
1452
+ }
1453
+ const parsed = typeof value === "string" ? Number.parseFloat(value) : Number(value);
1454
+ if (!Number.isFinite(parsed) || parsed < 0) {
1455
+ throw new Error("usd must be a non-negative number.");
1456
+ }
1457
+ return Math.round(parsed * 1e6);
1458
+ }
1459
+ function assertString(value, label) {
1460
+ if (typeof value !== "string" || !value.trim()) {
1461
+ throw new Error(`${label} must be a non-empty string.`);
1462
+ }
1463
+ }
1464
+ function assertPositiveDecimal(value, label) {
1465
+ if (typeof value === "number") {
1466
+ assertPositiveNumber(value, label);
1467
+ return;
1468
+ }
1469
+ if (typeof value === "bigint") {
1470
+ if (value <= 0n) {
1471
+ throw new Error(`${label} must be positive.`);
1472
+ }
1473
+ return;
1474
+ }
1475
+ assertString(value, label);
1476
+ if (!/^(?:\d+\.?\d*|\.\d+)$/.test(value.trim())) {
1477
+ throw new Error(`${label} must be a positive decimal string.`);
1478
+ }
1479
+ const numeric = Number(value);
1480
+ if (!Number.isFinite(numeric) || numeric <= 0) {
1481
+ throw new Error(`${label} must be positive.`);
1482
+ }
1483
+ }
1484
+ function collectExchangeErrorMessages(payload) {
1485
+ if (!payload || typeof payload !== "object") return [];
1486
+ const root = payload;
1487
+ const messages = [];
1488
+ const statuses = root.response?.data?.statuses;
1489
+ if (Array.isArray(statuses)) {
1490
+ statuses.forEach((status, index) => {
1491
+ if (status && typeof status === "object" && "error" in status && typeof status.error === "string") {
1492
+ const errorText = status.error;
1493
+ messages.push(`status[${index}]: ${errorText}`);
1494
+ }
1495
+ });
1496
+ }
1497
+ const singleStatus = root.response?.data?.status;
1498
+ if (singleStatus && typeof singleStatus === "object" && "error" in singleStatus && typeof singleStatus.error === "string") {
1499
+ messages.push(singleStatus.error);
1500
+ }
1501
+ return messages;
1502
+ }
1503
+ async function postExchange(env, body) {
1504
+ const response = await fetch(`${API_BASES[env]}/exchange`, {
1505
+ method: "POST",
1506
+ headers: { "content-type": "application/json" },
1507
+ body: JSON.stringify(body)
1508
+ });
1509
+ const text = await response.text().catch(() => "");
1510
+ const json = (() => {
1511
+ if (!text) return null;
1512
+ try {
1513
+ return JSON.parse(text);
1514
+ } catch {
1515
+ return null;
1516
+ }
1517
+ })();
1518
+ if (!response.ok) {
1519
+ throw new HyperliquidApiError("Hyperliquid exchange action failed.", {
1520
+ status: response.status,
1521
+ statusText: response.statusText,
1522
+ body: json ?? (text ? text : null)
1523
+ });
1524
+ }
1525
+ if (!json) {
1526
+ throw new HyperliquidApiError("Hyperliquid exchange action failed.", {
1527
+ status: response.status,
1528
+ statusText: response.statusText,
1529
+ body: text ? text : null
1530
+ });
1531
+ }
1532
+ if (json.status !== "ok") {
1533
+ throw new HyperliquidApiError("Hyperliquid exchange returned error.", {
1534
+ status: response.status,
1535
+ statusText: response.statusText,
1536
+ body: json
1537
+ });
1538
+ }
1539
+ const nestedErrors = collectExchangeErrorMessages(json);
1540
+ if (nestedErrors.length > 0) {
1541
+ throw new HyperliquidApiError("Hyperliquid exchange returned action errors.", {
1542
+ status: response.status,
1543
+ statusText: response.statusText,
1544
+ body: json,
1545
+ errors: nestedErrors
1546
+ });
1547
+ }
1548
+ return json;
1549
+ }
1550
+ function resolveRequiredNonce(params) {
1551
+ if (typeof params.nonce === "number") {
1552
+ return params.nonce;
1553
+ }
1554
+ const resolved = params.nonceSource?.() ?? params.wallet?.nonceSource?.();
1555
+ if (resolved === void 0) {
1556
+ throw new Error(`${params.action} requires an explicit nonce or wallet nonce source.`);
1557
+ }
1558
+ return resolved;
1559
+ }
1560
+ function assertPositiveDecimalInput(value, label) {
1561
+ if (typeof value === "number") {
1562
+ if (!Number.isFinite(value) || value <= 0) {
1563
+ throw new Error(`${label} must be a positive number.`);
1564
+ }
1565
+ return;
1566
+ }
1567
+ if (typeof value === "bigint") {
1568
+ if (value <= 0n) {
1569
+ throw new Error(`${label} must be positive.`);
1570
+ }
1571
+ return;
1572
+ }
1573
+ const trimmed = value.trim();
1574
+ if (!trimmed.length) {
1575
+ throw new Error(`${label} must be a non-empty string.`);
1576
+ }
1577
+ if (!/^(?:\\d+\\.?\\d*|\\.\\d+)$/.test(trimmed)) {
1578
+ throw new Error(`${label} must be a positive decimal string.`);
1579
+ }
1580
+ const numeric = Number(trimmed);
1581
+ if (!Number.isFinite(numeric) || numeric <= 0) {
1582
+ throw new Error(`${label} must be positive.`);
1583
+ }
1584
+ }
1585
+ function normalizePositiveDecimalString(raw, label) {
1586
+ const trimmed = raw.trim();
1587
+ if (!trimmed.length) {
1588
+ throw new Error(`${label} must be a non-empty decimal string.`);
1589
+ }
1590
+ if (!/^(?:\\d+\\.?\\d*|\\.\\d+)$/.test(trimmed)) {
1591
+ throw new Error(`${label} must be a positive decimal string.`);
1592
+ }
1593
+ const normalized = trimmed.replace(/^0+(?=\\d)/, "").replace(/(\\.\\d*?)0+$/, "$1").replace(/\\.$/, "");
1594
+ const numeric = Number(normalized);
1595
+ if (!Number.isFinite(numeric) || numeric <= 0) {
1596
+ throw new Error(`${label} must be positive.`);
1597
+ }
1598
+ return normalized;
1599
+ }
1600
+ async function placeHyperliquidOrder(options) {
1601
+ const {
1602
+ wallet,
1603
+ orders,
1604
+ grouping = "na",
1605
+ environment,
1606
+ vaultAddress,
1607
+ expiresAfter,
1608
+ nonce
1609
+ } = options;
1610
+ if (!wallet?.account || !wallet.walletClient) {
1611
+ throw new Error("Hyperliquid order signing requires a wallet with signing capabilities.");
1612
+ }
1613
+ if (!orders.length) {
1614
+ throw new Error("At least one order is required.");
1615
+ }
1616
+ const inferredEnvironment = environment ?? "mainnet";
1617
+ const resolvedBaseUrl = API_BASES[inferredEnvironment];
1618
+ const preparedOrders = await Promise.all(
1619
+ orders.map(async (intent) => {
1620
+ assertPositiveDecimalInput(intent.price, "price");
1621
+ assertPositiveDecimalInput(intent.size, "size");
1622
+ if (intent.trigger) {
1623
+ assertPositiveDecimalInput(intent.trigger.triggerPx, "triggerPx");
1624
+ }
1625
+ const assetIndex = await resolveHyperliquidAssetIndex({
1626
+ symbol: intent.symbol,
1627
+ baseUrl: resolvedBaseUrl,
1628
+ environment: inferredEnvironment,
1629
+ fetcher: fetch
1630
+ });
1631
+ const order = {
1632
+ a: assetIndex,
1633
+ b: intent.side === "buy",
1634
+ p: toApiDecimal(intent.price),
1635
+ s: toApiDecimal(intent.size),
1636
+ r: intent.reduceOnly ?? false,
1637
+ t: intent.trigger ? {
1638
+ trigger: {
1639
+ isMarket: Boolean(intent.trigger.isMarket),
1640
+ triggerPx: toApiDecimal(intent.trigger.triggerPx),
1641
+ tpsl: intent.trigger.tpsl
1642
+ }
1643
+ } : {
1644
+ limit: {
1645
+ tif: intent.tif ?? "Ioc"
1646
+ }
1647
+ },
1648
+ ...intent.clientId ? { c: normalizeCloid(intent.clientId) } : {}
1649
+ };
1650
+ return order;
1651
+ })
1652
+ );
1653
+ const action = {
1654
+ type: "order",
1655
+ orders: preparedOrders,
1656
+ grouping,
1657
+ builder: {
1658
+ b: normalizeAddress(BUILDER_CODE.address),
1659
+ f: BUILDER_CODE.fee
1660
+ }
1661
+ };
1662
+ const effectiveNonce = resolveRequiredNonce({
1663
+ nonce,
1664
+ nonceSource: options.nonceSource,
1665
+ wallet,
1666
+ action: "Hyperliquid order submission"
1667
+ });
1668
+ const signature = await signL1Action({
1669
+ wallet,
1670
+ action,
1671
+ nonce: effectiveNonce,
1672
+ ...vaultAddress ? { vaultAddress } : {},
1673
+ ...typeof expiresAfter === "number" ? { expiresAfter } : {},
1674
+ isTestnet: inferredEnvironment === "testnet"
1675
+ });
1676
+ const body = {
1677
+ action,
1678
+ nonce: effectiveNonce,
1679
+ signature
1680
+ };
1681
+ if (vaultAddress) {
1682
+ body.vaultAddress = normalizeAddress(vaultAddress);
1683
+ }
1684
+ if (typeof expiresAfter === "number") {
1685
+ body.expiresAfter = expiresAfter;
1686
+ }
1687
+ const response = await fetch(`${resolvedBaseUrl}/exchange`, {
1688
+ method: "POST",
1689
+ headers: { "content-type": "application/json" },
1690
+ body: JSON.stringify(body)
1691
+ });
1692
+ const rawText = await response.text().catch(() => null);
1693
+ let parsed = null;
1694
+ if (rawText && rawText.length) {
1695
+ try {
1696
+ parsed = JSON.parse(rawText);
1697
+ } catch {
1698
+ parsed = rawText;
1699
+ }
1700
+ }
1701
+ const json = parsed && typeof parsed === "object" && "status" in parsed ? parsed : null;
1702
+ if (!response.ok || !json) {
1703
+ const detail = parsed?.error ?? parsed?.message ?? (typeof parsed === "string" ? parsed : rawText);
1704
+ const suffix = detail ? ` Detail: ${detail}` : "";
1705
+ throw new HyperliquidApiError(
1706
+ `Failed to submit Hyperliquid order.${suffix}`,
1707
+ parsed ?? rawText ?? { status: response.status }
1708
+ );
1709
+ }
1710
+ if (json.status !== "ok") {
1711
+ const detail = parsed?.error ?? rawText;
1712
+ throw new HyperliquidApiError(
1713
+ detail ? `Hyperliquid API returned an error status: ${detail}` : "Hyperliquid API returned an error status.",
1714
+ json
1715
+ );
1716
+ }
1717
+ const statuses = json.response?.data?.statuses ?? [];
1718
+ const errorStatuses = statuses.filter(
1719
+ (entry) => Boolean(
1720
+ entry && typeof entry === "object" && "error" in entry && typeof entry.error === "string"
1721
+ )
1722
+ );
1723
+ if (errorStatuses.length) {
1724
+ const message = errorStatuses.map((entry) => entry.error).join(", ");
1725
+ throw new HyperliquidApiError(message || "Hyperliquid rejected the order.", json);
1726
+ }
1727
+ return json;
1728
+ }
1729
+ async function depositToHyperliquidBridge(options) {
1730
+ const { environment, amount, wallet } = options;
1731
+ const parsedAmount = Number(amount);
1732
+ if (!Number.isFinite(parsedAmount) || parsedAmount <= 0) {
1733
+ throw new Error("Deposit amount must be a positive number.");
1734
+ }
1735
+ if (parsedAmount < MIN_DEPOSIT_USDC) {
1736
+ throw new Error(`Minimum deposit is ${MIN_DEPOSIT_USDC} USDC.`);
1737
+ }
1738
+ if (!wallet.account || !wallet.walletClient || !wallet.publicClient) {
1739
+ throw new Error("Wallet client and public client are required for deposit.");
1740
+ }
1741
+ const bridgeAddress = getBridgeAddress(environment);
1742
+ const usdcAddress = getUsdcAddress(environment);
1743
+ const amountUnits = parseUnits(amount, 6);
1744
+ const data = encodeFunctionData({
1745
+ abi: erc20Abi,
1746
+ functionName: "transfer",
1747
+ args: [bridgeAddress, amountUnits]
1748
+ });
1749
+ const txHash = await wallet.walletClient.sendTransaction({
1750
+ account: wallet.account,
1751
+ to: usdcAddress,
1752
+ data
1753
+ });
1754
+ await wallet.publicClient.waitForTransactionReceipt({ hash: txHash });
1755
+ return {
1756
+ txHash,
1757
+ amount: parsedAmount,
1758
+ amountUnits: amountUnits.toString(),
1759
+ environment,
1760
+ bridgeAddress
1761
+ };
1762
+ }
1763
+ async function withdrawFromHyperliquid(options) {
1764
+ const { environment, amount, destination, wallet } = options;
1765
+ const normalizedAmount = normalizePositiveDecimalString(amount, "Withdraw amount");
1766
+ const parsedAmount = Number.parseFloat(normalizedAmount);
1767
+ if (!wallet.account || !wallet.walletClient || !wallet.publicClient) {
1768
+ throw new Error("Wallet client and public client are required for withdraw.");
1769
+ }
1770
+ const signatureChainId = getSignatureChainId(environment);
1771
+ const hyperliquidChain = HL_CHAIN_LABEL[environment];
1772
+ const nonce = resolveRequiredNonce({
1773
+ nonce: options.nonce,
1774
+ nonceSource: options.nonceSource,
1775
+ wallet,
1776
+ action: "Hyperliquid withdraw"
1777
+ });
1778
+ const time = BigInt(nonce);
1779
+ const normalizedDestination = normalizeAddress(destination);
1780
+ const signatureHex = await wallet.walletClient.signTypedData({
1781
+ account: wallet.account,
1782
+ domain: {
1783
+ name: "HyperliquidSignTransaction",
1784
+ version: "1",
1785
+ chainId: Number.parseInt(signatureChainId, 16),
1786
+ verifyingContract: ZERO_ADDRESS
1787
+ },
1788
+ types: {
1789
+ "HyperliquidTransaction:Withdraw": [
1790
+ { name: "hyperliquidChain", type: "string" },
1791
+ { name: "destination", type: "string" },
1792
+ { name: "amount", type: "string" },
1793
+ { name: "time", type: "uint64" }
1794
+ ]
1795
+ },
1796
+ primaryType: "HyperliquidTransaction:Withdraw",
1797
+ message: {
1798
+ hyperliquidChain,
1799
+ destination: normalizedDestination,
1800
+ amount: normalizedAmount,
1801
+ time
1802
+ }
1803
+ });
1804
+ const response = await fetch(`${HL_ENDPOINT[environment]}/exchange`, {
1805
+ method: "POST",
1806
+ headers: { "content-type": "application/json" },
1807
+ body: JSON.stringify({
1808
+ action: {
1809
+ type: "withdraw3",
1810
+ signatureChainId,
1811
+ hyperliquidChain,
1812
+ destination: normalizedDestination,
1813
+ amount: normalizedAmount,
1814
+ time: nonce
1815
+ },
1816
+ nonce,
1817
+ signature: splitSignature(signatureHex)
1818
+ })
1819
+ });
1820
+ const json = await response.json().catch(() => null);
1821
+ if (!response.ok || json?.status !== "ok") {
1822
+ throw new Error(
1823
+ `Hyperliquid withdraw failed: ${json?.response ?? json?.error ?? response.statusText}`
1824
+ );
1825
+ }
1826
+ return {
1827
+ amount: parsedAmount,
1828
+ destination: normalizedDestination,
1829
+ environment,
1830
+ nonce,
1831
+ status: json.status ?? "ok"
1832
+ };
1833
+ }
1834
+ async function fetchHyperliquidClearinghouseState(params) {
1835
+ const response = await fetch(`${HL_ENDPOINT[params.environment]}/info`, {
1836
+ method: "POST",
1837
+ headers: { "content-type": "application/json" },
1838
+ body: JSON.stringify({ type: "clearinghouseState", user: params.walletAddress })
1839
+ });
1840
+ const data = await response.json().catch(() => null);
1841
+ return { ok: response.ok, data };
1842
+ }
1843
+ async function approveHyperliquidBuilderFee(options) {
1844
+ const { environment, wallet, nonce, signatureChainId } = options;
1845
+ if (!wallet?.account || !wallet.walletClient) {
1846
+ throw new Error("Hyperliquid builder approval requires a wallet with signing capabilities.");
1847
+ }
1848
+ const inferredEnvironment = environment ?? "mainnet";
1849
+ const maxFeeRate = `${BUILDER_CODE.fee / 1e3}%`;
1850
+ const effectiveNonce = resolveRequiredNonce({
1851
+ nonce,
1852
+ nonceSource: options.nonceSource,
1853
+ wallet,
1854
+ action: "Hyperliquid builder approval"
1855
+ });
1856
+ const response = await fetch(`${API_BASES[inferredEnvironment]}/exchange`, {
1857
+ method: "POST",
1858
+ headers: { "content-type": "application/json" },
1859
+ body: JSON.stringify({
1860
+ action: {
1861
+ type: "approveBuilderFee",
1862
+ maxFeeRate,
1863
+ builder: normalizeAddress(BUILDER_CODE.address),
1864
+ hyperliquidChain: HL_CHAIN_LABEL[inferredEnvironment],
1865
+ signatureChainId: signatureChainId ?? getSignatureChainId(inferredEnvironment),
1866
+ nonce: effectiveNonce
1867
+ },
1868
+ nonce: effectiveNonce,
1869
+ signature: await signApproveBuilderFee({
1870
+ wallet,
1871
+ maxFeeRate,
1872
+ nonce: BigInt(effectiveNonce),
1873
+ signatureChainId: signatureChainId ?? getSignatureChainId(inferredEnvironment),
1874
+ isTestnet: inferredEnvironment === "testnet"
1875
+ })
1876
+ })
1877
+ });
1878
+ const rawText = await response.text().catch(() => null);
1879
+ let parsed = null;
1880
+ if (rawText && rawText.length) {
1881
+ try {
1882
+ parsed = JSON.parse(rawText);
1883
+ } catch {
1884
+ parsed = rawText;
1885
+ }
1886
+ }
1887
+ const json = parsed && typeof parsed === "object" && "status" in parsed ? parsed : null;
1888
+ if (!response.ok || !json) {
1889
+ const detail = parsed?.error ?? parsed?.message ?? (typeof parsed === "string" ? parsed : rawText);
1890
+ const suffix = detail ? ` Detail: ${detail}` : "";
1891
+ throw new HyperliquidApiError(
1892
+ `Failed to submit builder approval.${suffix}`,
1893
+ parsed ?? rawText ?? { status: response.status }
1894
+ );
1895
+ }
1896
+ if (json.status !== "ok") {
1897
+ const detail = parsed?.error ?? rawText;
1898
+ throw new HyperliquidApiError(
1899
+ detail ? `Hyperliquid builder approval returned an error: ${detail}` : "Hyperliquid builder approval returned an error.",
1900
+ json
1901
+ );
1902
+ }
1903
+ return json;
1904
+ }
1905
+ async function getHyperliquidMaxBuilderFee(params) {
1906
+ const response = await fetch(`${API_BASES[params.environment]}/info`, {
1907
+ method: "POST",
1908
+ headers: { "content-type": "application/json" },
1909
+ body: JSON.stringify({
1910
+ type: "maxBuilderFee",
1911
+ user: normalizeAddress(params.user),
1912
+ builder: BUILDER_CODE.address
1913
+ })
1914
+ });
1915
+ const data = await response.json().catch(() => null);
1916
+ if (!response.ok) {
1917
+ throw new HyperliquidApiError(
1918
+ "Failed to query max builder fee.",
1919
+ data ?? { status: response.status }
1920
+ );
1921
+ }
1922
+ return data;
1923
+ }
1924
+ function createHyperliquidActionHash(params) {
1925
+ return createL1ActionHash(params);
1926
+ }
1927
+
1928
+ export { DEFAULT_HYPERLIQUID_MARKET_SLIPPAGE_BPS, HyperliquidApiError, HyperliquidBuilderApprovalError, HyperliquidExchangeClient, HyperliquidGuardError, HyperliquidInfoClient, HyperliquidTermsError, approveHyperliquidBuilderFee, batchModifyHyperliquidOrders, buildHyperliquidMarketIdentity, cancelAllHyperliquidOrders, cancelHyperliquidOrders, cancelHyperliquidOrdersByCloid, cancelHyperliquidTwapOrder, computeHyperliquidMarketIocLimitPrice, createHyperliquidActionHash, createHyperliquidSubAccount, createMonotonicNonceFactory, depositToHyperliquidBridge, fetchHyperliquidAssetCtxs, fetchHyperliquidClearinghouseState, fetchHyperliquidFrontendOpenOrders, fetchHyperliquidHistoricalOrders, fetchHyperliquidMeta, fetchHyperliquidMetaAndAssetCtxs, fetchHyperliquidOpenOrders, fetchHyperliquidOrderStatus, fetchHyperliquidPreTransferCheck, fetchHyperliquidSpotAssetCtxs, fetchHyperliquidSpotClearinghouseState, fetchHyperliquidSpotMeta, fetchHyperliquidSpotMetaAndAssetCtxs, fetchHyperliquidUserFills, fetchHyperliquidUserFillsByTime, fetchHyperliquidUserRateLimit, getHyperliquidMaxBuilderFee, modifyHyperliquidOrder, placeHyperliquidOrder, placeHyperliquidTwapOrder, reserveHyperliquidRequestWeight, resolveHyperliquidAbstractionFromMode, scheduleHyperliquidCancel, sendHyperliquidSpot, setHyperliquidAccountAbstractionMode, setHyperliquidDexAbstraction, setHyperliquidPortfolioMargin, transferHyperliquidSubAccount, updateHyperliquidIsolatedMargin, updateHyperliquidLeverage, withdrawFromHyperliquid };
1929
+ //# sourceMappingURL=browser.js.map
1930
+ //# sourceMappingURL=browser.js.map