opentool 0.13.0 → 0.15.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.
@@ -316,11 +316,31 @@ function computeHyperliquidMarketIocLimitPrice(params) {
316
316
  const slippage = bps / 1e4;
317
317
  const multiplier = params.side === "buy" ? 1 + slippage : 1 - slippage;
318
318
  const price = params.markPrice * multiplier;
319
- return formatRoundedDecimal(price, decimals);
319
+ const precision = Math.max(0, Math.min(12, Math.floor(decimals)));
320
+ const factor = 10 ** precision;
321
+ const scaled = price * factor;
322
+ const directionalRounded = params.side === "buy" ? Math.ceil(scaled) / factor : Math.floor(scaled) / factor;
323
+ return formatRoundedDecimal(directionalRounded, precision);
320
324
  }
321
325
  var HyperliquidApiError = class extends Error {
322
326
  constructor(message, response) {
323
- super(message);
327
+ const responseRecord = response && typeof response === "object" ? response : null;
328
+ const explicitErrors = Array.isArray(responseRecord?.errors) ? responseRecord.errors.filter(
329
+ (entry) => typeof entry === "string" && entry.trim().length > 0
330
+ ) : [];
331
+ const bodyStatuses = responseRecord?.body && typeof responseRecord.body === "object" && responseRecord.body !== null && "response" in responseRecord.body ? (responseRecord.body.response?.data?.statuses ?? []).map((status) => typeof status?.error === "string" ? status.error : null).filter((entry) => Boolean(entry && entry.trim().length > 0)) : [];
332
+ const singleStatusError = responseRecord?.body && typeof responseRecord.body === "object" && responseRecord.body !== null && "response" in responseRecord.body ? responseRecord.body.response?.data?.status?.error : null;
333
+ const details = Array.from(
334
+ new Set(
335
+ [
336
+ ...explicitErrors,
337
+ ...bodyStatuses,
338
+ typeof singleStatusError === "string" ? singleStatusError : null
339
+ ].filter((entry) => Boolean(entry && entry.trim().length > 0))
340
+ )
341
+ );
342
+ const enrichedMessage = details.length > 0 ? `${message} ${details.join(" | ")}` : message;
343
+ super(enrichedMessage);
324
344
  this.response = response;
325
345
  this.name = "HyperliquidApiError";
326
346
  }
@@ -548,7 +568,14 @@ async function resolveHyperliquidAssetIndex(args) {
548
568
  }
549
569
  function toApiDecimal(value) {
550
570
  if (typeof value === "string") {
551
- return value;
571
+ const trimmed = value.trim();
572
+ if (!trimmed.length) {
573
+ throw new Error("Decimal strings must be non-empty.");
574
+ }
575
+ if (!/^-?(?:\d+\.?\d*|\.\d+)$/.test(trimmed)) {
576
+ throw new Error("Decimal strings must be plain base-10 numbers.");
577
+ }
578
+ return trimmed.replace(/^(-?)0+(?=\d)/, "$1").replace(/\.0*$|(\.\d+?)0+$/, "$1").replace(/^(-?)\./, "$10.").replace(/^-?$/, "0").replace(/^-0$/, "0");
552
579
  }
553
580
  if (typeof value === "bigint") {
554
581
  return value.toString();
@@ -1384,7 +1411,7 @@ async function placeHyperliquidTwapOrder(options) {
1384
1411
  symbol: twap.symbol,
1385
1412
  baseUrl: API_BASES[env],
1386
1413
  environment: env,
1387
- fetcher: fetch
1414
+ fetcher: (...args) => fetch(...args)
1388
1415
  });
1389
1416
  const action = {
1390
1417
  type: "twapOrder",
@@ -1406,7 +1433,7 @@ async function cancelHyperliquidTwapOrder(options) {
1406
1433
  symbol: options.cancel.symbol,
1407
1434
  baseUrl: API_BASES[env],
1408
1435
  environment: env,
1409
- fetcher: fetch
1436
+ fetcher: (...args) => fetch(...args)
1410
1437
  });
1411
1438
  const action = {
1412
1439
  type: "twapCancel",
@@ -1423,7 +1450,7 @@ async function updateHyperliquidLeverage(options) {
1423
1450
  symbol: options.input.symbol,
1424
1451
  baseUrl: API_BASES[env],
1425
1452
  environment: env,
1426
- fetcher: fetch
1453
+ fetcher: (...args) => fetch(...args)
1427
1454
  });
1428
1455
  const action = {
1429
1456
  type: "updateLeverage",
@@ -1441,7 +1468,7 @@ async function updateHyperliquidIsolatedMargin(options) {
1441
1468
  symbol: options.input.symbol,
1442
1469
  baseUrl: API_BASES[env],
1443
1470
  environment: env,
1444
- fetcher: fetch
1471
+ fetcher: (...args) => fetch(...args)
1445
1472
  });
1446
1473
  const action = {
1447
1474
  type: "updateIsolatedMargin",
@@ -1556,7 +1583,7 @@ async function withAssetIndexes(options, entries, mapper) {
1556
1583
  symbol: entry.symbol,
1557
1584
  baseUrl: API_BASES[env],
1558
1585
  environment: env,
1559
- fetcher: fetch
1586
+ fetcher: (...args) => fetch(...args)
1560
1587
  });
1561
1588
  return mapper(assetIndex, entry);
1562
1589
  })
@@ -1571,7 +1598,7 @@ async function buildOrder(intent, options) {
1571
1598
  symbol: intent.symbol,
1572
1599
  baseUrl: API_BASES[env],
1573
1600
  environment: env,
1574
- fetcher: fetch
1601
+ fetcher: (...args) => fetch(...args)
1575
1602
  });
