opentool 0.14.0 → 0.15.1

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();
@@ -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 "";
@@ -1812,7 +1904,22 @@ function parseSpotPairSymbol(symbol) {
1812
1904
  return { base, quote };
1813
1905
  }
1814
1906
  function isHyperliquidSpotSymbol(symbol) {
1815
- return symbol.startsWith("@") || symbol.includes("/");
1907
+ const trimmed = symbol.trim();
1908
+ if (!trimmed) return false;
1909
+ if (trimmed.startsWith("@") || trimmed.includes("/")) return true;
1910
+ if (trimmed.includes(":")) return false;
1911
+ return resolveHyperliquidPair(trimmed) !== null;
1912
+ }
1913
+ function resolveHyperliquidMarketDataCoin(value) {
1914
+ if (!value) return null;
1915
+ const trimmed = value.trim();
1916
+ if (!trimmed) return null;
1917
+ if (trimmed.startsWith("@")) return trimmed;
1918
+ const pair = resolveHyperliquidPair(trimmed);
1919
+ if (pair && !extractHyperliquidDex(trimmed)) {
1920
+ return pair;
1921
+ }
1922
+ return trimmed;
1816
1923
  }
1817
1924
  function resolveSpotMidCandidates(baseSymbol) {
1818
1925
  const base = baseSymbol.trim().toUpperCase();
@@ -2050,20 +2157,10 @@ function planHyperliquidTrade(params) {
2050
2157
  }
2051
2158
 
2052
2159
  // 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(".");
2160
+ function countDecimalPlaces(value) {
2161
+ const [, dec = ""] = value.split(".");
2058
2162
  return dec.length;
2059
2163
  }
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
2164
  function assertNumberString(value) {
2068
2165
  if (!/^-?(?:\d+\.?\d*|\.\d+)$/.test(value)) {
2069
2166
  throw new TypeError("Invalid decimal number string.");
@@ -2114,6 +2211,29 @@ var StringMath = {
2114
2211
  const index = value.indexOf(".");
2115
2212
  return index === -1 ? value : value.slice(0, index) || "0";
2116
2213
  },
2214
+ roundInteger(value, mode) {
2215
+ const normalized = normalizeDecimalString(value);
2216
+ const negative = normalized.startsWith("-");
2217
+ if (negative) {
2218
+ throw new RangeError("Directional rounding only supports positive values.");
2219
+ }
2220
+ const [intPartRaw, fracPart = ""] = normalized.split(".");
2221
+ const intPart = intPartRaw.replace(/^0+(?=\d)/, "") || "0";
2222
+ const hasFraction = /[1-9]/.test(fracPart);
2223
+ if (!hasFraction) return intPart;
2224
+ if (mode === "down") return intPart;
2225
+ const digits = intPart.split("");
2226
+ let carry = 1;
2227
+ for (let idx = digits.length - 1; idx >= 0 && carry > 0; idx -= 1) {
2228
+ const next = Number(digits[idx] ?? "0") + carry;
2229
+ digits[idx] = String(next % 10);
2230
+ carry = next >= 10 ? 1 : 0;
2231
+ }
2232
+ if (carry > 0) {
2233
+ digits.unshift("1");
2234
+ }
2235
+ return digits.join("").replace(/^0+(?=\d)/, "") || "0";
2236
+ },
2117
2237
  toPrecisionTruncate(value, precision) {
2118
2238
  if (!Number.isInteger(precision) || precision < 1) {
2119
2239
  throw new RangeError("Precision must be a positive integer.");
@@ -2140,6 +2260,41 @@ var StringMath = {
2140
2260
  return normalizeDecimalString(result);
2141
2261
  }
2142
2262
  };
2263
+ function ceilDiv(numerator, denominator) {
2264
+ if (denominator <= 0n) {
2265
+ throw new RangeError("Denominator must be positive.");
2266
+ }
2267
+ return (numerator + denominator - 1n) / denominator;
2268
+ }
2269
+ function scaleDecimalToInt(value, decimals, mode) {
2270
+ if (!Number.isInteger(decimals) || decimals < 0) {
2271
+ throw new RangeError("Decimals must be a non-negative integer.");
2272
+ }
2273
+ const normalized = normalizeDecimalString(value);
2274
+ assertNumberString(normalized);
2275
+ const negative = normalized.startsWith("-");
2276
+ if (negative) {
2277
+ throw new RangeError("Only positive values are supported.");
2278
+ }
2279
+ const shifted = StringMath.multiplyByPow10(normalized, decimals);
2280
+ const rounded = StringMath.roundInteger(shifted, mode);
2281
+ return BigInt(rounded);
2282
+ }
2283
+ function formatScaledDecimal(value, decimals) {
2284
+ if (!Number.isInteger(decimals) || decimals < 0) {
2285
+ throw new RangeError("Decimals must be a non-negative integer.");
2286
+ }
2287
+ const negative = value < 0n;
2288
+ const abs = negative ? -value : value;
2289
+ const raw = abs.toString();
2290
+ if (decimals === 0) {
2291
+ return `${negative ? "-" : ""}${raw}`;
2292
+ }
2293
+ const padded = raw.padStart(decimals + 1, "0");
2294
+ const intPart = padded.slice(0, -decimals) || "0";
2295
+ const fracPart = padded.slice(-decimals);
2296
+ return normalizeDecimalString(`${negative ? "-" : ""}${intPart}.${fracPart}`);
2297
+ }
2143
2298
  function formatHyperliquidPrice(price, szDecimals, marketType = "perp") {
2144
2299
  const normalized = price.toString().trim();
2145
2300
  assertNumberString(normalized);
@@ -2172,33 +2327,52 @@ function formatHyperliquidOrderSize(value, szDecimals) {
2172
2327
  }
2173
2328
  }
2174
2329
  function roundHyperliquidPriceToTick(price, tick, side) {
2175
- if (!Number.isFinite(price) || price <= 0) {
2176
- throw new Error("Price must be positive.");
2177
- }
2178
2330
  if (!Number.isFinite(tick.tickDecimals) || tick.tickDecimals < 0) {
2179
2331
  throw new Error("tick.tickDecimals must be a non-negative number.");
2180
2332
  }
2181
2333
  if (tick.tickSizeInt <= 0n) {
2182
2334
  throw new Error("tick.tickSizeInt must be positive.");
2183
2335
  }
2184
- const scale = 10 ** tick.tickDecimals;
2185
- const scaled = BigInt(Math.round(price * scale));
2336
+ const normalized = normalizeDecimalString(price.toString());
2337
+ assertNumberString(normalized);
2338
+ if (Number.parseFloat(normalized) <= 0) {
2339
+ throw new Error("Price must be positive.");
2340
+ }
2341
+ const scaled = scaleDecimalToInt(
2342
+ normalized,
2343
+ tick.tickDecimals,
2344
+ side === "buy" ? "up" : "down"
2345
+ );
2186
2346
  const tickSize = tick.tickSizeInt;
2187
2347
  const rounded = side === "sell" ? scaled / tickSize * tickSize : (scaled + tickSize - 1n) / tickSize * tickSize;
2188
- const integer = Number(rounded) / scale;
2189
- return clampPriceDecimals(integer);
2348
+ return formatScaledDecimal(rounded, tick.tickDecimals);
2190
2349
  }
2191
2350
  function formatHyperliquidMarketablePrice(params) {
2192
2351
  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);
2352
+ if (!Number.isFinite(mid) || mid <= 0) {
2353
+ throw new Error("mid must be a positive number.");
2354
+ }
2355
+ if (!Number.isFinite(slippageBps) || slippageBps < 0) {
2356
+ throw new Error("slippageBps must be a non-negative number.");
2357
+ }
2358
+ const midString = normalizeDecimalString(mid.toString());
2359
+ const baseDecimals = countDecimalPlaces(midString);
2360
+ const workDecimals = Math.max(baseDecimals + 4, tick?.tickDecimals ?? 0, 8);
2361
+ const scaledMid = scaleDecimalToInt(midString, workDecimals, "down");
2362
+ const slippageNumerator = BigInt(
2363
+ side === "buy" ? 1e4 + slippageBps : 1e4 - slippageBps
2364
+ );
2365
+ const adjustedScaled = side === "buy" ? ceilDiv(scaledMid * slippageNumerator, 10000n) : scaledMid * slippageNumerator / 10000n;
2366
+ const adjusted = formatScaledDecimal(adjustedScaled, workDecimals);
2196
2367
  if (tick) {
2197
2368
  return roundHyperliquidPriceToTick(adjusted, tick, side);
2198
2369
  }
2199
- const scaled = adjusted * factor;
2200
- const rounded = side === "buy" ? Math.ceil(scaled) / factor : Math.floor(scaled) / factor;
2201
- return clampPriceDecimals(rounded);
2370
+ const roundedScaled = scaleDecimalToInt(
2371
+ adjusted,
2372
+ baseDecimals,
2373
+ side === "buy" ? "up" : "down"
2374
+ );
2375
+ return formatScaledDecimal(roundedScaled, baseDecimals);
2202
2376
  }
2203
2377
  function extractHyperliquidOrderIds(responses) {
2204
2378
  const cloids = /* @__PURE__ */ new Set();
@@ -2792,6 +2966,393 @@ var __hyperliquidMarketDataInternals = {
2792
2966
  toScaledInt,
2793
2967
  formatScaledInt
2794
2968
  };
2969
+ function resolveRequiredNonce(params) {
2970
+ if (typeof params.nonce === "number") {
2971
+ return params.nonce;
2972
+ }
2973
+ const resolved = params.nonceSource?.() ?? params.wallet?.nonceSource?.();
2974
+ if (resolved === void 0) {
2975
+ throw new Error(`${params.action} requires an explicit nonce or wallet nonce source.`);
2976
+ }
2977
+ return resolved;
2978
+ }
2979
+ function assertPositiveDecimalInput(value, label) {
2980
+ if (typeof value === "number") {
2981
+ if (!Number.isFinite(value) || value <= 0) {
2982
+ throw new Error(`${label} must be a positive number.`);
2983
+ }
2984
+ return;
2985
+ }
2986
+ if (typeof value === "bigint") {
2987
+ if (value <= 0n) {
2988
+ throw new Error(`${label} must be positive.`);
2989
+ }
2990
+ return;
2991
+ }
2992
+ const trimmed = value.trim();
2993
+ if (!trimmed.length) {
2994
+ throw new Error(`${label} must be a non-empty string.`);
2995
+ }
2996
+ if (!/^(?:\d+\.?\d*|\.\d+)$/.test(trimmed)) {
2997
+ throw new Error(`${label} must be a positive decimal string.`);
2998
+ }
2999
+ const numeric = Number(trimmed);
3000
+ if (!Number.isFinite(numeric) || numeric <= 0) {
3001
+ throw new Error(`${label} must be positive.`);
3002
+ }
3003
+ }
3004
+ async function placeHyperliquidOrder(options) {
3005
+ const {
3006
+ wallet,
3007
+ orders,
3008
+ grouping = "na",
3009
+ environment,
3010
+ vaultAddress,
3011
+ expiresAfter,
3012
+ nonce
3013
+ } = options;
3014
+ if (!wallet?.account || !wallet.walletClient) {
3015
+ throw new Error("Hyperliquid order signing requires a wallet with signing capabilities.");
3016
+ }
3017
+ if (!orders.length) {
3018
+ throw new Error("At least one order is required.");
3019
+ }
3020
+ const inferredEnvironment = environment ?? "mainnet";
3021
+ const resolvedBaseUrl = API_BASES[inferredEnvironment];
3022
+ const preparedOrders = await Promise.all(
3023
+ orders.map(async (intent) => {
3024
+ assertPositiveDecimalInput(intent.price, "price");
3025
+ assertPositiveDecimalInput(intent.size, "size");
3026
+ if (intent.trigger) {
3027
+ assertPositiveDecimalInput(intent.trigger.triggerPx, "triggerPx");
3028
+ }
3029
+ const assetIndex = await resolveHyperliquidAssetIndex({
3030
+ symbol: intent.symbol,
3031
+ baseUrl: resolvedBaseUrl,
3032
+ environment: inferredEnvironment,
3033
+ fetcher: (...args) => fetch(...args)
3034
+ });
3035
+ const order = {
3036
+ a: assetIndex,
3037
+ b: intent.side === "buy",
3038
+ p: toApiDecimal(intent.price),
3039
+ s: toApiDecimal(intent.size),
3040
+ r: intent.reduceOnly ?? false,
3041
+ t: intent.trigger ? {
3042
+ trigger: {
3043
+ isMarket: Boolean(intent.trigger.isMarket),
3044
+ triggerPx: toApiDecimal(intent.trigger.triggerPx),
3045
+ tpsl: intent.trigger.tpsl
3046
+ }
3047
+ } : {
3048
+ limit: {
3049
+ tif: intent.tif ?? "Ioc"
3050
+ }
3051
+ },
3052
+ ...intent.clientId ? { c: normalizeCloid(intent.clientId) } : {}
3053
+ };
3054
+ return order;
3055
+ })
3056
+ );
3057
+ const action = {
3058
+ type: "order",
3059
+ orders: preparedOrders,
3060
+ grouping,
3061
+ builder: {
3062
+ b: normalizeAddress(BUILDER_CODE.address),
3063
+ f: BUILDER_CODE.fee
3064
+ }
3065
+ };
3066
+ const effectiveNonce = resolveRequiredNonce({
3067
+ nonce,
3068
+ nonceSource: options.nonceSource,
3069
+ wallet,
3070
+ action: "Hyperliquid order submission"
3071
+ });
3072
+ const signature = await signL1Action({
3073
+ wallet,
3074
+ action,
3075
+ nonce: effectiveNonce,
3076
+ ...vaultAddress ? { vaultAddress } : {},
3077
+ ...typeof expiresAfter === "number" ? { expiresAfter } : {},
3078
+ isTestnet: inferredEnvironment === "testnet"
3079
+ });
3080
+ const body = {
3081
+ action,
3082
+ nonce: effectiveNonce,
3083
+ signature
3084
+ };
3085
+ if (vaultAddress) {
3086
+ body.vaultAddress = normalizeAddress(vaultAddress);
3087
+ }
3088
+ if (typeof expiresAfter === "number") {
3089
+ body.expiresAfter = expiresAfter;
3090
+ }
3091
+ const response = await fetch(`${resolvedBaseUrl}/exchange`, {
3092
+ method: "POST",
3093
+ headers: { "content-type": "application/json" },
3094
+ body: JSON.stringify(body)
3095
+ });
3096
+ const rawText = await response.text().catch(() => null);
3097
+ let parsed = null;
3098
+ if (rawText && rawText.length) {
3099
+ try {
3100
+ parsed = JSON.parse(rawText);
3101
+ } catch {
3102
+ parsed = rawText;
3103
+ }
3104
+ }
3105
+ const json = parsed && typeof parsed === "object" && "status" in parsed ? parsed : null;
3106
+ if (!response.ok || !json) {
3107
+ const detail = parsed?.error ?? parsed?.message ?? (typeof parsed === "string" ? parsed : rawText);
3108
+ const suffix = detail ? ` Detail: ${detail}` : "";
3109
+ throw new HyperliquidApiError(
3110
+ `Failed to submit Hyperliquid order.${suffix}`,
3111
+ parsed ?? rawText ?? { status: response.status }
3112
+ );
3113
+ }
3114
+ if (json.status !== "ok") {
3115
+ const detail = parsed?.error ?? rawText;
3116
+ throw new HyperliquidApiError(
3117
+ detail ? `Hyperliquid API returned an error status: ${detail}` : "Hyperliquid API returned an error status.",
3118
+ json
3119
+ );
3120
+ }
3121
+ const statuses = json.response?.data?.statuses ?? [];
3122
+ const errorStatuses = statuses.filter(
3123
+ (entry) => Boolean(
3124
+ entry && typeof entry === "object" && "error" in entry && typeof entry.error === "string"
3125
+ )
3126
+ );
3127
+ if (errorStatuses.length) {
3128
+ const message = errorStatuses.map((entry) => entry.error).join(", ");
3129
+ throw new HyperliquidApiError(message || "Hyperliquid rejected the order.", json);
3130
+ }
3131
+ return json;
3132
+ }
3133
+
3134
+ // src/adapters/hyperliquid/tpsl.ts
3135
+ var DEFAULT_HYPERLIQUID_TPSL_MARKET_SLIPPAGE_BPS = 1e3;
3136
+ function toDecimalInput(value, label) {
3137
+ if (typeof value === "bigint") {
3138
+ if (value <= 0n) {
3139
+ throw new Error(`${label} must be positive.`);
3140
+ }
3141
+ return value.toString();
3142
+ }
3143
+ return value;
3144
+ }
3145
+ function toPositiveNumber(value, label) {
3146
+ if (typeof value === "bigint") {
3147
+ if (value <= 0n) {
3148
+ throw new Error(`${label} must be positive.`);
3149
+ }
3150
+ return Number(value);
3151
+ }
3152
+ const numeric = typeof value === "number" ? value : Number.parseFloat(value.toString().trim());
3153
+ if (!Number.isFinite(numeric) || numeric <= 0) {
3154
+ throw new Error(`${label} must be positive.`);
3155
+ }
3156
+ return numeric;
3157
+ }
3158
+ function normalizeExecutionType(value) {
3159
+ return value ?? "market";
3160
+ }
3161
+ function resolveTriggerDirection(params) {
3162
+ const isLong = params.parentSide === "buy";
3163
+ if (params.leg === "tp") {
3164
+ if (isLong && params.triggerPx <= params.referencePrice) {
3165
+ throw new Error("Take profit trigger must be above the current price for long positions.");
3166
+ }
3167
+ if (!isLong && params.triggerPx >= params.referencePrice) {
3168
+ throw new Error("Take profit trigger must be below the current price for short positions.");
3169
+ }
3170
+ return;
3171
+ }
3172
+ if (isLong && params.triggerPx >= params.referencePrice) {
3173
+ throw new Error("Stop loss trigger must be below the current price for long positions.");
3174
+ }
3175
+ if (!isLong && params.triggerPx <= params.referencePrice) {
3176
+ throw new Error("Stop loss trigger must be above the current price for short positions.");
3177
+ }
3178
+ }
3179
+ async function buildTpSlChildOrder(params) {
3180
+ const marketType = isHyperliquidSpotSymbol(params.symbol) ? "spot" : "perp";
3181
+ const [szDecimals, tick] = await Promise.all([
3182
+ fetchHyperliquidSizeDecimals({
3183
+ environment: params.environment,
3184
+ symbol: params.symbol
3185
+ }),
3186
+ fetchHyperliquidTickSize({
3187
+ environment: params.environment,
3188
+ symbol: params.symbol
3189
+ }).catch(() => null)
3190
+ ]);
3191
+ const childSide = params.parentSide === "buy" ? "sell" : "buy";
3192
+ const triggerPxNumeric = toPositiveNumber(params.leg.triggerPx, `${params.legType} triggerPx`);
3193
+ resolveTriggerDirection({
3194
+ leg: params.legType,
3195
+ parentSide: params.parentSide,
3196
+ referencePrice: params.referencePrice,
3197
+ triggerPx: triggerPxNumeric
3198
+ });
3199
+ const execution = normalizeExecutionType(params.leg.execution);
3200
+ const size = formatHyperliquidSize(toDecimalInput(params.size, "size"), szDecimals);
3201
+ const triggerPx = formatHyperliquidPrice(triggerPxNumeric, szDecimals, marketType);
3202
+ const explicitLimitPrice = params.leg.price != null ? toDecimalInput(params.leg.price, `${params.legType} price`) : null;
3203
+ const explicitLimitPriceNumeric = explicitLimitPrice != null ? toPositiveNumber(explicitLimitPrice, `${params.legType} price`) : null;
3204
+ if (execution === "limit" && explicitLimitPriceNumeric == null) {
3205
+ throw new Error(`${params.legType} limit price is required for limit execution.`);
3206
+ }
3207
+ if (execution === "limit" && explicitLimitPriceNumeric != null) {
3208
+ if (childSide === "sell" && explicitLimitPriceNumeric > triggerPxNumeric) {
3209
+ throw new Error(`${params.legType} sell limit price must be at or below the trigger price.`);
3210
+ }
3211
+ if (childSide === "buy" && explicitLimitPriceNumeric < triggerPxNumeric) {
3212
+ throw new Error(`${params.legType} buy limit price must be at or above the trigger price.`);
3213
+ }
3214
+ }
3215
+ const price = execution === "limit" ? formatHyperliquidPrice(
3216
+ explicitLimitPrice,
3217
+ szDecimals,
3218
+ marketType
3219
+ ) : formatHyperliquidMarketablePrice({
3220
+ mid: triggerPxNumeric,
3221
+ side: childSide,
3222
+ slippageBps: params.triggerMarketSlippageBps,
3223
+ tick
3224
+ });
3225
+ return {
3226
+ symbol: params.symbol,
3227
+ side: childSide,
3228
+ price,
3229
+ size,
3230
+ reduceOnly: true,
3231
+ trigger: {
3232
+ triggerPx,
3233
+ isMarket: execution === "market",
3234
+ tpsl: params.legType
3235
+ },
3236
+ ...params.leg.clientId ? { clientId: params.leg.clientId } : {}
3237
+ };
3238
+ }
3239
+ async function buildAttachedTpSlOrders(params) {
3240
+ const referencePrice = toPositiveNumber(params.referencePrice, "referencePrice");
3241
+ const legs = await Promise.all(
3242
+ [
3243
+ params.takeProfit ? buildTpSlChildOrder({
3244
+ symbol: params.symbol,
3245
+ parentSide: params.parentSide,
3246
+ size: params.size,
3247
+ referencePrice,
3248
+ legType: "tp",
3249
+ leg: params.takeProfit,
3250
+ environment: params.environment,
3251
+ triggerMarketSlippageBps: params.triggerMarketSlippageBps
3252
+ }) : null,
3253
+ params.stopLoss ? buildTpSlChildOrder({
3254
+ symbol: params.symbol,
3255
+ parentSide: params.parentSide,
3256
+ size: params.size,
3257
+ referencePrice,
3258
+ legType: "sl",
3259
+ leg: params.stopLoss,
3260
+ environment: params.environment,
3261
+ triggerMarketSlippageBps: params.triggerMarketSlippageBps
3262
+ }) : null
3263
+ ]
3264
+ );
3265
+ return legs.filter((entry) => Boolean(entry));
3266
+ }
3267
+ async function placeHyperliquidOrderWithTpSl(options) {
3268
+ const env = options.environment ?? "mainnet";
3269
+ const childOrders = await buildAttachedTpSlOrders({
3270
+ symbol: options.parent.symbol,
3271
+ parentSide: options.parent.side,
3272
+ size: options.parent.size,
3273
+ referencePrice: options.referencePrice,
3274
+ takeProfit: options.takeProfit ?? null,
3275
+ stopLoss: options.stopLoss ?? null,
3276
+ environment: env,
3277
+ triggerMarketSlippageBps: options.triggerMarketSlippageBps ?? DEFAULT_HYPERLIQUID_TPSL_MARKET_SLIPPAGE_BPS
3278
+ });
3279
+ return placeHyperliquidOrder({
3280
+ wallet: options.wallet,
3281
+ orders: [options.parent, ...childOrders],
3282
+ grouping: options.grouping ?? "normalTpsl",
3283
+ environment: env,
3284
+ ...options.vaultAddress ? { vaultAddress: options.vaultAddress } : {},
3285
+ ...typeof options.expiresAfter === "number" ? { expiresAfter: options.expiresAfter } : {},
3286
+ ...typeof options.nonce === "number" ? { nonce: options.nonce } : {},
3287
+ ...options.nonceSource ? { nonceSource: options.nonceSource } : {}
3288
+ });
3289
+ }
3290
+ async function placeHyperliquidPositionTpSl(options) {
3291
+ const env = options.environment ?? "mainnet";
3292
+ const parentSide = options.positionSide === "long" ? "buy" : "sell";
3293
+ const childOrders = await buildAttachedTpSlOrders({
3294
+ symbol: options.symbol,
3295
+ parentSide,
3296
+ size: options.size,
3297
+ referencePrice: options.referencePrice,
3298
+ takeProfit: options.takeProfit ?? null,
3299
+ stopLoss: options.stopLoss ?? null,
3300
+ environment: env,
3301
+ triggerMarketSlippageBps: options.triggerMarketSlippageBps ?? DEFAULT_HYPERLIQUID_TPSL_MARKET_SLIPPAGE_BPS
3302
+ });
3303
+ if (childOrders.length === 0) {
3304
+ throw new Error("At least one TP or SL order is required.");
3305
+ }
3306
+ return placeHyperliquidOrder({
3307
+ wallet: options.wallet,
3308
+ orders: childOrders,
3309
+ grouping: options.grouping ?? "positionTpsl",
3310
+ environment: env,
3311
+ ...options.vaultAddress ? { vaultAddress: options.vaultAddress } : {},
3312
+ ...typeof options.expiresAfter === "number" ? { expiresAfter: options.expiresAfter } : {},
3313
+ ...typeof options.nonce === "number" ? { nonce: options.nonce } : {},
3314
+ ...options.nonceSource ? { nonceSource: options.nonceSource } : {}
3315
+ });
3316
+ }
3317
+
3318
+ // src/adapters/hyperliquid/risk-utils.ts
3319
+ function toFinitePositive(value) {
3320
+ return Number.isFinite(value) && value > 0 ? value : null;
3321
+ }
3322
+ function estimateMaintenanceLeverage(maxLeverage) {
3323
+ const normalized = toFinitePositive(maxLeverage);
3324
+ if (!normalized) return null;
3325
+ return normalized * 2;
3326
+ }
3327
+ function estimateHyperliquidLiquidationPrice(params) {
3328
+ const entryPrice = toFinitePositive(params.entryPrice);
3329
+ const notionalUsd = toFinitePositive(params.notionalUsd);
3330
+ const leverage = toFinitePositive(params.leverage);
3331
+ const maintenanceLeverage = estimateMaintenanceLeverage(params.maxLeverage);
3332
+ if (!entryPrice || !notionalUsd || !leverage || !maintenanceLeverage) {
3333
+ return null;
3334
+ }
3335
+ const size = notionalUsd / entryPrice;
3336
+ if (!Number.isFinite(size) || size <= 0) {
3337
+ return null;
3338
+ }
3339
+ const isolatedMargin = notionalUsd / leverage;
3340
+ const marginAvailable = params.marginMode === "cross" ? Math.max(
3341
+ toFinitePositive(params.availableCollateralUsd ?? 0) ?? isolatedMargin,
3342
+ isolatedMargin
3343
+ ) : isolatedMargin;
3344
+ const sideSign = params.side === "buy" ? 1 : -1;
3345
+ const maintenanceFactor = 1 / maintenanceLeverage;
3346
+ const denominator = 1 - maintenanceFactor * sideSign;
3347
+ if (!Number.isFinite(denominator) || denominator <= 0) {
3348
+ return null;
3349
+ }
3350
+ const liquidationPrice = entryPrice - sideSign * (marginAvailable / size) / denominator;
3351
+ if (!Number.isFinite(liquidationPrice) || liquidationPrice <= 0) {
3352
+ return null;
3353
+ }
3354
+ return liquidationPrice;
3355
+ }
2795
3356
 
2796
3357
  // src/adapters/hyperliquid/utils.ts
2797
3358
  var DEFAULT_HYPERLIQUID_CADENCE_CRON = {
@@ -2866,7 +3427,7 @@ function resolveHyperliquidCadenceFromResolution(resolution) {
2866
3427
  }
2867
3428
 
2868
3429
  // src/adapters/hyperliquid/index.ts
2869
- function resolveRequiredNonce(params) {
3430
+ function resolveRequiredNonce2(params) {
2870
3431
  if (typeof params.nonce === "number") {
2871
3432
  return params.nonce;
2872
3433
  }
@@ -2876,7 +3437,7 @@ function resolveRequiredNonce(params) {
2876
3437
  }
2877
3438
  return resolved;
2878
3439
  }
2879
- function assertPositiveDecimalInput(value, label) {
3440
+ function assertPositiveDecimalInput2(value, label) {
2880
3441
  if (typeof value === "number") {
2881
3442
  if (!Number.isFinite(value) || value <= 0) {
2882
3443
  throw new Error(`${label} must be a positive number.`);
@@ -2916,7 +3477,7 @@ function normalizePositiveDecimalString(raw, label) {
2916
3477
  }
2917
3478
  return normalized;
2918
3479
  }
2919
- async function placeHyperliquidOrder(options) {
3480
+ async function placeHyperliquidOrder2(options) {
2920
3481
  const {
2921
3482
  wallet,
2922
3483
  orders,
@@ -2937,10 +3498,10 @@ async function placeHyperliquidOrder(options) {
2937
3498
  const resolvedBaseUrl = API_BASES[inferredEnvironment];
2938
3499
  const preparedOrders = await Promise.all(
2939
3500
  orders.map(async (intent) => {
2940
- assertPositiveDecimalInput(intent.price, "price");
2941
- assertPositiveDecimalInput(intent.size, "size");
3501
+ assertPositiveDecimalInput2(intent.price, "price");
3502
+ assertPositiveDecimalInput2(intent.size, "size");
2942
3503
  if (intent.trigger) {
2943
- assertPositiveDecimalInput(intent.trigger.triggerPx, "triggerPx");
3504
+ assertPositiveDecimalInput2(intent.trigger.triggerPx, "triggerPx");
2944
3505
  }
2945
3506
  const assetIndex = await resolveHyperliquidAssetIndex({
2946
3507
  symbol: intent.symbol,
@@ -2984,7 +3545,7 @@ async function placeHyperliquidOrder(options) {
2984
3545
  f: effectiveBuilder.fee
2985
3546
  };
2986
3547
  }
2987
- const effectiveNonce = resolveRequiredNonce({
3548
+ const effectiveNonce = resolveRequiredNonce2({
2988
3549
  nonce,
2989
3550
  nonceSource: options.nonceSource,
2990
3551
  wallet,
@@ -3105,7 +3666,7 @@ async function withdrawFromHyperliquid(options) {
3105
3666
  chainId: Number.parseInt(signatureChainId, 16),
3106
3667
  verifyingContract: ZERO_ADDRESS
3107
3668
  };
3108
- const nonce = resolveRequiredNonce({
3669
+ const nonce = resolveRequiredNonce2({
3109
3670
  nonce: options.nonce,
3110
3671
  nonceSource: options.nonceSource,
3111
3672
  wallet,
@@ -3191,7 +3752,7 @@ async function approveHyperliquidBuilderFee(options) {
3191
3752
  const inferredEnvironment = environment ?? "mainnet";
3192
3753
  const resolvedBaseUrl = API_BASES[inferredEnvironment];
3193
3754
  const maxFeeRate = formattedPercent;
3194
- const effectiveNonce = resolveRequiredNonce({
3755
+ const effectiveNonce = resolveRequiredNonce2({
3195
3756
  nonce,
3196
3757
  nonceSource: options.nonceSource,
3197
3758
  wallet,
@@ -3314,6 +3875,6 @@ var __hyperliquidInternals = {
3314
3875
  splitSignature
3315
3876
  };
3316
3877
 
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 };
3878
+ 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, resolveHyperliquidMarketDataCoin, 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
3879
  //# sourceMappingURL=index.js.map
3319
3880
  //# sourceMappingURL=index.js.map