opentool 0.8.23 → 0.8.25

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.
@@ -6,6 +6,29 @@ import { hexToBytes, concatBytes, bytesToHex } from '@noble/hashes/utils';
6
6
  // src/adapters/hyperliquid/index.ts
7
7
 
8
8
  // src/store/index.ts
9
+ var STORE_EVENT_LEVELS = [
10
+ "decision",
11
+ "execution",
12
+ "lifecycle"
13
+ ];
14
+ var STORE_EVENT_LEVEL_SET = new Set(STORE_EVENT_LEVELS);
15
+ var MARKET_REQUIRED_ACTIONS = [
16
+ "swap",
17
+ "bridge",
18
+ "order",
19
+ "trade",
20
+ "lend",
21
+ "borrow",
22
+ "repay",
23
+ "stake",
24
+ "unstake",
25
+ "withdraw",
26
+ "provide_liquidity",
27
+ "remove_liquidity",
28
+ "claim"
29
+ ];
30
+ var MARKET_REQUIRED_ACTIONS_SET = new Set(MARKET_REQUIRED_ACTIONS);
31
+ var EXECUTION_ACTIONS_SET = new Set(MARKET_REQUIRED_ACTIONS);
9
32
  var StoreError = class extends Error {
10
33
  constructor(message, status, causeData) {
11
34
  super(message);
@@ -14,13 +37,57 @@ var StoreError = class extends Error {
14
37
  this.name = "StoreError";
15
38
  }
16
39
  };
40
+ var normalizeAction = (action) => {
41
+ const normalized = action?.trim().toLowerCase();
42
+ return normalized ? normalized : null;
43
+ };
44
+ var coerceEventLevel = (value) => {
45
+ if (typeof value !== "string") return null;
46
+ const normalized = value.trim().toLowerCase();
47
+ if (!normalized || !STORE_EVENT_LEVEL_SET.has(normalized)) return null;
48
+ return normalized;
49
+ };
17
50
  var requiresMarketIdentity = (input) => {
18
- const action = (input.action ?? "").toLowerCase();
19
- if (action === "order" || action === "swap" || action === "trade") return true;
20
- return false;
51
+ const action = normalizeAction(input.action);
52
+ if (!action) return false;
53
+ return MARKET_REQUIRED_ACTIONS_SET.has(action);
21
54
  };
22
55
  var hasMarketIdentity = (value) => {
23
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
56
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
57
+ const record = value;
58
+ const requiredKeys = ["market_type", "venue", "environment", "canonical_symbol"];
59
+ return requiredKeys.every((key) => {
60
+ const field = record[key];
61
+ return typeof field === "string" && field.trim().length > 0;
62
+ });
63
+ };
64
+ var resolveEventLevel = (input) => {
65
+ const direct = coerceEventLevel(input.eventLevel);
66
+ if (direct) return direct;
67
+ const metadataLevel = coerceEventLevel(input.metadata?.eventLevel);
68
+ if (metadataLevel) return metadataLevel;
69
+ const action = normalizeAction(input.action);
70
+ if (action && EXECUTION_ACTIONS_SET.has(action) && (input.metadata?.lifecycle === true || typeof input.metadata?.executionRef === "string" || typeof input.metadata?.parentExecutionRef === "string")) {
71
+ return "lifecycle";
72
+ }
73
+ if (action && EXECUTION_ACTIONS_SET.has(action) || hasMarketIdentity(input.market)) {
74
+ return "execution";
75
+ }
76
+ if (action) return "decision";
77
+ return null;
78
+ };
79
+ var normalizeStoreInput = (input) => {
80
+ const metadata = { ...input.metadata ?? {} };
81
+ const eventLevel = resolveEventLevel({ ...input, metadata });
82
+ if (eventLevel) {
83
+ metadata.eventLevel = eventLevel;
84
+ }
85
+ const hasMetadata = Object.keys(metadata).length > 0;
86
+ return {
87
+ ...input,
88
+ ...eventLevel ? { eventLevel } : {},
89
+ ...hasMetadata ? { metadata } : {}
90
+ };
24
91
  };
25
92
  function resolveConfig(options) {
26
93
  const baseUrl = options?.baseUrl ?? process.env.BASE_URL ?? "https://api.openpond.ai";
@@ -41,8 +108,26 @@ function resolveConfig(options) {
41
108
  return { baseUrl: normalizedBaseUrl, apiKey, fetchFn };
42
109
  }
43
110
  async function store(input, options) {
44
- if (requiresMarketIdentity(input) && !hasMarketIdentity(input.market)) {
45
- throw new StoreError("market is required for trade events");
111
+ const normalizedInput = normalizeStoreInput(input);
112
+ const eventLevel = normalizedInput.eventLevel;
113
+ const normalizedAction = normalizeAction(normalizedInput.action);
114
+ if (eventLevel === "execution" || eventLevel === "lifecycle") {
115
+ if (!normalizedAction || !EXECUTION_ACTIONS_SET.has(normalizedAction)) {
116
+ throw new StoreError(
117
+ `eventLevel "${eventLevel}" requires an execution action`
118
+ );
119
+ }
120
+ }
121
+ if (eventLevel === "execution" && !hasMarketIdentity(normalizedInput.market)) {
122
+ throw new StoreError(
123
+ `market is required for execution events. market must include market_type, venue, environment, canonical_symbol`
124
+ );
125
+ }
126
+ const shouldApplyLegacyMarketRule = eventLevel == null || eventLevel === "execution";
127
+ if (shouldApplyLegacyMarketRule && requiresMarketIdentity(normalizedInput) && !hasMarketIdentity(normalizedInput.market)) {
128
+ throw new StoreError(
129
+ `market is required for action "${normalizedInput.action}". market must include market_type, venue, environment, canonical_symbol`
130
+ );
46
131
  }
47
132
  const { baseUrl, apiKey, fetchFn } = resolveConfig(options);
48
133
  const url = `${baseUrl}/apps/positions/tx`;
@@ -54,7 +139,7 @@ async function store(input, options) {
54
139
  "content-type": "application/json",
55
140
  "openpond-api-key": apiKey
56
141
  },
57
- body: JSON.stringify(input)
142
+ body: JSON.stringify(normalizedInput)
58
143
  });
59
144
  } catch (error) {
60
145
  throw new StoreError("Failed to reach store endpoint", void 0, error);
@@ -493,12 +578,29 @@ function toApiDecimal(value) {
493
578
  }
494
579
  return asString;
495
580
  }
581
+ var NORMALIZED_HEX_PATTERN = /^0x[0-9a-f]+$/;
582
+ var ADDRESS_HEX_LENGTH = 42;
583
+ var CLOID_HEX_LENGTH = 34;
496
584
  function normalizeHex(value) {
497
- const lower = value.toLowerCase();
498
- return lower.replace(/^0x0+/, "0x") || "0x0";
585
+ const lower = value.trim().toLowerCase();
586
+ if (!NORMALIZED_HEX_PATTERN.test(lower)) {
587
+ throw new Error(`Invalid hex value: ${value}`);
588
+ }
589
+ return lower;
499
590
  }
500
591
  function normalizeAddress(value) {
501
- return normalizeHex(value);
592
+ const normalized = normalizeHex(value);
593
+ if (normalized.length !== ADDRESS_HEX_LENGTH) {
594
+ throw new Error(`Invalid address length: ${normalized}`);
595
+ }
596
+ return normalized;
597
+ }
598
+ function normalizeCloid(value) {
599
+ const normalized = normalizeHex(value);
600
+ if (normalized.length !== CLOID_HEX_LENGTH) {
601
+ throw new Error(`Invalid cloid length: ${normalized}`);
602
+ }
603
+ return normalized;
502
604
  }
503
605
  async function signL1Action(args) {
504
606
  const { wallet, action, nonce, vaultAddress, expiresAfter, isTestnet } = args;
@@ -1241,8 +1343,8 @@ async function cancelHyperliquidOrdersByCloid(options) {
1241
1343
  options,
1242
1344
  options.cancels,
1243
1345
  (idx, entry) => ({
1244
- a: idx,
1245
- c: normalizeAddress(entry.cloid)
1346
+ asset: idx,
1347
+ cloid: normalizeCloid(entry.cloid)
1246
1348
  })
1247
1349
  )
1248
1350
  };
@@ -1253,10 +1355,10 @@ async function cancelAllHyperliquidOrders(options) {
1253
1355
  return submitExchangeAction(options, action);
1254
1356
  }
1255
1357
  async function scheduleHyperliquidCancel(options) {
1256
- if (options.time !== null) {
1358
+ if (options.time != null) {
1257
1359
  assertPositiveNumber(options.time, "time");
1258
1360
  }
1259
- const action = { type: "scheduleCancel", time: options.time };
1361
+ const action = options.time == null ? { type: "scheduleCancel" } : { type: "scheduleCancel", time: options.time };
1260
1362
  return submitExchangeAction(options, action);
1261
1363
  }
1262
1364
  async function modifyHyperliquidOrder(options) {
@@ -1490,7 +1592,7 @@ async function buildOrder(intent, options) {
1490
1592
  r: intent.reduceOnly ?? false,
1491
1593
  t: limitOrTrigger,
1492
1594
  ...intent.clientId ? {
1493
- c: normalizeAddress(intent.clientId)
1595
+ c: normalizeCloid(intent.clientId)
1494
1596
  } : {}
1495
1597
  };
1496
1598
  }
@@ -1537,6 +1639,32 @@ function assertPositiveDecimal(value, label) {
1537
1639
  return;
1538
1640
  }
1539
1641
  assertString(value, label);
1642
+ if (!/^(?:\d+\.?\d*|\.\d+)$/.test(value.trim())) {
1643
+ throw new Error(`${label} must be a positive decimal string.`);
1644
+ }
1645
+ const numeric = Number(value);
1646
+ if (!Number.isFinite(numeric) || numeric <= 0) {
1647
+ throw new Error(`${label} must be positive.`);
1648
+ }
1649
+ }
1650
+ function collectExchangeErrorMessages(payload) {
1651
+ if (!payload || typeof payload !== "object") return [];
1652
+ const root = payload;
1653
+ const messages = [];
1654
+ const statuses = root.response?.data?.statuses;
1655
+ if (Array.isArray(statuses)) {
1656
+ statuses.forEach((status, index) => {
1657
+ if (status && typeof status === "object" && "error" in status && typeof status.error === "string") {
1658
+ const errorText = status.error;
1659
+ messages.push(`status[${index}]: ${errorText}`);
1660
+ }
1661
+ });
1662
+ }
1663
+ const singleStatus = root.response?.data?.status;
1664
+ if (singleStatus && typeof singleStatus === "object" && "error" in singleStatus && typeof singleStatus.error === "string") {
1665
+ messages.push(singleStatus.error);
1666
+ }
1667
+ return messages;
1540
1668
  }
1541
1669
  async function postExchange(env, body) {
1542
1670
  const response = await fetch(`${API_BASES[env]}/exchange`, {
@@ -1574,10 +1702,886 @@ async function postExchange(env, body) {
1574
1702
  body: json
1575
1703
  });
1576
1704
  }
1705
+ const nestedErrors = collectExchangeErrorMessages(json);
1706
+ if (nestedErrors.length > 0) {
1707
+ throw new HyperliquidApiError("Hyperliquid exchange returned action errors.", {
1708
+ status: response.status,
1709
+ statusText: response.statusText,
1710
+ body: json,
1711
+ errors: nestedErrors
1712
+ });
1713
+ }
1577
1714
  return json;
1578
1715
  }
1579
1716
 
1717
+ // src/adapters/hyperliquid/env.ts
1718
+ function resolveHyperliquidChain(environment) {
1719
+ return environment === "mainnet" ? "arbitrum" : "arbitrum-sepolia";
1720
+ }
1721
+ function resolveHyperliquidRpcEnvVar(environment) {
1722
+ return environment === "mainnet" ? "ARBITRUM_RPC_URL" : "ARBITRUM_SEPOLIA_RPC_URL";
1723
+ }
1724
+ function resolveHyperliquidChainConfig(environment, env = process.env) {
1725
+ const rpcVar = resolveHyperliquidRpcEnvVar(environment);
1726
+ const rpcUrl = env[rpcVar];
1727
+ return {
1728
+ chain: resolveHyperliquidChain(environment),
1729
+ ...rpcUrl ? { rpcUrl } : {}
1730
+ };
1731
+ }
1732
+ function resolveHyperliquidStoreNetwork(environment) {
1733
+ return environment === "mainnet" ? "hyperliquid" : "hyperliquid-testnet";
1734
+ }
1735
+
1736
+ // src/adapters/hyperliquid/symbols.ts
1737
+ var UNKNOWN_SYMBOL2 = "UNKNOWN";
1738
+ function extractHyperliquidDex(symbol) {
1739
+ const idx = symbol.indexOf(":");
1740
+ if (idx <= 0) return null;
1741
+ const dex = symbol.slice(0, idx).trim().toLowerCase();
1742
+ return dex || null;
1743
+ }
1744
+ function normalizeSpotTokenName2(value) {
1745
+ const raw = (value ?? "").trim();
1746
+ if (!raw) return "";
1747
+ if (raw.endsWith("0") && raw.length > 1) {
1748
+ return raw.slice(0, -1);
1749
+ }
1750
+ return raw;
1751
+ }
1752
+ function normalizeHyperliquidBaseSymbol(value) {
1753
+ if (!value) return null;
1754
+ const trimmed = value.trim();
1755
+ if (!trimmed) return null;
1756
+ const withoutDex = trimmed.includes(":") ? trimmed.split(":").slice(1).join(":") : trimmed;
1757
+ const base = withoutDex.split("-")[0] ?? withoutDex;
1758
+ const baseNoPair = base.split("/")[0] ?? base;
1759
+ const normalized = baseNoPair.trim().toUpperCase();
1760
+ if (!normalized || normalized === UNKNOWN_SYMBOL2) return null;
1761
+ return normalized;
1762
+ }
1763
+ function normalizeHyperliquidMetaSymbol(symbol) {
1764
+ const trimmed = symbol.trim();
1765
+ const noDex = trimmed.includes(":") ? trimmed.split(":").slice(1).join(":") : trimmed;
1766
+ const noPair = noDex.split("-")[0] ?? noDex;
1767
+ return (noPair.split("/")[0] ?? noPair).trim();
1768
+ }
1769
+ function resolveHyperliquidPair(value) {
1770
+ if (!value) return null;
1771
+ const trimmed = value.trim();
1772
+ if (!trimmed) return null;
1773
+ const withoutDex = trimmed.includes(":") ? trimmed.split(":").slice(1).join(":") : trimmed;
1774
+ if (withoutDex.includes("/")) {
1775
+ return withoutDex.toUpperCase();
1776
+ }
1777
+ if (withoutDex.includes("-")) {
1778
+ const [base, ...rest] = withoutDex.split("-");
1779
+ const quote = rest.join("-").trim();
1780
+ if (!base || !quote) return null;
1781
+ return `${base.toUpperCase()}/${quote.toUpperCase()}`;
1782
+ }
1783
+ return null;
1784
+ }
1785
+ function parseSpotPairSymbol(symbol) {
1786
+ const trimmed = symbol.trim();
1787
+ if (!trimmed.includes("/")) return null;
1788
+ const [rawBase, rawQuote] = trimmed.split("/");
1789
+ const base = rawBase?.trim().toUpperCase() ?? "";
1790
+ const quote = rawQuote?.trim().toUpperCase() ?? "";
1791
+ if (!base || !quote) return null;
1792
+ return { base, quote };
1793
+ }
1794
+ function isHyperliquidSpotSymbol(symbol) {
1795
+ return symbol.startsWith("@") || symbol.includes("/");
1796
+ }
1797
+ function resolveSpotMidCandidates(baseSymbol) {
1798
+ const base = baseSymbol.trim().toUpperCase();
1799
+ if (!base) return [];
1800
+ const candidates = [base];
1801
+ if (base.startsWith("U") && base.length > 1) {
1802
+ candidates.push(base.slice(1));
1803
+ }
1804
+ return Array.from(new Set(candidates));
1805
+ }
1806
+ function resolveSpotTokenCandidates(value) {
1807
+ const normalized = normalizeSpotTokenName2(value).toUpperCase();
1808
+ if (!normalized) return [];
1809
+ const candidates = [normalized];
1810
+ if (normalized.startsWith("U") && normalized.length > 1) {
1811
+ candidates.push(normalized.slice(1));
1812
+ }
1813
+ return Array.from(new Set(candidates));
1814
+ }
1815
+ function resolveHyperliquidOrderSymbol(value) {
1816
+ if (!value) return null;
1817
+ const trimmed = value.trim();
1818
+ if (!trimmed) return null;
1819
+ if (trimmed.startsWith("@")) return trimmed;
1820
+ if (trimmed.includes(":")) {
1821
+ const [rawDex, ...restParts] = trimmed.split(":");
1822
+ const dex = rawDex.trim().toLowerCase();
1823
+ const rest = restParts.join(":");
1824
+ const base = rest.split("/")[0]?.split("-")[0] ?? rest;
1825
+ const normalizedBase = base.trim().toUpperCase();
1826
+ if (!dex || !normalizedBase || normalizedBase === UNKNOWN_SYMBOL2) {
1827
+ return null;
1828
+ }
1829
+ return `${dex}:${normalizedBase}`;
1830
+ }
1831
+ const pair = resolveHyperliquidPair(trimmed);
1832
+ if (pair) return pair;
1833
+ return normalizeHyperliquidBaseSymbol(trimmed);
1834
+ }
1835
+ function resolveHyperliquidSymbol(asset, override) {
1836
+ const raw = override && override.trim().length > 0 ? override.trim() : asset.trim();
1837
+ if (!raw) return raw;
1838
+ if (raw.startsWith("@")) return raw;
1839
+ if (raw.includes(":")) {
1840
+ const [dexRaw, ...restParts] = raw.split(":");
1841
+ const dex = dexRaw.trim().toLowerCase();
1842
+ const rest = restParts.join(":");
1843
+ const base2 = rest.split("/")[0]?.split("-")[0] ?? rest;
1844
+ const normalizedBase = base2.trim().toUpperCase();
1845
+ if (!dex) return normalizedBase;
1846
+ return `${dex}:${normalizedBase}`;
1847
+ }
1848
+ if (raw.includes("/")) {
1849
+ return raw.toUpperCase();
1850
+ }
1851
+ if (raw.includes("-")) {
1852
+ const [base2, ...rest] = raw.split("-");
1853
+ const quote = rest.join("-").trim();
1854
+ if (base2 && quote) {
1855
+ return `${base2.toUpperCase()}/${quote.toUpperCase()}`;
1856
+ }
1857
+ }
1858
+ const base = raw.split("-")[0] ?? raw;
1859
+ const baseNoPair = base.split("/")[0] ?? base;
1860
+ return baseNoPair.trim().toUpperCase();
1861
+ }
1862
+
1863
+ // src/adapters/hyperliquid/order-utils.ts
1864
+ var MAX_HYPERLIQUID_PRICE_DECIMALS = 8;
1865
+ function countDecimals(value) {
1866
+ if (!Number.isFinite(value)) return 0;
1867
+ const s = value.toString();
1868
+ const [, dec = ""] = s.split(".");
1869
+ return dec.length;
1870
+ }
1871
+ function clampPriceDecimals(value) {
1872
+ if (!Number.isFinite(value) || value <= 0) {
1873
+ throw new Error("Price must be positive.");
1874
+ }
1875
+ const fixed = value.toFixed(MAX_HYPERLIQUID_PRICE_DECIMALS);
1876
+ return fixed.replace(/\.?0+$/, "");
1877
+ }
1878
+ function assertNumberString(value) {
1879
+ if (!/^-?(?:\d+\.?\d*|\.\d+)$/.test(value)) {
1880
+ throw new TypeError("Invalid decimal number string.");
1881
+ }
1882
+ }
1883
+ function normalizeDecimalString(value) {
1884
+ return value.trim().replace(/^(-?)0+(?=\d)/, "$1").replace(/\.0*$|(\.\d+?)0+$/, "$1").replace(/^(-?)\./, "$10.").replace(/^-?$/, "0").replace(/^-0$/, "0");
1885
+ }
1886
+ var StringMath = {
1887
+ log10Floor(value) {
1888
+ const abs = value.startsWith("-") ? value.slice(1) : value;
1889
+ const num = Number(abs);
1890
+ if (!Number.isFinite(num) || num === 0) return -Infinity;
1891
+ const [intPart, fracPart = ""] = abs.split(".");
1892
+ if (Number(intPart) !== 0) {
1893
+ return intPart.replace(/^0+/, "").length - 1;
1894
+ }
1895
+ const leadingZeros = fracPart.match(/^0*/)?.[0]?.length ?? 0;
1896
+ return -(leadingZeros + 1);
1897
+ },
1898
+ multiplyByPow10(value, exp) {
1899
+ if (!Number.isInteger(exp)) {
1900
+ throw new RangeError("Exponent must be an integer.");
1901
+ }
1902
+ if (exp === 0) return normalizeDecimalString(value);
1903
+ const negative = value.startsWith("-");
1904
+ const abs = negative ? value.slice(1) : value;
1905
+ const [intRaw, fracRaw = ""] = abs.split(".");
1906
+ const intPart = intRaw || "0";
1907
+ let output;
1908
+ if (exp > 0) {
1909
+ if (exp >= fracRaw.length) {
1910
+ output = intPart + fracRaw + "0".repeat(exp - fracRaw.length);
1911
+ } else {
1912
+ output = `${intPart}${fracRaw.slice(0, exp)}.${fracRaw.slice(exp)}`;
1913
+ }
1914
+ } else {
1915
+ const absExp = -exp;
1916
+ if (absExp >= intPart.length) {
1917
+ output = `0.${"0".repeat(absExp - intPart.length)}${intPart}${fracRaw}`;
1918
+ } else {
1919
+ output = `${intPart.slice(0, -absExp)}.${intPart.slice(-absExp)}${fracRaw}`;
1920
+ }
1921
+ }
1922
+ return normalizeDecimalString((negative ? "-" : "") + output);
1923
+ },
1924
+ trunc(value) {
1925
+ const index = value.indexOf(".");
1926
+ return index === -1 ? value : value.slice(0, index) || "0";
1927
+ },
1928
+ toPrecisionTruncate(value, precision) {
1929
+ if (!Number.isInteger(precision) || precision < 1) {
1930
+ throw new RangeError("Precision must be a positive integer.");
1931
+ }
1932
+ if (/^-?0+(\.0*)?$/.test(value)) return "0";
1933
+ const negative = value.startsWith("-");
1934
+ const abs = negative ? value.slice(1) : value;
1935
+ const magnitude = StringMath.log10Floor(abs);
1936
+ const shiftAmount = precision - magnitude - 1;
1937
+ const shifted = StringMath.multiplyByPow10(abs, shiftAmount);
1938
+ const truncated = StringMath.trunc(shifted);
1939
+ const shiftedBack = StringMath.multiplyByPow10(truncated, -shiftAmount);
1940
+ return normalizeDecimalString(negative ? `-${shiftedBack}` : shiftedBack);
1941
+ },
1942
+ toFixedTruncate(value, decimals) {
1943
+ if (!Number.isInteger(decimals) || decimals < 0) {
1944
+ throw new RangeError("Decimals must be a non-negative integer.");
1945
+ }
1946
+ const matcher = new RegExp(`^-?(?:\\d+)?(?:\\.\\d{0,${decimals}})?`);
1947
+ const result = value.match(matcher)?.[0];
1948
+ if (!result) {
1949
+ throw new TypeError("Invalid number format.");
1950
+ }
1951
+ return normalizeDecimalString(result);
1952
+ }
1953
+ };
1954
+ function formatHyperliquidPrice(price, szDecimals, marketType = "perp") {
1955
+ const normalized = price.toString().trim();
1956
+ assertNumberString(normalized);
1957
+ if (/^-?\d+$/.test(normalized)) {
1958
+ return normalizeDecimalString(normalized);
1959
+ }
1960
+ const maxDecimals2 = Math.max((marketType === "perp" ? 6 : 8) - szDecimals, 0);
1961
+ const decimalsTrimmed = StringMath.toFixedTruncate(normalized, maxDecimals2);
1962
+ const sigFigTrimmed = StringMath.toPrecisionTruncate(decimalsTrimmed, 5);
1963
+ if (sigFigTrimmed === "0") {
1964
+ throw new RangeError("Price is too small and was truncated to 0.");
1965
+ }
1966
+ return sigFigTrimmed;
1967
+ }
1968
+ function formatHyperliquidSize(size, szDecimals) {
1969
+ const normalized = size.toString().trim();
1970
+ assertNumberString(normalized);
1971
+ const truncated = StringMath.toFixedTruncate(normalized, szDecimals);
1972
+ if (truncated === "0") {
1973
+ throw new RangeError("Size is too small and was truncated to 0.");
1974
+ }
1975
+ return truncated;
1976
+ }
1977
+ function formatHyperliquidOrderSize(value, szDecimals) {
1978
+ if (!Number.isFinite(value) || value <= 0) return "0";
1979
+ try {
1980
+ return formatHyperliquidSize(value, szDecimals);
1981
+ } catch {
1982
+ return "0";
1983
+ }
1984
+ }
1985
+ function roundHyperliquidPriceToTick(price, tick, side) {
1986
+ if (!Number.isFinite(price) || price <= 0) {
1987
+ throw new Error("Price must be positive.");
1988
+ }
1989
+ if (!Number.isFinite(tick.tickDecimals) || tick.tickDecimals < 0) {
1990
+ throw new Error("tick.tickDecimals must be a non-negative number.");
1991
+ }
1992
+ if (tick.tickSizeInt <= 0n) {
1993
+ throw new Error("tick.tickSizeInt must be positive.");
1994
+ }
1995
+ const scale = 10 ** tick.tickDecimals;
1996
+ const scaled = BigInt(Math.round(price * scale));
1997
+ const tickSize = tick.tickSizeInt;
1998
+ const rounded = side === "sell" ? scaled / tickSize * tickSize : (scaled + tickSize - 1n) / tickSize * tickSize;
1999
+ const integer = Number(rounded) / scale;
2000
+ return clampPriceDecimals(integer);
2001
+ }
2002
+ function formatHyperliquidMarketablePrice(params) {
2003
+ const { mid, side, slippageBps, tick } = params;
2004
+ const decimals = countDecimals(mid);
2005
+ const factor = 10 ** decimals;
2006
+ const adjusted = mid * (side === "buy" ? 1 + slippageBps / 1e4 : 1 - slippageBps / 1e4);
2007
+ if (tick) {
2008
+ return roundHyperliquidPriceToTick(adjusted, tick, side);
2009
+ }
2010
+ const scaled = adjusted * factor;
2011
+ const rounded = side === "buy" ? Math.ceil(scaled) / factor : Math.floor(scaled) / factor;
2012
+ return clampPriceDecimals(rounded);
2013
+ }
2014
+ function extractHyperliquidOrderIds(responses) {
2015
+ const cloids = /* @__PURE__ */ new Set();
2016
+ const oids = /* @__PURE__ */ new Set();
2017
+ const push = (val, target) => {
2018
+ if (val === null || val === void 0) return;
2019
+ const str = String(val);
2020
+ if (str.length) target.add(str);
2021
+ };
2022
+ for (const res of responses) {
2023
+ const statuses = res?.response?.data?.statuses;
2024
+ if (!Array.isArray(statuses)) continue;
2025
+ for (const status of statuses) {
2026
+ const resting = status.resting;
2027
+ const filled = status.filled;
2028
+ push(resting?.cloid, cloids);
2029
+ push(resting?.oid, oids);
2030
+ push(filled?.cloid, cloids);
2031
+ push(filled?.oid, oids);
2032
+ }
2033
+ }
2034
+ return {
2035
+ cloids: Array.from(cloids),
2036
+ oids: Array.from(oids)
2037
+ };
2038
+ }
2039
+ function resolveHyperliquidOrderRef(params) {
2040
+ const { response, fallbackCloid, fallbackOid, prefix = "hl-order", index = 0 } = params;
2041
+ const statuses = response?.response?.data?.statuses ?? [];
2042
+ if (Array.isArray(statuses)) {
2043
+ for (const status of statuses) {
2044
+ const filled = status && typeof status.filled === "object" ? status.filled : null;
2045
+ if (filled) {
2046
+ if (typeof filled.cloid === "string" && filled.cloid.trim().length > 0) {
2047
+ return filled.cloid;
2048
+ }
2049
+ if (typeof filled.oid === "number" || typeof filled.oid === "string" && filled.oid.trim().length > 0) {
2050
+ return String(filled.oid);
2051
+ }
2052
+ }
2053
+ const resting = status && typeof status.resting === "object" ? status.resting : null;
2054
+ if (resting) {
2055
+ if (typeof resting.cloid === "string" && resting.cloid.trim().length > 0) {
2056
+ return resting.cloid;
2057
+ }
2058
+ if (typeof resting.oid === "number" || typeof resting.oid === "string" && resting.oid.trim().length > 0) {
2059
+ return String(resting.oid);
2060
+ }
2061
+ }
2062
+ }
2063
+ }
2064
+ if (fallbackCloid && fallbackCloid.trim().length > 0) {
2065
+ return fallbackCloid;
2066
+ }
2067
+ if (fallbackOid && fallbackOid.trim().length > 0) {
2068
+ return fallbackOid;
2069
+ }
2070
+ return `${prefix}-${Date.now()}-${index}`;
2071
+ }
2072
+ function resolveHyperliquidErrorDetail(error) {
2073
+ if (error instanceof HyperliquidApiError) {
2074
+ return error.response ?? null;
2075
+ }
2076
+ if (error && typeof error === "object" && "response" in error) {
2077
+ return error.response ?? null;
2078
+ }
2079
+ return null;
2080
+ }
2081
+
2082
+ // src/adapters/hyperliquid/state-readers.ts
2083
+ function unwrapData(payload) {
2084
+ if (!payload || typeof payload !== "object") return null;
2085
+ if ("data" in payload) {
2086
+ const data = payload.data;
2087
+ if (data && typeof data === "object") {
2088
+ return data;
2089
+ }
2090
+ }
2091
+ return payload;
2092
+ }
2093
+ function readHyperliquidNumber(value) {
2094
+ if (typeof value === "number" && Number.isFinite(value)) return value;
2095
+ if (typeof value === "string" && value.trim().length > 0) {
2096
+ const parsed = Number(value);
2097
+ return Number.isFinite(parsed) ? parsed : null;
2098
+ }
2099
+ return null;
2100
+ }
2101
+ function readHyperliquidAccountValue(payload) {
2102
+ const data = unwrapData(payload);
2103
+ if (!data) return null;
2104
+ const candidates = [
2105
+ data?.marginSummary?.accountValue,
2106
+ data?.crossMarginSummary?.accountValue,
2107
+ data?.accountValue,
2108
+ data?.equity,
2109
+ data?.totalAccountValue,
2110
+ data?.marginSummary?.totalAccountValue
2111
+ ];
2112
+ for (const value of candidates) {
2113
+ const parsed = readHyperliquidNumber(value);
2114
+ if (parsed !== null) return parsed;
2115
+ }
2116
+ return null;
2117
+ }
2118
+ function matchPerpCoin(params) {
2119
+ const coin = params.coin.toUpperCase();
2120
+ const target = params.target.toUpperCase();
2121
+ if (params.prefixMatch) return coin.startsWith(target);
2122
+ return coin === target;
2123
+ }
2124
+ function readHyperliquidPerpPositionSize(payload, symbol, options) {
2125
+ const data = unwrapData(payload);
2126
+ const rows = Array.isArray(data?.assetPositions) ? data.assetPositions : [];
2127
+ const base = symbol.split("-")[0]?.toUpperCase() ?? symbol.toUpperCase();
2128
+ const prefixMatch = options?.prefixMatch ?? false;
2129
+ for (const row of rows) {
2130
+ const position = row.position ?? row;
2131
+ const coin = typeof position?.coin === "string" ? position.coin : typeof row.coin === "string" ? row.coin : "";
2132
+ if (!matchPerpCoin({ coin, target: base, prefixMatch })) continue;
2133
+ const size = position.szi ?? row.szi;
2134
+ const parsed = readHyperliquidNumber(size);
2135
+ return parsed ?? 0;
2136
+ }
2137
+ return 0;
2138
+ }
2139
+ function readHyperliquidPerpPosition(payload, symbol, options) {
2140
+ const data = unwrapData(payload);
2141
+ const rows = Array.isArray(data?.assetPositions) ? data.assetPositions : [];
2142
+ const target = symbol.split("-")[0]?.toUpperCase() ?? symbol.toUpperCase();
2143
+ const prefixMatch = options?.prefixMatch ?? false;
2144
+ for (const row of rows) {
2145
+ const position = row?.position ?? row;
2146
+ const coin = typeof position?.coin === "string" ? position.coin : typeof row?.coin === "string" ? row.coin : "";
2147
+ if (!matchPerpCoin({ coin, target, prefixMatch })) continue;
2148
+ const size = readHyperliquidNumber(position?.szi ?? row.szi) ?? 0;
2149
+ const positionValue = Math.abs(
2150
+ readHyperliquidNumber(position?.positionValue ?? row.positionValue) ?? 0
2151
+ );
2152
+ const unrealizedPnl = readHyperliquidNumber(
2153
+ position?.unrealizedPnl ?? row.unrealizedPnl
2154
+ );
2155
+ return { size, positionValue, unrealizedPnl };
2156
+ }
2157
+ return { size: 0, positionValue: 0, unrealizedPnl: null };
2158
+ }
2159
+ function readHyperliquidSpotBalanceSize(payload, symbol) {
2160
+ const data = unwrapData(payload);
2161
+ const rows = Array.isArray(data?.balances) ? data.balances : [];
2162
+ const base = symbol.split("/")[0]?.split("-")[0]?.toUpperCase() ?? symbol.toUpperCase();
2163
+ for (const row of rows) {
2164
+ const coin = typeof row?.coin === "string" ? row.coin : typeof row?.asset === "string" ? row.asset : "";
2165
+ if (coin.toUpperCase() !== base) continue;
2166
+ const total = row.total ?? row.balance ?? row.szi;
2167
+ const parsed = readHyperliquidNumber(total);
2168
+ return parsed ?? 0;
2169
+ }
2170
+ return 0;
2171
+ }
2172
+ function readHyperliquidSpotBalance(payload, base) {
2173
+ const data = unwrapData(payload);
2174
+ const balances = Array.isArray(data?.balances) ? data.balances : [];
2175
+ const target = base.toUpperCase();
2176
+ for (const row of balances) {
2177
+ const coin = typeof row?.coin === "string" ? row.coin : "";
2178
+ if (coin.toUpperCase() !== target) continue;
2179
+ const total = readHyperliquidNumber(row?.total) ?? 0;
2180
+ const entryNtl = readHyperliquidNumber(row?.entryNtl);
2181
+ return { total, entryNtl };
2182
+ }
2183
+ return { total: 0, entryNtl: null };
2184
+ }
2185
+ function readHyperliquidSpotAccountValue(params) {
2186
+ const rows = Array.isArray(params.balances) ? params.balances : [];
2187
+ if (rows.length === 0) return null;
2188
+ let total = 0;
2189
+ let hasValue = false;
2190
+ for (const row of rows) {
2191
+ const coin = typeof row?.coin === "string" ? row.coin : typeof row?.asset === "string" ? row.asset : "";
2192
+ if (!coin) continue;
2193
+ const amount = readHyperliquidNumber(
2194
+ row.total ?? row.balance ?? row.szi
2195
+ );
2196
+ if (amount == null || amount === 0) continue;
2197
+ const price = params.pricesUsd.get(coin.toUpperCase());
2198
+ if (price == null || !Number.isFinite(price) || price <= 0) continue;
2199
+ total += amount * price;
2200
+ hasValue = true;
2201
+ }
2202
+ return hasValue ? total : null;
2203
+ }
2204
+
2205
+ // src/adapters/hyperliquid/market-data.ts
2206
+ var META_CACHE_TTL_MS = 5 * 60 * 1e3;
2207
+ var allMidsCache = /* @__PURE__ */ new Map();
2208
+ function gcd(a, b) {
2209
+ let left = a < 0n ? -a : a;
2210
+ let right = b < 0n ? -b : b;
2211
+ while (right !== 0n) {
2212
+ const next = left % right;
2213
+ left = right;
2214
+ right = next;
2215
+ }
2216
+ return left;
2217
+ }
2218
+ function pow10(decimals) {
2219
+ let result = 1n;
2220
+ for (let i = 0; i < decimals; i += 1) {
2221
+ result *= 10n;
2222
+ }
2223
+ return result;
2224
+ }
2225
+ function maxDecimals(values) {
2226
+ let max = 0;
2227
+ for (const value of values) {
2228
+ const dot = value.indexOf(".");
2229
+ if (dot === -1) continue;
2230
+ const decimals = value.length - dot - 1;
2231
+ if (decimals > max) max = decimals;
2232
+ }
2233
+ return max;
2234
+ }
2235
+ function toScaledInt(value, decimals) {
2236
+ const trimmed = value.trim();
2237
+ const negative = trimmed.startsWith("-");
2238
+ const unsigned = negative ? trimmed.slice(1) : trimmed;
2239
+ const [intPart, fracPart = ""] = unsigned.split(".");
2240
+ const padded = fracPart.padEnd(decimals, "0").slice(0, decimals);
2241
+ const combined = `${intPart || "0"}${padded}`;
2242
+ const asInt = BigInt(combined || "0");
2243
+ return negative ? -asInt : asInt;
2244
+ }
2245
+ function formatScaledInt(value, decimals) {
2246
+ const negative = value < 0n;
2247
+ const absValue = negative ? -value : value;
2248
+ const scale = pow10(decimals);
2249
+ const integer = absValue / scale;
2250
+ const fraction = absValue % scale;
2251
+ if (decimals === 0) {
2252
+ return `${negative ? "-" : ""}${integer.toString()}`;
2253
+ }
2254
+ const fractionStr = fraction.toString().padStart(decimals, "0");
2255
+ return `${negative ? "-" : ""}${integer.toString()}.${fractionStr}`.replace(
2256
+ /\.?0+$/,
2257
+ ""
2258
+ );
2259
+ }
2260
+ function resolveSpotSizeDecimals(meta, symbol) {
2261
+ const universe = meta.universe ?? [];
2262
+ const tokens = meta.tokens ?? [];
2263
+ if (!universe.length || !tokens.length) {
2264
+ throw new Error(`Spot metadata unavailable for ${symbol}.`);
2265
+ }
2266
+ const tokenMap = /* @__PURE__ */ new Map();
2267
+ for (const token of tokens) {
2268
+ const index = token?.index;
2269
+ const szDecimals = typeof token?.szDecimals === "number" ? token.szDecimals : null;
2270
+ if (typeof index !== "number" || szDecimals == null) continue;
2271
+ tokenMap.set(index, {
2272
+ name: normalizeSpotTokenName2(token?.name),
2273
+ szDecimals
2274
+ });
2275
+ }
2276
+ if (symbol.startsWith("@")) {
2277
+ const targetIndex = Number.parseInt(symbol.slice(1), 10);
2278
+ if (!Number.isFinite(targetIndex)) {
2279
+ throw new Error(`Invalid spot pair id: ${symbol}`);
2280
+ }
2281
+ for (let idx = 0; idx < universe.length; idx += 1) {
2282
+ const market = universe[idx];
2283
+ const marketIndex = typeof market?.index === "number" ? market.index : idx;
2284
+ if (marketIndex !== targetIndex) continue;
2285
+ const [baseIndex] = Array.isArray(market?.tokens) ? market.tokens : [];
2286
+ const baseToken = tokenMap.get(baseIndex ?? -1);
2287
+ if (!baseToken) break;
2288
+ return baseToken.szDecimals;
2289
+ }
2290
+ throw new Error(`Unknown spot pair id: ${symbol}`);
2291
+ }
2292
+ const pair = parseSpotPairSymbol(symbol);
2293
+ if (!pair) {
2294
+ throw new Error(`Invalid spot symbol: ${symbol}`);
2295
+ }
2296
+ const normalizedBase = normalizeSpotTokenName2(pair.base).toUpperCase();
2297
+ const normalizedQuote = normalizeSpotTokenName2(pair.quote).toUpperCase();
2298
+ for (const market of universe) {
2299
+ const [baseIndex, quoteIndex] = Array.isArray(market?.tokens) ? market.tokens : [];
2300
+ const baseToken = tokenMap.get(baseIndex ?? -1);
2301
+ const quoteToken = tokenMap.get(quoteIndex ?? -1);
2302
+ if (!baseToken || !quoteToken) continue;
2303
+ if (baseToken.name.toUpperCase() === normalizedBase && quoteToken.name.toUpperCase() === normalizedQuote) {
2304
+ return baseToken.szDecimals;
2305
+ }
2306
+ }
2307
+ throw new Error(`No size decimals found for ${symbol}.`);
2308
+ }
2309
+ async function fetchHyperliquidAllMids(environment) {
2310
+ const cacheKey = environment;
2311
+ const cached = allMidsCache.get(cacheKey);
2312
+ if (cached && Date.now() - cached.fetchedAt < META_CACHE_TTL_MS) {
2313
+ return cached.mids;
2314
+ }
2315
+ const baseUrl = API_BASES[environment];
2316
+ const res = await fetch(`${baseUrl}/info`, {
2317
+ method: "POST",
2318
+ headers: { "content-type": "application/json" },
2319
+ body: JSON.stringify({ type: "allMids" })
2320
+ });
2321
+ const json = await res.json().catch(() => null);
2322
+ if (!res.ok || !json || typeof json !== "object") {
2323
+ throw new Error(`Failed to load Hyperliquid mid prices (${res.status}).`);
2324
+ }
2325
+ allMidsCache.set(cacheKey, { fetchedAt: Date.now(), mids: json });
2326
+ return json;
2327
+ }
2328
+ async function fetchHyperliquidTickSize(params) {
2329
+ return fetchHyperliquidTickSizeForCoin(params.environment, params.symbol);
2330
+ }
2331
+ async function fetchHyperliquidSpotTickSize(params) {
2332
+ if (!Number.isFinite(params.marketIndex)) {
2333
+ throw new Error("Hyperliquid spot market index is invalid.");
2334
+ }
2335
+ return fetchHyperliquidTickSizeForCoin(
2336
+ params.environment,
2337
+ `@${params.marketIndex}`
2338
+ );
2339
+ }
2340
+ async function fetchHyperliquidTickSizeForCoin(environment, coin) {
2341
+ const base = API_BASES[environment];
2342
+ const res = await fetch(`${base}/info`, {
2343
+ method: "POST",
2344
+ headers: { "content-type": "application/json" },
2345
+ body: JSON.stringify({ type: "l2Book", coin })
2346
+ });
2347
+ if (!res.ok) {
2348
+ throw new Error(`Hyperliquid l2Book failed for ${coin}`);
2349
+ }
2350
+ const data = await res.json().catch(() => null);
2351
+ const levels = Array.isArray(data?.levels) ? data?.levels ?? [] : [];
2352
+ const prices = levels.flatMap(
2353
+ (side) => Array.isArray(side) ? side.map((entry) => String(entry?.px ?? "")) : []
2354
+ ).filter((px) => px.length > 0);
2355
+ if (prices.length < 2) {
2356
+ throw new Error(`Hyperliquid l2Book missing price levels for ${coin}`);
2357
+ }
2358
+ const decimals = maxDecimals(prices);
2359
+ const scaled = prices.map((px) => toScaledInt(px, decimals));
2360
+ const unique = Array.from(new Set(scaled.map((v) => v.toString()))).map((v) => BigInt(v)).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
2361
+ let tick = 0n;
2362
+ for (let i = 1; i < unique.length; i += 1) {
2363
+ const diff = unique[i] - unique[i - 1];
2364
+ if (diff <= 0n) continue;
2365
+ tick = tick === 0n ? diff : gcd(tick, diff);
2366
+ }
2367
+ if (tick === 0n) {
2368
+ tick = 1n;
2369
+ }
2370
+ return { tickSizeInt: tick, tickDecimals: decimals };
2371
+ }
2372
+ async function fetchHyperliquidPerpMarketInfo(params) {
2373
+ const data = await fetchHyperliquidMetaAndAssetCtxs(params.environment);
2374
+ const universe = data?.[0]?.universe ?? [];
2375
+ const contexts = data?.[1] ?? [];
2376
+ const target = normalizeHyperliquidMetaSymbol(params.symbol).toUpperCase();
2377
+ const idx = universe.findIndex(
2378
+ (entry) => normalizeHyperliquidMetaSymbol(entry?.name ?? "").toUpperCase() === target
2379
+ );
2380
+ if (idx < 0) {
2381
+ throw new Error(`Unknown Hyperliquid perp asset: ${params.symbol}`);
2382
+ }
2383
+ const ctx = contexts[idx] ?? null;
2384
+ const price = readHyperliquidNumber(ctx?.markPx ?? ctx?.midPx ?? ctx?.oraclePx);
2385
+ if (!price || price <= 0) {
2386
+ throw new Error(`No perp price available for ${params.symbol}`);
2387
+ }
2388
+ const fundingRate = readHyperliquidNumber(ctx?.funding);
2389
+ const szDecimals = readHyperliquidNumber(universe[idx]?.szDecimals);
2390
+ if (szDecimals == null) {
2391
+ throw new Error(`No size decimals available for ${params.symbol}`);
2392
+ }
2393
+ return {
2394
+ symbol: params.symbol,
2395
+ price,
2396
+ fundingRate,
2397
+ szDecimals
2398
+ };
2399
+ }
2400
+ async function fetchHyperliquidSpotMarketInfo(params) {
2401
+ const mids = params.mids === void 0 ? await fetchHyperliquidAllMids(params.environment).catch(() => null) : params.mids;
2402
+ const data = await fetchHyperliquidSpotMetaAndAssetCtxs(params.environment);
2403
+ const universe = data?.[0]?.universe ?? [];
2404
+ const tokens = data?.[0]?.tokens ?? [];
2405
+ const contexts = data?.[1] ?? [];
2406
+ const tokenMap = /* @__PURE__ */ new Map();
2407
+ for (const token of tokens) {
2408
+ const index = token?.index;
2409
+ const szDecimals = readHyperliquidNumber(token?.szDecimals);
2410
+ if (typeof index !== "number" || szDecimals == null) continue;
2411
+ tokenMap.set(index, {
2412
+ name: normalizeSpotTokenName2(token?.name),
2413
+ szDecimals
2414
+ });
2415
+ }
2416
+ const baseCandidates = resolveSpotTokenCandidates(params.base);
2417
+ const quoteCandidates = resolveSpotTokenCandidates(params.quote);
2418
+ const normalizedBase = normalizeSpotTokenName2(params.base).toUpperCase();
2419
+ const normalizedQuote = normalizeSpotTokenName2(params.quote).toUpperCase();
2420
+ for (let idx = 0; idx < universe.length; idx += 1) {
2421
+ const market = universe[idx];
2422
+ const [baseIndex, quoteIndex] = Array.isArray(market?.tokens) ? market.tokens : [];
2423
+ const baseToken = tokenMap.get(baseIndex ?? -1);
2424
+ const quoteToken = tokenMap.get(quoteIndex ?? -1);
2425
+ if (!baseToken || !quoteToken) continue;
2426
+ const marketBaseCandidates = resolveSpotTokenCandidates(baseToken.name);
2427
+ const marketQuoteCandidates = resolveSpotTokenCandidates(quoteToken.name);
2428
+ if (baseCandidates.some((candidate) => marketBaseCandidates.includes(candidate)) && quoteCandidates.some((candidate) => marketQuoteCandidates.includes(candidate))) {
2429
+ const contextIndex = typeof market?.index === "number" ? market.index : idx;
2430
+ const ctx = (contextIndex >= 0 && contextIndex < contexts.length ? contexts[contextIndex] : null) ?? contexts[idx] ?? null;
2431
+ let price = null;
2432
+ if (mids) {
2433
+ for (const candidate of resolveSpotMidCandidates(baseToken.name)) {
2434
+ const mid = readHyperliquidNumber(mids[candidate]);
2435
+ if (mid != null && mid > 0) {
2436
+ price = mid;
2437
+ break;
2438
+ }
2439
+ }
2440
+ }
2441
+ if (!price || price <= 0) {
2442
+ price = readHyperliquidNumber(ctx?.markPx ?? ctx?.midPx ?? ctx?.oraclePx);
2443
+ }
2444
+ if (!price || price <= 0) {
2445
+ throw new Error(
2446
+ `No spot price available for ${normalizedBase}/${normalizedQuote}`
2447
+ );
2448
+ }
2449
+ const marketIndex = typeof market?.index === "number" ? market.index : idx;
2450
+ return {
2451
+ symbol: `${baseToken.name.toUpperCase()}/${quoteToken.name.toUpperCase()}`,
2452
+ base: baseToken.name.toUpperCase(),
2453
+ quote: quoteToken.name.toUpperCase(),
2454
+ assetId: 1e4 + marketIndex,
2455
+ marketIndex,
2456
+ price,
2457
+ szDecimals: baseToken.szDecimals
2458
+ };
2459
+ }
2460
+ }
2461
+ throw new Error(`Unknown Hyperliquid spot market: ${normalizedBase}/${normalizedQuote}`);
2462
+ }
2463
+ async function fetchHyperliquidSizeDecimals(params) {
2464
+ const { symbol, environment } = params;
2465
+ if (isHyperliquidSpotSymbol(symbol)) {
2466
+ const meta2 = await fetchHyperliquidSpotMeta(environment);
2467
+ return resolveSpotSizeDecimals(meta2, symbol);
2468
+ }
2469
+ const meta = await fetchHyperliquidMeta(environment);
2470
+ const universe = Array.isArray(meta?.universe) ? meta.universe : [];
2471
+ const normalized = normalizeHyperliquidMetaSymbol(symbol).toUpperCase();
2472
+ const match = universe.find(
2473
+ (entry) => normalizeHyperliquidMetaSymbol(entry?.name ?? "").toUpperCase() === normalized
2474
+ );
2475
+ if (!match || typeof match.szDecimals !== "number") {
2476
+ throw new Error(`No size decimals found for ${symbol}.`);
2477
+ }
2478
+ return match.szDecimals;
2479
+ }
2480
+ function buildHyperliquidSpotUsdPriceMap(params) {
2481
+ const universe = params.meta.universe ?? [];
2482
+ const tokens = params.meta.tokens ?? [];
2483
+ const tokenMap = /* @__PURE__ */ new Map();
2484
+ for (const token of tokens) {
2485
+ const index = token?.index;
2486
+ if (typeof index !== "number") continue;
2487
+ tokenMap.set(index, normalizeSpotTokenName2(token?.name).toUpperCase());
2488
+ }
2489
+ const prices = /* @__PURE__ */ new Map();
2490
+ prices.set("USDC", 1);
2491
+ for (let idx = 0; idx < universe.length; idx += 1) {
2492
+ const market = universe[idx];
2493
+ const [baseIndex, quoteIndex] = Array.isArray(market?.tokens) ? market.tokens : [];
2494
+ const base = tokenMap.get(baseIndex ?? -1);
2495
+ const quote = tokenMap.get(quoteIndex ?? -1);
2496
+ if (!base || !quote) continue;
2497
+ if (quote !== "USDC") continue;
2498
+ const contextIndex = typeof market?.index === "number" ? market.index : idx;
2499
+ const ctx = (contextIndex >= 0 && contextIndex < params.ctxs.length ? params.ctxs[contextIndex] : null) ?? params.ctxs[idx] ?? null;
2500
+ let price = null;
2501
+ if (params.mids) {
2502
+ for (const candidate of resolveSpotMidCandidates(base)) {
2503
+ const mid = readHyperliquidNumber(params.mids[candidate]);
2504
+ if (mid != null && mid > 0) {
2505
+ price = mid;
2506
+ break;
2507
+ }
2508
+ }
2509
+ }
2510
+ if (!price || price <= 0) {
2511
+ price = readHyperliquidNumber(ctx?.markPx ?? ctx?.midPx ?? ctx?.oraclePx);
2512
+ }
2513
+ if (!price || price <= 0) continue;
2514
+ prices.set(base, price);
2515
+ }
2516
+ return prices;
2517
+ }
2518
+ async function fetchHyperliquidSpotUsdPriceMap(environment) {
2519
+ const [spotMetaAndCtxs, mids] = await Promise.all([
2520
+ fetchHyperliquidSpotMetaAndAssetCtxs(environment),
2521
+ fetchHyperliquidAllMids(environment).catch(() => null)
2522
+ ]);
2523
+ const [metaRaw, ctxsRaw] = spotMetaAndCtxs;
2524
+ const meta = {
2525
+ universe: Array.isArray(metaRaw?.universe) ? metaRaw.universe : [],
2526
+ tokens: Array.isArray(metaRaw?.tokens) ? metaRaw.tokens : []
2527
+ };
2528
+ const ctxs = Array.isArray(ctxsRaw) ? ctxsRaw : [];
2529
+ return buildHyperliquidSpotUsdPriceMap({ meta, ctxs, mids });
2530
+ }
2531
+ async function fetchHyperliquidSpotAccountValue(params) {
2532
+ const pricesUsd = await fetchHyperliquidSpotUsdPriceMap(params.environment);
2533
+ return readHyperliquidSpotAccountValue({
2534
+ balances: params.balances,
2535
+ pricesUsd
2536
+ });
2537
+ }
2538
+ var __hyperliquidMarketDataInternals = {
2539
+ maxDecimals,
2540
+ toScaledInt,
2541
+ formatScaledInt
2542
+ };
2543
+
1580
2544
  // src/adapters/hyperliquid/index.ts
2545
+ function assertPositiveDecimalInput(value, label) {
2546
+ if (typeof value === "number") {
2547
+ if (!Number.isFinite(value) || value <= 0) {
2548
+ throw new Error(`${label} must be a positive number.`);
2549
+ }
2550
+ return;
2551
+ }
2552
+ if (typeof value === "bigint") {
2553
+ if (value <= 0n) {
2554
+ throw new Error(`${label} must be positive.`);
2555
+ }
2556
+ return;
2557
+ }
2558
+ const trimmed = value.trim();
2559
+ if (!trimmed.length) {
2560
+ throw new Error(`${label} must be a non-empty string.`);
2561
+ }
2562
+ if (!/^(?:\d+\.?\d*|\.\d+)$/.test(trimmed)) {
2563
+ throw new Error(`${label} must be a positive decimal string.`);
2564
+ }
2565
+ const numeric = Number(trimmed);
2566
+ if (!Number.isFinite(numeric) || numeric <= 0) {
2567
+ throw new Error(`${label} must be positive.`);
2568
+ }
2569
+ }
2570
+ function normalizePositiveDecimalString(raw, label) {
2571
+ const trimmed = raw.trim();
2572
+ if (!trimmed.length) {
2573
+ throw new Error(`${label} must be a non-empty decimal string.`);
2574
+ }
2575
+ if (!/^(?:\d+\.?\d*|\.\d+)$/.test(trimmed)) {
2576
+ throw new Error(`${label} must be a positive decimal string.`);
2577
+ }
2578
+ const normalized = trimmed.replace(/^0+(?=\d)/, "").replace(/(\.\d*?)0+$/, "$1").replace(/\.$/, "");
2579
+ const numeric = Number(normalized);
2580
+ if (!Number.isFinite(numeric) || numeric <= 0) {
2581
+ throw new Error(`${label} must be positive.`);
2582
+ }
2583
+ return normalized;
2584
+ }
1581
2585
  async function placeHyperliquidOrder(options) {
1582
2586
  const {
1583
2587
  wallet,
@@ -1601,6 +2605,11 @@ async function placeHyperliquidOrder(options) {
1601
2605
  const resolvedBaseUrl = API_BASES[inferredEnvironment];
1602
2606
  const preparedOrders = await Promise.all(
1603
2607
  orders.map(async (intent) => {
2608
+ assertPositiveDecimalInput(intent.price, "price");
2609
+ assertPositiveDecimalInput(intent.size, "size");
2610
+ if (intent.trigger) {
2611
+ assertPositiveDecimalInput(intent.trigger.triggerPx, "triggerPx");
2612
+ }
1604
2613
  const assetIndex = await resolveHyperliquidAssetIndex({
1605
2614
  symbol: intent.symbol,
1606
2615
  baseUrl: resolvedBaseUrl,
@@ -1626,7 +2635,7 @@ async function placeHyperliquidOrder(options) {
1626
2635
  r: intent.reduceOnly ?? false,
1627
2636
  t: limitOrTrigger,
1628
2637
  ...intent.clientId ? {
1629
- c: normalizeHex(intent.clientId)
2638
+ c: normalizeCloid(intent.clientId)
1630
2639
  } : {}
1631
2640
  };
1632
2641
  return order;
@@ -1695,7 +2704,9 @@ async function placeHyperliquidOrder(options) {
1695
2704
  }
1696
2705
  const statuses = json.response?.data?.statuses ?? [];
1697
2706
  const errorStatuses = statuses.filter(
1698
- (entry) => "error" in entry
2707
+ (entry) => Boolean(
2708
+ entry && typeof entry === "object" && "error" in entry && typeof entry.error === "string"
2709
+ )
1699
2710
  );
1700
2711
  if (errorStatuses.length) {
1701
2712
  const message = errorStatuses.map((entry) => entry.error).join(", ");
@@ -1749,10 +2760,11 @@ async function depositToHyperliquidBridge(options) {
1749
2760
  }
1750
2761
  async function withdrawFromHyperliquid(options) {
1751
2762
  const { environment, amount, destination, wallet } = options;
1752
- const parsedAmount = Number(amount);
1753
- if (!Number.isFinite(parsedAmount) || parsedAmount <= 0) {
1754
- throw new Error("Withdraw amount must be a positive number.");
1755
- }
2763
+ const normalizedAmount = normalizePositiveDecimalString(
2764
+ amount,
2765
+ "Withdraw amount"
2766
+ );
2767
+ const parsedAmount = Number.parseFloat(normalizedAmount);
1756
2768
  if (!wallet.account || !wallet.walletClient || !wallet.publicClient) {
1757
2769
  throw new Error(
1758
2770
  "Wallet client and public client are required for withdraw."
@@ -1772,7 +2784,7 @@ async function withdrawFromHyperliquid(options) {
1772
2784
  const message = {
1773
2785
  hyperliquidChain,
1774
2786
  destination: normalizedDestination,
1775
- amount: parsedAmount.toString(),
2787
+ amount: normalizedAmount,
1776
2788
  time
1777
2789
  };
1778
2790
  const types = {
@@ -1797,7 +2809,7 @@ async function withdrawFromHyperliquid(options) {
1797
2809
  signatureChainId,
1798
2810
  hyperliquidChain,
1799
2811
  destination: normalizedDestination,
1800
- amount: parsedAmount.toString(),
2812
+ amount: normalizedAmount,
1801
2813
  time: nonce
1802
2814
  },
1803
2815
  nonce,
@@ -1967,6 +2979,6 @@ var __hyperliquidInternals = {
1967
2979
  splitSignature
1968
2980
  };
1969
2981
 
1970
- export { DEFAULT_HYPERLIQUID_MARKET_SLIPPAGE_BPS, HyperliquidApiError, HyperliquidBuilderApprovalError, HyperliquidExchangeClient, HyperliquidGuardError, HyperliquidInfoClient, HyperliquidTermsError, __hyperliquidInternals, approveHyperliquidBuilderFee, batchModifyHyperliquidOrders, buildHyperliquidMarketIdentity, cancelAllHyperliquidOrders, cancelHyperliquidOrders, cancelHyperliquidOrdersByCloid, cancelHyperliquidTwapOrder, computeHyperliquidMarketIocLimitPrice, createHyperliquidSubAccount, createMonotonicNonceFactory, depositToHyperliquidBridge, fetchHyperliquidAssetCtxs, fetchHyperliquidClearinghouseState, fetchHyperliquidFrontendOpenOrders, fetchHyperliquidHistoricalOrders, fetchHyperliquidMeta, fetchHyperliquidMetaAndAssetCtxs, fetchHyperliquidOpenOrders, fetchHyperliquidOrderStatus, fetchHyperliquidPreTransferCheck, fetchHyperliquidSpotAssetCtxs, fetchHyperliquidSpotClearinghouseState, fetchHyperliquidSpotMeta, fetchHyperliquidSpotMetaAndAssetCtxs, fetchHyperliquidUserFills, fetchHyperliquidUserFillsByTime, fetchHyperliquidUserRateLimit, getHyperliquidMaxBuilderFee, modifyHyperliquidOrder, placeHyperliquidOrder, placeHyperliquidTwapOrder, recordHyperliquidBuilderApproval, recordHyperliquidTermsAcceptance, reserveHyperliquidRequestWeight, resolveHyperliquidAbstractionFromMode, scheduleHyperliquidCancel, sendHyperliquidSpot, setHyperliquidAccountAbstractionMode, setHyperliquidDexAbstraction, setHyperliquidPortfolioMargin, transferHyperliquidSubAccount, updateHyperliquidIsolatedMargin, updateHyperliquidLeverage, withdrawFromHyperliquid };
2982
+ export { DEFAULT_HYPERLIQUID_MARKET_SLIPPAGE_BPS, HyperliquidApiError, HyperliquidBuilderApprovalError, HyperliquidExchangeClient, HyperliquidGuardError, HyperliquidInfoClient, HyperliquidTermsError, __hyperliquidInternals, __hyperliquidMarketDataInternals, approveHyperliquidBuilderFee, batchModifyHyperliquidOrders, buildHyperliquidMarketIdentity, buildHyperliquidSpotUsdPriceMap, cancelAllHyperliquidOrders, cancelHyperliquidOrders, cancelHyperliquidOrdersByCloid, cancelHyperliquidTwapOrder, computeHyperliquidMarketIocLimitPrice, createHyperliquidSubAccount, createMonotonicNonceFactory, depositToHyperliquidBridge, extractHyperliquidDex, extractHyperliquidOrderIds, fetchHyperliquidAllMids, fetchHyperliquidAssetCtxs, 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, normalizeHyperliquidMetaSymbol, normalizeSpotTokenName2 as normalizeSpotTokenName, parseSpotPairSymbol, placeHyperliquidOrder, placeHyperliquidTwapOrder, readHyperliquidAccountValue, readHyperliquidNumber, readHyperliquidPerpPosition, readHyperliquidPerpPositionSize, readHyperliquidSpotAccountValue, readHyperliquidSpotBalance, readHyperliquidSpotBalanceSize, recordHyperliquidBuilderApproval, recordHyperliquidTermsAcceptance, reserveHyperliquidRequestWeight, resolveHyperliquidAbstractionFromMode, resolveHyperliquidChain, resolveHyperliquidChainConfig, resolveHyperliquidErrorDetail, resolveHyperliquidOrderRef, resolveHyperliquidOrderSymbol, resolveHyperliquidPair, resolveHyperliquidRpcEnvVar, resolveHyperliquidStoreNetwork, resolveHyperliquidSymbol, resolveSpotMidCandidates, resolveSpotTokenCandidates, roundHyperliquidPriceToTick, scheduleHyperliquidCancel, sendHyperliquidSpot, setHyperliquidAccountAbstractionMode, setHyperliquidDexAbstraction, setHyperliquidPortfolioMargin, transferHyperliquidSubAccount, updateHyperliquidIsolatedMargin, updateHyperliquidLeverage, withdrawFromHyperliquid };
1971
2983
  //# sourceMappingURL=index.js.map
1972
2984
  //# sourceMappingURL=index.js.map