1576
1603
  const limitOrTrigger = intent.trigger ? mapTrigger(intent.trigger) : {
1577
1604
  limit: {
@@ -1735,6 +1762,71 @@ function extractHyperliquidDex(symbol) {
1735
1762
  const dex = symbol.slice(0, idx).trim().toLowerCase();
1736
1763
  return dex || null;
1737
1764
  }
1765
+ function parseHyperliquidSymbol(value) {
1766
+ if (!value) return null;
1767
+ const trimmed = value.trim();
1768
+ if (!trimmed) return null;
1769
+ if (trimmed.startsWith("@")) {
1770
+ return {
1771
+ raw: trimmed,
1772
+ kind: "spotIndex",
1773
+ normalized: trimmed,
1774
+ routeTicker: trimmed,
1775
+ displaySymbol: trimmed,
1776
+ base: null,
1777
+ quote: null,
1778
+ pair: null,
1779
+ dex: null,
1780
+ leverageMode: "cross"
1781
+ };
1782
+ }
1783
+ const dex = extractHyperliquidDex(trimmed);
1784
+ const pair = resolveHyperliquidPair(trimmed);
1785
+ const base = normalizeHyperliquidBaseSymbol(trimmed);
1786
+ if (dex) {
1787
+ if (!base) return null;
1788
+ return {
1789
+ raw: trimmed,
1790
+ kind: "perp",
1791
+ normalized: `${dex}:${base}`,
1792
+ routeTicker: `${dex}:${base}`,
1793
+ displaySymbol: `${dex.toUpperCase()}:${base}-USDC`,
1794
+ base,
1795
+ quote: null,
1796
+ pair: null,
1797
+ dex,
1798
+ leverageMode: "isolated"
1799
+ };
1800
+ }
1801
+ if (pair) {
1802
+ const [pairBase, pairQuote] = pair.split("/");
1803
+ return {
1804
+ raw: trimmed,
1805
+ kind: "spot",
1806
+ normalized: pair,
1807
+ routeTicker: pair.replace("/", "-"),
1808
+ displaySymbol: pair.replace("/", "-"),
1809
+ base: pairBase ?? null,
1810
+ quote: pairQuote ?? null,
1811
+ pair,
1812
+ dex: null,
1813
+ leverageMode: "cross"
1814
+ };
1815
+ }
1816
+ if (!base) return null;
1817
+ return {
1818
+ raw: trimmed,
1819
+ kind: "perp",
1820
+ normalized: base,
1821
+ routeTicker: base,
1822
+ displaySymbol: `${base}-USDC`,
1823
+ base,
1824
+ quote: null,
1825
+ pair: null,
1826
+ dex: null,
1827
+ leverageMode: "cross"
1828
+ };
1829
+ }
1738
1830
  function normalizeSpotTokenName2(value) {
1739
1831
  const raw = (value ?? "").trim();
1740
1832
  if (!raw) return "";
@@ -2050,20 +2142,10 @@ function planHyperliquidTrade(params) {
2050
2142
  }
2051
2143
 
2052
2144
  // src/adapters/hyperliquid/order-utils.ts
2053
- var MAX_HYPERLIQUID_PRICE_DECIMALS = 8;
2054
- function countDecimals(value) {
2055
- if (!Number.isFinite(value)) return 0;
2056
- const s = value.toString();
2057
- const [, dec = ""] = s.split(".");
2145
+ function countDecimalPlaces(value) {
2146
+ const [, dec = ""] = value.split(".");
2058
2147
  return dec.length;
2059
2148
  }
2060
- function clampPriceDecimals(value) {
2061
- if (!Number.isFinite(value) || value <= 0) {
2062
- throw new Error("Price must be positive.");
2063
- }
2064
- const fixed = value.toFixed(MAX_HYPERLIQUID_PRICE_DECIMALS);
2065
- return fixed.replace(/\.?0+$/, "");
2066
- }
2067
2149
  function assertNumberString(value) {
2068
2150
  if (!/^-?(?:\d+\.?\d*|\.\d+)$/.test(value)) {
2069
2151
  throw new TypeError("Invalid decimal number string.");
@@ -2114,6 +2196,29 @@ var StringMath = {
2114
2196
  const index = value.indexOf(".");
2115
2197
  return index === -1 ? value : value.slice(0, index) || "0";
2116
2198
  },
2199
+ roundInteger(value, mode) {
2200
+ const normalized = normalizeDecimalString(value);
2201
+ const negative = normalized.startsWith("-");
2202
+ if (negative) {
2203
+ throw new RangeError("Directional rounding only supports positive values.");
2204
+ }
2205
+ const [intPartRaw, fracPart = ""] = normalized.split(".");
2206
+ const intPart = intPartRaw.replace(/^0+(?=\d)/, "") || "0";
2207
+ const hasFraction = /[1-9]/.test(fracPart);
2208
+ if (!hasFraction) return intPart;
2209
+ if (mode === "down") return intPart;
2210
+ const digits = intPart.split("");
2211
+ let carry = 1;
2212
+ for (let idx = digits.length - 1; idx >= 0 && carry > 0; idx -= 1) {
2213
+ const next = Number(digits[idx] ?? "0") + carry;
2214
+ digits[idx] = String(next % 10);
2215
+ carry = next >= 10 ? 1 : 0;
2216
+ }
2217
+ if (carry > 0) {
2218
+ digits.unshift("1");
2219
+ }
2220
+ return digits.join("").replace(/^0+(?=\d)/, "") || "0";
2221
+ },
2117
2222
  toPrecisionTruncate(value, precision) {
2118
2223
  if (!Number.isInteger(precision) || precision < 1) {
2119
2224
  throw new RangeError("Precision must be a positive integer.");
@@ -2140,6 +2245,41 @@ var StringMath = {
2140
2245
  return normalizeDecimalString(result);
2141
2246
  }
2142
2247
  };
2248
+ function ceilDiv(numerator, denominator) {
2249
+ if (denominator <= 0n) {
2250
+ throw new RangeError("Denominator must be positive.");
2251
+ }
2252
+ return (numerator + denominator - 1n) / denominator;
2253
+ }
2254
+ function scaleDecimalToInt(value, decimals, mode) {
2255
+ if (!Number.isInteger(decimals) || decimals < 0) {
2256
+ throw new RangeError("Decimals must be a non-negative integer.");
2257
+ }
2258
+ const normalized = normalizeDecimalString(value);
2259
+ assertNumberString(normalized);
2260
+ const negative = normalized.startsWith("-");
2261
+ if (negative) {
2262
+ throw new RangeError("Only positive values are supported.");
2263
+ }
2264
+ const shifted = StringMath.multiplyByPow10(normalized, decimals);
2265
+ const rounded = StringMath.roundInteger(shifted, mode);
2266
+ return BigInt(rounded);
2267
+ }
2268
+ function formatScaledDecimal(value, decimals) {
2269
+ if (!Number.isInteger(decimals) || decimals < 0) {
2270
+ throw new RangeError("Decimals must be a non-negative integer.");
2271
+ }
2272
+ const negative = value < 0n;
2273
+ const abs = negative ? -value : value;
2274
+ const raw = abs.toString();
2275
+ if (decimals === 0) {
2276
+ return `${negative ? "-" : ""}${raw}`;
2277
+ }
2278
+ const padded = raw.padStart(decimals + 1, "0");
2279
+ const intPart = padded.slice(0, -decimals) || "0";
2280
+ const fracPart = padded.slice(-decimals);
2281
+ return normalizeDecimalString(`${negative ? "-" : ""}${intPart}.${fracPart}`);
2282
+ }
2143
2283
  function formatHyperliquidPrice(price, szDecimals, marketType = "perp") {
2144
2284
  const normalized = price.toString().trim();
2145
2285
  assertNumberString(normalized);
@@ -2172,33 +2312,52 @@ function formatHyperliquidOrderSize(value, szDecimals) {
2172
2312
  }
2173
2313
  }
2174
2314
  function roundHyperliquidPriceToTick(price, tick, side) {
2175
- if (!Number.isFinite(price) || price <= 0) {
2176
- throw new Error("Price must be positive.");
2177
- }
2178
2315
  if (!Number.isFinite(tick.tickDecimals) || tick.tickDecimals < 0) {
2179
2316
  throw new Error("tick.tickDecimals must be a non-negative number.");
2180
2317
  }
2181
2318
  if (tick.tickSizeInt <= 0n) {
2182
2319
  throw new Error("tick.tickSizeInt must be positive.");
2183
2320
  }
2184
- const scale = 10 ** tick.tickDecimals;
2185
- const scaled = BigInt(Math.round(price * scale));
2321
+ const normalized = normalizeDecimalString(price.toString());
2322
+ assertNumberString(normalized);
2323
+ if (Number.parseFloat(normalized) <= 0) {
2324
+ throw new Error("Price must be positive.");
2325
+ }
2326
+ const scaled = scaleDecimalToInt(
2327
+ normalized,
2328
+ tick.tickDecimals,
2329
+ side === "buy" ? "up" : "down"
2330
+ );
2186
2331
  const tickSize = tick.tickSizeInt;
2187
2332
  const rounded = side === "sell" ? scaled / tickSize * tickSize : (scaled + tickSize - 1n) / tickSize * tickSize;
2188
- const integer = Number(rounded) / scale;
2189
- return clampPriceDecimals(integer);
2333
+ return formatScaledDecimal(rounded, tick.tickDecimals);
2190
2334
  }
2191
2335
  function formatHyperliquidMarketablePrice(params) {
2192
2336
  const { mid, side, slippageBps, tick } = params;
2193
- const decimals = countDecimals(mid);
2194
- const factor = 10 ** decimals;
2195
- const adjusted = mid * (side === "buy" ? 1 + slippageBps / 1e4 : 1 - slippageBps / 1e4);
2337
+ if (!Number.isFinite(mid) || mid <= 0) {
2338
+ throw new Error("mid must be a positive number.");
2339
+ }
2340
+ if (!Number.isFinite(slippageBps) || slippageBps < 0) {
2341
+ throw new Error("slippageBps must be a non-negative number.");
2342
+ }
2343
+ const midString = normalizeDecimalString(mid.toString());
2344
+ const baseDecimals = countDecimalPlaces(midString);
2345
+ const workDecimals = Math.max(baseDecimals + 4, tick?.tickDecimals ?? 0, 8);
2346
+ const scaledMid = scaleDecimalToInt(midString, workDecimals, "down");
2347
+ const slippageNumerator = BigInt(
2348
+ side === "buy" ? 1e4 + slippageBps : 1e4 - slippageBps
2349
+ );
2350
+ const adjustedScaled = side === "buy" ? ceilDiv(scaledMid * slippageNumerator, 10000n) : scaledMid * slippageNumerator / 10000n;
2351
+ const adjusted = formatScaledDecimal(adjustedScaled, workDecimals);
2196
2352
  if (tick) {
2197
2353
  return roundHyperliquidPriceToTick(adjusted, tick, side);
2198
2354
  }
2199
- const scaled = adjusted * factor;
2200
- const rounded = side === "buy" ? Math.ceil(scaled) / factor : Math.floor(scaled) / factor;
2201
- return clampPriceDecimals(rounded);
2355
+ const roundedScaled = scaleDecimalToInt(
2356
+ adjusted,
2357
+ baseDecimals,
2358
+ side === "buy" ? "up" : "down"
2359
+ );
2360
+ return formatScaledDecimal(roundedScaled, baseDecimals);
2202
2361
  }
2203
2362
  function extractHyperliquidOrderIds(responses) {
2204
2363
  const cloids = /* @__PURE__ */ new Set();
@@ -2792,6 +2951,393 @@ var __hyperliquidMarketDataInternals = {
2792
2951
  toScaledInt,
2793
2952
  formatScaledInt
2794
2953
  };
2954
+ function resolveRequiredNonce(params) {
2955
+ if (typeof params.nonce === "number") {
2956
+ return params.nonce;
2957
+ }
2958
+ const resolved = params.nonceSource?.() ?? params.wallet?.nonceSource?.();
2959
+ if (resolved === void 0) {
2960
+ throw new Error(`${params.action} requires an explicit nonce or wallet nonce source.`);
2961
+ }
2962
+ return resolved;
2963
+ }
2964
+ function assertPositiveDecimalInput(value, label) {
2965
+ if (typeof value === "number") {
2966
+ if (!Number.isFinite(value) || value <= 0) {
2967
+ throw new Error(`${label} must be a positive number.`);
2968
+ }
2969
+ return;
2970
+ }
2971
+ if (typeof value === "bigint") {
2972
+ if (value <= 0n) {
2973
+ throw new Error(`${label} must be positive.`);
2974
+ }
2975
+ return;
2976
+ }
2977
+ const trimmed = value.trim();
2978
+ if (!trimmed.length) {
2979
+ throw new Error(`${label} must be a non-empty string.`);
2980
+ }
2981
+ if (!/^(?:\d+\.?\d*|\.\d+)$/.test(trimmed)) {
2982
+ throw new Error(`${label} must be a positive decimal string.`);
2983
+ }
2984
+ const numeric = Number(trimmed);
2985
+ if (!Number.isFinite(numeric) || numeric <= 0) {
2986
+ throw new Error(`${label} must be positive.`);
2987
+ }
2988
+ }
2989
+ async function placeHyperliquidOrder(options) {
2990
+ const {
2991
+ wallet,
2992
+ orders,
2993
+ grouping = "na",
2994
+ environment,
2995
+ vaultAddress,
2996
+ expiresAfter,
2997
+ nonce
2998
+ } = options;
2999
+ if (!wallet?.account || !wallet.walletClient) {
3000
+ throw new Error("Hyperliquid order signing requires a wallet with signing capabilities.");
3001
+ }
3002
+ if (!orders.length) {
3003
+ throw new Error("At least one order is required.");
3004
+ }
3005
+ const inferredEnvironment = environment ?? "mainnet";
3006
+ const resolvedBaseUrl = API_BASES[inferredEnvironment];
3007
+ const preparedOrders = await Promise.all(
3008
+ orders.map(async (intent) => {
3009
+ assertPositiveDecimalInput(intent.price, "price");
3010
+ assertPositiveDecimalInput(intent.size, "size");
3011
+ if (intent.trigger) {
3012
+ assertPositiveDecimalInput(intent.trigger.triggerPx, "triggerPx");
3013
+ }
3014
+ const assetIndex = await resolveHyperliquidAssetIndex({
3015
+ symbol: intent.symbol,
3016
+ baseUrl: resolvedBaseUrl,
3017
+ environment: inferredEnvironment,
3018
+ fetcher: (...args) => fetch(...args)
3019
+ });
3020
+ const order = {
3021
+ a: assetIndex,
3022
+ b: intent.side === "buy",
3023
+ p: toApiDecimal(intent.price),
3024
+ s: toApiDecimal(intent.size),
3025
+ r: intent.reduceOnly ?? false,
3026
+ t: intent.trigger ? {
3027
+ trigger: {
3028
+ isMarket: Boolean(intent.trigger.isMarket),
3029
+ triggerPx: toApiDecimal(intent.trigger.triggerPx),
3030
+ tpsl: intent.trigger.tpsl
3031
+ }
3032
+ } : {
3033
+ limit: {
3034
+ tif: intent.tif ?? "Ioc"
3035
+ }
3036
+ },
3037
+ ...intent.clientId ? { c: normalizeCloid(intent.clientId) } : {}
3038
+ };
3039
+ return order;
3040
+ })
3041
+ );
3042
+ const action = {
3043
+ type: "order",
3044
+ orders: preparedOrders,
3045
+ grouping,
3046
+ builder: {
3047
+ b: normalizeAddress(BUILDER_CODE.address),
3048
+ f: BUILDER_CODE.fee
3049
+ }
3050
+ };
3051
+ const effectiveNonce = resolveRequiredNonce({
3052
+ nonce,
3053
+ nonceSource: options.nonceSource,
3054
+ wallet,
3055
+ action: "Hyperliquid order submission"
3056
+ });
3057
+ const signature = await signL1Action({
3058
+ wallet,
3059
+ action,
3060
+ nonce: effectiveNonce,
3061
+ ...vaultAddress ? { vaultAddress } : {},
3062
+ ...typeof expiresAfter === "number" ? { expiresAfter } : {},
3063
+ isTestnet: inferredEnvironment === "testnet"
3064
+ });
3065
+ const body = {
3066
+ action,
3067
+ nonce: effectiveNonce,
3068
+ signature
3069
+ };
3070
+ if (vaultAddress) {
3071
+ body.vaultAddress = normalizeAddress(vaultAddress);
3072
+ }
3073
+ if (typeof expiresAfter === "number") {
3074
+ body.expiresAfter = expiresAfter;
3075
+ }
3076
+ const response = await fetch(`${resolvedBaseUrl}/exchange`, {
3077
+ method: "POST",
3078
+ headers: { "content-type": "application/json" },
3079
+ body: JSON.stringify(body)
3080
+ });
3081
+ const rawText = await response.text().catch(() => null);
3082
+ let parsed = null;
3083
+ if (rawText && rawText.length) {
3084
+ try {
3085
+ parsed = JSON.parse(rawText);
3086
+ } catch {
3087
+ parsed = rawText;
3088
+ }
3089
+ }
3090
+ const json = parsed && typeof parsed === "object" && "status" in parsed ? parsed : null;
3091
+ if (!response.ok || !json) {
3092
+ const detail = parsed?.error ?? parsed?.message ?? (typeof parsed === "string" ? parsed : rawText);
3093
+ const suffix = detail ? ` Detail: ${detail}` : "";
3094
+ throw new HyperliquidApiError(
3095
+ `Failed to submit Hyperliquid order.${suffix}`,
3096
+ parsed ?? rawText ?? { status: response.status }
3097
+ );
3098
+ }
3099
+ if (json.status !== "ok") {
3100
+ const detail = parsed?.error ?? rawText;
3101
+ throw new HyperliquidApiError(
3102
+ detail ? `Hyperliquid API returned an error status: ${detail}` : "Hyperliquid API returned an error status.",
3103
+ json
3104
+ );
3105
+ }
3106
+ const statuses = json.response?.data?.statuses ?? [];
3107
+ const errorStatuses = statuses.filter(
3108
+ (entry) => Boolean(
3109
+ entry && typeof entry === "object" && "error" in entry && typeof entry.error === "string"
3110
+ )
3111
+ );
3112
+ if (errorStatuses.length) {
3113
+ const message = errorStatuses.map((entry) => entry.error).join(", ");
3114
+ throw new HyperliquidApiError(message || "Hyperliquid rejected the order.", json);
3115
+ }
3116
+ return json;
3117
+ }
3118
+
3119
+ // src/adapters/hyperliquid/tpsl.ts
3120
+ var DEFAULT_HYPERLIQUID_TPSL_MARKET_SLIPPAGE_BPS = 1e3;
3121
+ function toDecimalInput(value, label) {
3122
+ if (typeof value === "bigint") {
3123
+ if (value <= 0n) {
3124
+ throw new Error(`${label} must be positive.`);
3125
+ }
3126
+ return value.toString();
3127
+ }
3128
+ return value;
3129
+ }
3130
+ function toPositiveNumber(value, label) {
3131
+ if (typeof value === "bigint") {
3132
+ if (value <= 0n) {
3133
+ throw new Error(`${label} must be positive.`);
3134
+ }
3135
+ return Number(value);
3136
+ }
3137
+ const numeric = typeof value === "number" ? value : Number.parseFloat(value.toString().trim());
3138
+ if (!Number.isFinite(numeric) || numeric <= 0) {
3139
+ throw new Error(`${label} must be positive.`);
3140
+ }
3141
+ return numeric;
3142
+ }
3143
+ function normalizeExecutionType(value) {
3144
+ return value ?? "market";
3145
+ }
3146
+ function resolveTriggerDirection(params) {
3147
+ const isLong = params.parentSide === "buy";
3148
+ if (params.leg === "tp") {
3149
+ if (isLong && params.triggerPx <= params.referencePrice) {
3150
+ throw new Error("Take profit trigger must be above the current price for long positions.");
3151
+ }
3152
+ if (!isLong && params.triggerPx >= params.referencePrice) {
3153
+ throw new Error("Take profit trigger must be below the current price for short positions.");
3154
+ }
3155
+ return;
3156
+ }
3157
+ if (isLong && params.triggerPx >= params.referencePrice) {
3158
+ throw new Error("Stop loss trigger must be below the current price for long positions.");
3159
+ }
3160
+ if (!isLong && params.triggerPx <= params.referencePrice) {
3161
+ throw new Error("Stop loss trigger must be above the current price for short positions.");
3162
+ }
3163
+ }
3164
+ async function buildTpSlChildOrder(params) {
3165
+ const marketType = isHyperliquidSpotSymbol(params.symbol) ? "spot" : "perp";
3166
+ const [szDecimals, tick] = await Promise.all([
3167
+ fetchHyperliquidSizeDecimals({
3168
+ environment: params.environment,
3169
+ symbol: params.symbol
3170
+ }),
3171
+ fetchHyperliquidTickSize({
3172
+ environment: params.environment,
3173
+ symbol: params.symbol
3174
+ }).catch(() => null)
3175
+ ]);
3176
+ const childSide = params.parentSide === "buy" ? "sell" : "buy";
3177
+ const triggerPxNumeric = toPositiveNumber(params.leg.triggerPx, `${params.legType} triggerPx`);
3178
+ resolveTriggerDirection({
3179
+ leg: params.legType,
3180
+ parentSide: params.parentSide,
3181
+ referencePrice: params.referencePrice,
3182
+ triggerPx: triggerPxNumeric
3183
+ });
3184
+ const execution = normalizeExecutionType(params.leg.execution);
3185
+ const size = formatHyperliquidSize(toDecimalInput(params.size, "size"), szDecimals);
3186
+ const triggerPx = formatHyperliquidPrice(triggerPxNumeric, szDecimals, marketType);
3187
+ const explicitLimitPrice = params.leg.price != null ? toDecimalInput(params.leg.price, `${params.legType} price`) : null;
3188
+ const explicitLimitPriceNumeric = explicitLimitPrice != null ? toPositiveNumber(explicitLimitPrice, `${params.legType} price`) : null;
3189
+ if (execution === "limit" && explicitLimitPriceNumeric == null) {
3190
+ throw new Error(`${params.legType} limit price is required for limit execution.`);
3191
+ }
3192
+ if (execution === "limit" && explicitLimitPriceNumeric != null) {
3193
+ if (childSide === "sell" && explicitLimitPriceNumeric > triggerPxNumeric) {
3194
+ throw new Error(`${params.legType} sell limit price must be at or below the trigger price.`);
3195
+ }
3196
+ if (childSide === "buy" && explicitLimitPriceNumeric < triggerPxNumeric) {
3197
+ throw new Error(`${params.legType} buy limit price must be at or above the trigger price.`);
3198
+ }
3199
+ }
3200
+ const price = execution === "limit" ? formatHyperliquidPrice(
3201
+ explicitLimitPrice,
3202
+ szDecimals,
3203
+ marketType
3204
+ ) : formatHyperliquidMarketablePrice({
3205
+ mid: triggerPxNumeric,
3206
+ side: childSide,
3207
+ slippageBps: params.triggerMarketSlippageBps,
3208
+ tick
3209
+ });
3210
+ return {
3211
+ symbol: params.symbol,
3212
+ side: childSide,
3213
+ price,
3214
+ size,
3215
+ reduceOnly: true,
3216
+ trigger: {
3217
+ triggerPx,
3218
+ isMarket: execution === "market",
3219
+ tpsl: params.legType
3220
+ },
3221
+ ...params.leg.clientId ? { clientId: params.leg.clientId } : {}
3222
+ };
3223
+ }
3224
+ async function buildAttachedTpSlOrders(params) {
3225
+ const referencePrice = toPositiveNumber(params.referencePrice, "referencePrice");
3226
+ const legs = await Promise.all(
3227
+ [
3228
+ params.takeProfit ? buildTpSlChildOrder({
3229
+ symbol: params.symbol,
3230
+ parentSide: params.parentSide,
3231
+ size: params.size,
3232
+ referencePrice,
3233
+ legType: "tp",
3234
+ leg: params.takeProfit,
3235
+ environment: params.environment,
3236
+ triggerMarketSlippageBps: params.triggerMarketSlippageBps
3237
+ }) : null,
3238
+ params.stopLoss ? buildTpSlChildOrder({
3239
+ symbol: params.symbol,
3240
+ parentSide: params.parentSide,
3241
+ size: params.size,
3242
+ referencePrice,
3243
+ legType: "sl",
3244
+ leg: params.stopLoss,
3245
+ environment: params.environment,
3246
+ triggerMarketSlippageBps: params.triggerMarketSlippageBps
3247
+ }) : null
3248
+ ]
3249
+ );
3250
+ return legs.filter((entry) => Boolean(entry));
3251
+ }
3252
+ async function placeHyperliquidOrderWithTpSl(options) {
3253
+ const env = options.environment ?? "mainnet";
3254
+ const childOrders = await buildAttachedTpSlOrders({
3255
+ symbol: options.parent.symbol,
3256
+ parentSide: options.parent.side,
3257
+ size: options.parent.size,
3258
+ referencePrice: options.referencePrice,
3259
+ takeProfit: options.takeProfit ?? null,
3260
+ stopLoss: options.stopLoss ?? null,
3261
+ environment: env,
3262
+ triggerMarketSlippageBps: options.triggerMarketSlippageBps ?? DEFAULT_HYPERLIQUID_TPSL_MARKET_SLIPPAGE_BPS
3263
+ });
3264
+ return placeHyperliquidOrder({
3265
+ wallet: options.wallet,
3266
+ orders: [options.parent, ...childOrders],
3267
+ grouping: options.grouping ?? "normalTpsl",
3268
+ environment: env,
3269
+ ...options.vaultAddress ? { vaultAddress: options.vaultAddress } : {},
3270
+ ...typeof options.expiresAfter === "number" ? { expiresAfter: options.expiresAfter } : {},
3271
+ ...typeof options.nonce === "number" ? { nonce: options.nonce } : {},
3272
+ ...options.nonceSource ? { nonceSource: options.nonceSource } : {}
3273
+ });
3274
+ }
3275
+ async function placeHyperliquidPositionTpSl(options) {
3276
+ const env = options.environment ?? "mainnet";
3277
+ const parentSide = options.positionSide === "long" ? "buy" : "sell";
3278
+ const childOrders = await buildAttachedTpSlOrders({
3279
+ symbol: options.symbol,
3280
+ parentSide,
3281
+ size: options.size,
3282
+ referencePrice: options.referencePrice,
3283
+ takeProfit: options.takeProfit ?? null,
3284
+ stopLoss: options.stopLoss ?? null,
3285
+ environment: env,
3286
+ triggerMarketSlippageBps: options.triggerMarketSlippageBps ?? DEFAULT_HYPERLIQUID_TPSL_MARKET_SLIPPAGE_BPS
3287
+ });
3288
+ if (childOrders.length === 0) {
3289
+ throw new Error("At least one TP or SL order is required.");
3290
+ }
3291
+ return placeHyperliquidOrder({
3292
+ wallet: options.wallet,
3293
+ orders: childOrders,
3294
+ grouping: options.grouping ?? "positionTpsl",
3295
+ environment: env,
3296
+ ...options.vaultAddress ? { vaultAddress: options.vaultAddress } : {},
3297
+ ...typeof options.expiresAfter === "number" ? { expiresAfter: options.expiresAfter } : {},
3298
+ ...typeof options.nonce === "number" ? { nonce: options.nonce } : {},
3299
+ ...options.nonceSource ? { nonceSource: options.nonceSource } : {}
3300
+ });
3301
+ }
3302
+
3303
+ // src/adapters/hyperliquid/risk-utils.ts
3304
+ function toFinitePositive(value) {
3305
+ return Number.isFinite(value) && value > 0 ? value : null;
3306
+ }
3307
+ function estimateMaintenanceLeverage(maxLeverage) {
3308
+ const normalized = toFinitePositive(maxLeverage);
3309
+ if (!normalized) return null;
3310
+ return normalized * 2;
3311
+ }
3312
+ function estimateHyperliquidLiquidationPrice(params) {
3313
+ const entryPrice = toFinitePositive(params.entryPrice);
3314
+ const notionalUsd = toFinitePositive(params.notionalUsd);
3315
+ const leverage = toFinitePositive(params.leverage);
3316
+ const maintenanceLeverage = estimateMaintenanceLeverage(params.maxLeverage);
3317
+ if (!entryPrice || !notionalUsd || !leverage || !maintenanceLeverage) {
3318
+ return null;
3319
+ }
3320
+ const size = notionalUsd / entryPrice;
3321
+ if (!Number.isFinite(size) || size <= 0) {
3322
+ return null;
3323
+ }
3324
+ const isolatedMargin = notionalUsd / leverage;
3325
+ const marginAvailable = params.marginMode === "cross" ? Math.max(
3326
+ toFinitePositive(params.availableCollateralUsd ?? 0) ?? isolatedMargin,
3327
+ isolatedMargin
3328
+ ) : isolatedMargin;
3329
+ const sideSign = params.side === "buy" ? 1 : -1;
3330
+ const maintenanceFactor = 1 / maintenanceLeverage;
3331
+ const denominator = 1 - maintenanceFactor * sideSign;
3332
+ if (!Number.isFinite(denominator) || denominator <= 0) {
3333
+ return null;
3334
+ }
3335
+ const liquidationPrice = entryPrice - sideSign * (marginAvailable / size) / denominator;
3336
+ if (!Number.isFinite(liquidationPrice) || liquidationPrice <= 0) {
3337
+ return null;
3338
+ }
3339
+ return liquidationPrice;
3340
+ }
2795
3341
 
2796
3342
  // src/adapters/hyperliquid/utils.ts
2797
3343
  var DEFAULT_HYPERLIQUID_CADENCE_CRON = {
@@ -2866,7 +3412,7 @@ function resolveHyperliquidCadenceFromResolution(resolution) {
2866
3412
  }
2867
3413
 
2868
3414
  // src/adapters/hyperliquid/index.ts
2869
- function resolveRequiredNonce(params) {
3415
+ function resolveRequiredNonce2(params) {
2870
3416
  if (typeof params.nonce === "number") {
2871
3417
  return params.nonce;
2872
3418
  }
@@ -2876,7 +3422,7 @@ function resolveRequiredNonce(params) {
2876
3422
  }
2877
3423
  return resolved;
2878
3424
  }
2879
- function assertPositiveDecimalInput(value, label) {
3425
+ function assertPositiveDecimalInput2(value, label) {
2880
3426
  if (typeof value === "number") {
2881
3427
  if (!Number.isFinite(value) || value <= 0) {
2882
3428
  throw new Error(`${label} must be a positive number.`);
@@ -2916,7 +3462,7 @@ function normalizePositiveDecimalString(raw, label) {
2916
3462
  }
2917
3463
  return normalized;
2918
3464
  }
2919
- async function placeHyperliquidOrder(options) {
3465
+ async function placeHyperliquidOrder2(options) {
2920
3466
  const {
2921
3467
  wallet,
2922
3468
  orders,
@@ -2937,16 +3483,16 @@ async function placeHyperliquidOrder(options) {
2937
3483
  const resolvedBaseUrl = API_BASES[inferredEnvironment];
2938
3484
  const preparedOrders = await Promise.all(
2939
3485
  orders.map(async (intent) => {
2940
- assertPositiveDecimalInput(intent.price, "price");
2941
- assertPositiveDecimalInput(intent.size, "size");
3486
+ assertPositiveDecimalInput2(intent.price, "price");
3487
+ assertPositiveDecimalInput2(intent.size, "size");
2942
3488
  if (intent.trigger) {
2943
- assertPositiveDecimalInput(intent.trigger.triggerPx, "triggerPx");
3489
+ assertPositiveDecimalInput2(intent.trigger.triggerPx, "triggerPx");
2944
3490
  }
2945
3491
  const assetIndex = await resolveHyperliquidAssetIndex({
2946
3492
  symbol: intent.symbol,
2947
3493
  baseUrl: resolvedBaseUrl,
2948
3494
  environment: inferredEnvironment,
2949
- fetcher: fetch
3495
+ fetcher: (...args) => fetch(...args)
2950
3496
  });
2951
3497
  const limitOrTrigger = intent.trigger ? {
2952
3498
  trigger: {
@@ -2984,7 +3530,7 @@ async function placeHyperliquidOrder(options) {
2984
3530
  f: effectiveBuilder.fee
2985
3531
  };
2986
3532
  }
2987
- const effectiveNonce = resolveRequiredNonce({
3533
+ const effectiveNonce = resolveRequiredNonce2({
2988
3534
  nonce,
2989
3535
  nonceSource: options.nonceSource,
2990
3536
  wallet,
@@ -3105,7 +3651,7 @@ async function withdrawFromHyperliquid(options) {
3105
3651
  chainId: Number.parseInt(signatureChainId, 16),
3106
3652
  verifyingContract: ZERO_ADDRESS
3107
3653
  };
3108
- const nonce = resolveRequiredNonce({
3654
+ const nonce = resolveRequiredNonce2({
3109
3655
  nonce: options.nonce,
3110
3656
  nonceSource: options.nonceSource,
3111
3657
  wallet,
@@ -3191,7 +3737,7 @@ async function approveHyperliquidBuilderFee(options) {
3191
3737
  const inferredEnvironment = environment ?? "mainnet";
3192
3738
  const resolvedBaseUrl = API_BASES[inferredEnvironment];
3193
3739
  const maxFeeRate = formattedPercent;
3194
- const effectiveNonce = resolveRequiredNonce({
3740
+ const effectiveNonce = resolveRequiredNonce2({
3195
3741
  nonce,
3196
3742
  nonceSource: options.nonceSource,
3197
3743
  wallet,
@@ -3314,6 +3860,6 @@ var __hyperliquidInternals = {
3314
3860
  splitSignature
3315
3861
  };
3316
3862
 
3317
- export { DEFAULT_HYPERLIQUID_CADENCE_CRON, DEFAULT_HYPERLIQUID_MARKET_SLIPPAGE_BPS, HyperliquidApiError, HyperliquidBuilderApprovalError, HyperliquidExchangeClient, HyperliquidGuardError, HyperliquidInfoClient, HyperliquidTermsError, __hyperliquidInternals, __hyperliquidMarketDataInternals, approveHyperliquidBuilderFee, batchModifyHyperliquidOrders, buildHyperliquidMarketIdentity, buildHyperliquidProfileAssets, buildHyperliquidSpotUsdPriceMap, cancelAllHyperliquidOrders, cancelHyperliquidOrders, cancelHyperliquidOrdersByCloid, cancelHyperliquidTwapOrder, clampHyperliquidAbs, clampHyperliquidFloat, clampHyperliquidInt, computeHyperliquidMarketIocLimitPrice, createHyperliquidSubAccount, createMonotonicNonceFactory, depositToHyperliquidBridge, extractHyperliquidDex, extractHyperliquidOrderIds, fetchHyperliquidAllMids, fetchHyperliquidAssetCtxs, fetchHyperliquidBars, fetchHyperliquidClearinghouseState, fetchHyperliquidFrontendOpenOrders, fetchHyperliquidHistoricalOrders, fetchHyperliquidMeta, fetchHyperliquidMetaAndAssetCtxs, fetchHyperliquidOpenOrders, fetchHyperliquidOrderStatus, fetchHyperliquidPerpMarketInfo, fetchHyperliquidPreTransferCheck, fetchHyperliquidSizeDecimals, fetchHyperliquidSpotAccountValue, fetchHyperliquidSpotAssetCtxs, fetchHyperliquidSpotClearinghouseState, fetchHyperliquidSpotMarketInfo, fetchHyperliquidSpotMeta, fetchHyperliquidSpotMetaAndAssetCtxs, fetchHyperliquidSpotTickSize, fetchHyperliquidSpotUsdPriceMap, fetchHyperliquidTickSize, fetchHyperliquidUserFills, fetchHyperliquidUserFillsByTime, fetchHyperliquidUserRateLimit, formatHyperliquidMarketablePrice, formatHyperliquidOrderSize, formatHyperliquidPrice, formatHyperliquidSize, getHyperliquidMaxBuilderFee, isHyperliquidSpotSymbol, modifyHyperliquidOrder, normalizeHyperliquidBaseSymbol, normalizeHyperliquidDcaEntries, normalizeHyperliquidIndicatorBars, normalizeHyperliquidMetaSymbol, normalizeSpotTokenName2 as normalizeSpotTokenName, parseHyperliquidJson, parseSpotPairSymbol, placeHyperliquidOrder, placeHyperliquidTwapOrder, planHyperliquidTrade, readHyperliquidAccountValue, readHyperliquidNumber, readHyperliquidPerpPosition, readHyperliquidPerpPositionSize, readHyperliquidSpotAccountValue, readHyperliquidSpotBalance, readHyperliquidSpotBalanceSize, recordHyperliquidBuilderApproval, recordHyperliquidTermsAcceptance, reserveHyperliquidRequestWeight, resolveHyperliquidAbstractionFromMode, resolveHyperliquidBudgetUsd, resolveHyperliquidCadenceCron, resolveHyperliquidCadenceFromResolution, resolveHyperliquidChain, resolveHyperliquidChainConfig, resolveHyperliquidDcaSymbolEntries, resolveHyperliquidErrorDetail, resolveHyperliquidHourlyInterval, resolveHyperliquidIntervalCron, resolveHyperliquidLeverageMode, resolveHyperliquidMaxPerRunUsd, resolveHyperliquidOrderRef, resolveHyperliquidOrderSymbol, resolveHyperliquidPair, resolveHyperliquidPerpSymbol, resolveHyperliquidProfileChain, resolveHyperliquidRpcEnvVar, resolveHyperliquidScheduleEvery, resolveHyperliquidScheduleUnit, resolveHyperliquidSpotSymbol, resolveHyperliquidStoreNetwork, resolveHyperliquidSymbol, resolveHyperliquidTargetSize, resolveSpotMidCandidates, resolveSpotTokenCandidates, roundHyperliquidPriceToTick, scheduleHyperliquidCancel, sendHyperliquidSpot, setHyperliquidAccountAbstractionMode, setHyperliquidDexAbstraction, setHyperliquidPortfolioMargin, transferHyperliquidSubAccount, updateHyperliquidIsolatedMargin, updateHyperliquidLeverage, withdrawFromHyperliquid };
3863
+ export { DEFAULT_HYPERLIQUID_CADENCE_CRON, DEFAULT_HYPERLIQUID_MARKET_SLIPPAGE_BPS, DEFAULT_HYPERLIQUID_TPSL_MARKET_SLIPPAGE_BPS, HyperliquidApiError, HyperliquidBuilderApprovalError, HyperliquidExchangeClient, HyperliquidGuardError, HyperliquidInfoClient, HyperliquidTermsError, __hyperliquidInternals, __hyperliquidMarketDataInternals, approveHyperliquidBuilderFee, batchModifyHyperliquidOrders, buildHyperliquidMarketIdentity, buildHyperliquidProfileAssets, buildHyperliquidSpotUsdPriceMap, cancelAllHyperliquidOrders, cancelHyperliquidOrders, cancelHyperliquidOrdersByCloid, cancelHyperliquidTwapOrder, clampHyperliquidAbs, clampHyperliquidFloat, clampHyperliquidInt, computeHyperliquidMarketIocLimitPrice, createHyperliquidSubAccount, createMonotonicNonceFactory, depositToHyperliquidBridge, estimateHyperliquidLiquidationPrice, extractHyperliquidDex, extractHyperliquidOrderIds, fetchHyperliquidAllMids, fetchHyperliquidAssetCtxs, fetchHyperliquidBars, fetchHyperliquidClearinghouseState, fetchHyperliquidFrontendOpenOrders, fetchHyperliquidHistoricalOrders, fetchHyperliquidMeta, fetchHyperliquidMetaAndAssetCtxs, fetchHyperliquidOpenOrders, fetchHyperliquidOrderStatus, fetchHyperliquidPerpMarketInfo, fetchHyperliquidPreTransferCheck, fetchHyperliquidSizeDecimals, fetchHyperliquidSpotAccountValue, fetchHyperliquidSpotAssetCtxs, fetchHyperliquidSpotClearinghouseState, fetchHyperliquidSpotMarketInfo, fetchHyperliquidSpotMeta, fetchHyperliquidSpotMetaAndAssetCtxs, fetchHyperliquidSpotTickSize, fetchHyperliquidSpotUsdPriceMap, fetchHyperliquidTickSize, fetchHyperliquidUserFills, fetchHyperliquidUserFillsByTime, fetchHyperliquidUserRateLimit, formatHyperliquidMarketablePrice, formatHyperliquidOrderSize, formatHyperliquidPrice, formatHyperliquidSize, getHyperliquidMaxBuilderFee, isHyperliquidSpotSymbol, modifyHyperliquidOrder, normalizeHyperliquidBaseSymbol, normalizeHyperliquidDcaEntries, normalizeHyperliquidIndicatorBars, normalizeHyperliquidMetaSymbol, normalizeSpotTokenName2 as normalizeSpotTokenName, parseHyperliquidJson, parseHyperliquidSymbol, parseSpotPairSymbol, placeHyperliquidOrder2 as placeHyperliquidOrder, placeHyperliquidOrderWithTpSl, placeHyperliquidPositionTpSl, placeHyperliquidTwapOrder, planHyperliquidTrade, readHyperliquidAccountValue, readHyperliquidNumber, readHyperliquidPerpPosition, readHyperliquidPerpPositionSize, readHyperliquidSpotAccountValue, readHyperliquidSpotBalance, readHyperliquidSpotBalanceSize, recordHyperliquidBuilderApproval, recordHyperliquidTermsAcceptance, reserveHyperliquidRequestWeight, resolveHyperliquidAbstractionFromMode, resolveHyperliquidBudgetUsd, resolveHyperliquidCadenceCron, resolveHyperliquidCadenceFromResolution, resolveHyperliquidChain, resolveHyperliquidChainConfig, resolveHyperliquidDcaSymbolEntries, resolveHyperliquidErrorDetail, resolveHyperliquidHourlyInterval, resolveHyperliquidIntervalCron, resolveHyperliquidLeverageMode, resolveHyperliquidMaxPerRunUsd, resolveHyperliquidOrderRef, resolveHyperliquidOrderSymbol, resolveHyperliquidPair, resolveHyperliquidPerpSymbol, resolveHyperliquidProfileChain, resolveHyperliquidRpcEnvVar, resolveHyperliquidScheduleEvery, resolveHyperliquidScheduleUnit, resolveHyperliquidSpotSymbol, resolveHyperliquidStoreNetwork, resolveHyperliquidSymbol, resolveHyperliquidTargetSize, resolveSpotMidCandidates, resolveSpotTokenCandidates, roundHyperliquidPriceToTick, scheduleHyperliquidCancel, sendHyperliquidSpot, setHyperliquidAccountAbstractionMode, setHyperliquidDexAbstraction, setHyperliquidPortfolioMargin, transferHyperliquidSubAccount, updateHyperliquidIsolatedMargin, updateHyperliquidLeverage, withdrawFromHyperliquid };
3318
3864
  //# sourceMappingURL=index.js.map
3319
3865
  //# sourceMappingURL=index.js.map