@continuumdao/ctm-mpc-defi 0.2.20 → 0.2.24

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.
Files changed (96) hide show
  1. package/dist/agent/catalog.cjs +256 -46
  2. package/dist/agent/catalog.cjs.map +1 -1
  3. package/dist/agent/catalog.d.ts +606 -154
  4. package/dist/agent/catalog.js +250 -47
  5. package/dist/agent/catalog.js.map +1 -1
  6. package/dist/agent/skills/gmx/SKILL.md +30 -0
  7. package/dist/agent/skills/hyperliquid/SKILL.md +32 -0
  8. package/dist/agent/skills/uniswap-v4/SKILL.md +83 -0
  9. package/dist/chains/evm/index.cjs +5 -1
  10. package/dist/chains/evm/index.cjs.map +1 -1
  11. package/dist/chains/evm/index.d.ts +4 -4
  12. package/dist/chains/evm/index.js +5 -1
  13. package/dist/chains/evm/index.js.map +1 -1
  14. package/dist/chains/near/index.d.ts +1 -1
  15. package/dist/chains/solana/index.d.ts +1 -1
  16. package/dist/core/index.cjs +25 -0
  17. package/dist/core/index.cjs.map +1 -1
  18. package/dist/core/index.d.ts +11 -36
  19. package/dist/core/index.js +23 -1
  20. package/dist/core/index.js.map +1 -1
  21. package/dist/eip712Multisign-xhXpUYp3.d.ts +36 -0
  22. package/dist/{envelope-CpBUh9eP.d.ts → envelope-C_bfuKab.d.ts} +3 -1
  23. package/dist/index.cjs +371 -13
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.ts +6 -5
  26. package/dist/index.js +369 -14
  27. package/dist/index.js.map +1 -1
  28. package/dist/protocols/evm/aave-v4/index.cjs +5 -1
  29. package/dist/protocols/evm/aave-v4/index.cjs.map +1 -1
  30. package/dist/protocols/evm/aave-v4/index.d.ts +1 -1
  31. package/dist/protocols/evm/aave-v4/index.js +5 -1
  32. package/dist/protocols/evm/aave-v4/index.js.map +1 -1
  33. package/dist/protocols/evm/circle-cctp/index.cjs +5 -1
  34. package/dist/protocols/evm/circle-cctp/index.cjs.map +1 -1
  35. package/dist/protocols/evm/circle-cctp/index.d.ts +2 -2
  36. package/dist/protocols/evm/circle-cctp/index.js +5 -1
  37. package/dist/protocols/evm/circle-cctp/index.js.map +1 -1
  38. package/dist/protocols/evm/curve-dao/index.cjs +5 -1
  39. package/dist/protocols/evm/curve-dao/index.cjs.map +1 -1
  40. package/dist/protocols/evm/curve-dao/index.d.ts +2 -2
  41. package/dist/protocols/evm/curve-dao/index.js +5 -1
  42. package/dist/protocols/evm/curve-dao/index.js.map +1 -1
  43. package/dist/protocols/evm/ethena/index.cjs +5 -1
  44. package/dist/protocols/evm/ethena/index.cjs.map +1 -1
  45. package/dist/protocols/evm/ethena/index.d.ts +1 -1
  46. package/dist/protocols/evm/ethena/index.js +5 -1
  47. package/dist/protocols/evm/ethena/index.js.map +1 -1
  48. package/dist/protocols/evm/euler-v2/index.cjs +5 -1
  49. package/dist/protocols/evm/euler-v2/index.cjs.map +1 -1
  50. package/dist/protocols/evm/euler-v2/index.d.ts +1 -1
  51. package/dist/protocols/evm/euler-v2/index.js +5 -1
  52. package/dist/protocols/evm/euler-v2/index.js.map +1 -1
  53. package/dist/protocols/evm/gmx/index.cjs +44 -1
  54. package/dist/protocols/evm/gmx/index.cjs.map +1 -1
  55. package/dist/protocols/evm/gmx/index.d.ts +23 -3
  56. package/dist/protocols/evm/gmx/index.js +43 -2
  57. package/dist/protocols/evm/gmx/index.js.map +1 -1
  58. package/dist/protocols/evm/hyperliquid/index.cjs +483 -208
  59. package/dist/protocols/evm/hyperliquid/index.cjs.map +1 -1
  60. package/dist/protocols/evm/hyperliquid/index.d.ts +168 -53
  61. package/dist/protocols/evm/hyperliquid/index.js +472 -210
  62. package/dist/protocols/evm/hyperliquid/index.js.map +1 -1
  63. package/dist/protocols/evm/lido/index.cjs +5 -1
  64. package/dist/protocols/evm/lido/index.cjs.map +1 -1
  65. package/dist/protocols/evm/lido/index.d.ts +1 -1
  66. package/dist/protocols/evm/lido/index.js +5 -1
  67. package/dist/protocols/evm/lido/index.js.map +1 -1
  68. package/dist/protocols/evm/maple/index.cjs +5 -1
  69. package/dist/protocols/evm/maple/index.cjs.map +1 -1
  70. package/dist/protocols/evm/maple/index.d.ts +1 -1
  71. package/dist/protocols/evm/maple/index.js +5 -1
  72. package/dist/protocols/evm/maple/index.js.map +1 -1
  73. package/dist/protocols/evm/morpho/index.cjs +5 -1
  74. package/dist/protocols/evm/morpho/index.cjs.map +1 -1
  75. package/dist/protocols/evm/morpho/index.d.ts +1 -1
  76. package/dist/protocols/evm/morpho/index.js +5 -1
  77. package/dist/protocols/evm/morpho/index.js.map +1 -1
  78. package/dist/protocols/evm/permit2/index.cjs +4 -0
  79. package/dist/protocols/evm/permit2/index.cjs.map +1 -1
  80. package/dist/protocols/evm/permit2/index.d.ts +1 -1
  81. package/dist/protocols/evm/permit2/index.js +4 -0
  82. package/dist/protocols/evm/permit2/index.js.map +1 -1
  83. package/dist/protocols/evm/sky/index.cjs +5 -1
  84. package/dist/protocols/evm/sky/index.cjs.map +1 -1
  85. package/dist/protocols/evm/sky/index.d.ts +1 -1
  86. package/dist/protocols/evm/sky/index.js +5 -1
  87. package/dist/protocols/evm/sky/index.js.map +1 -1
  88. package/dist/protocols/evm/uniswap-v4/index.cjs +1415 -13
  89. package/dist/protocols/evm/uniswap-v4/index.cjs.map +1 -1
  90. package/dist/protocols/evm/uniswap-v4/index.d.ts +353 -4
  91. package/dist/protocols/evm/uniswap-v4/index.js +1357 -15
  92. package/dist/protocols/evm/uniswap-v4/index.js.map +1 -1
  93. package/dist/{registry-Bv5o37_w.d.ts → registry-DxDSPQ-p.d.ts} +1 -1
  94. package/dist/{types-BfjWdw1j.d.ts → types-PZrvtjv8.d.ts} +2 -0
  95. package/dist/{types-DUeNJLr9.d.ts → types-RZWOTm8c.d.ts} +1 -1
  96. package/package.json +2 -2
@@ -1,4 +1,4 @@
1
- import { parseAbi, parseAbiParameters, parseAbiItem, getAddress, zeroAddress, formatUnits, parseUnits, createPublicClient, http, defineChain, encodeFunctionData, keccak256, encodeAbiParameters, decodeEventLog, decodeFunctionData, parseGwei, serializeTransaction, erc20Abi } from 'viem';
1
+ import { parseAbi, parseAbiParameters, parseAbiItem, getAddress, zeroAddress, formatUnits, parseUnits, createPublicClient, http, defineChain, encodeFunctionData, keccak256, encodeAbiParameters, decodeEventLog, decodeFunctionData, parseGwei, serializeTransaction, erc20Abi, hashTypedData, stringToHex } from 'viem';
2
2
  import { nodeFetchWithReadAuth, fetchChainFeeParams, gasLimitFromEstimateAndChainConfig, gweiToDecimalString, proposalTxParamsToFeeSnapshot, alignEip1559FeesWithLatestBase, getClientIdFromKeyGenResult } from '@continuumdao/continuum-node-sdk';
3
3
 
4
4
  // src/core/registry.ts
@@ -66,8 +66,18 @@ var UNIVERSAL_ROUTER_SPENDER = {
66
66
  143: "0x0d97dc33264bfc1c226207428a79b26757fb9dc3",
67
67
  59144: "0x661e93cca42afacb172121ef892830ca3b70f08d",
68
68
  4217: "0x1febb76be10aaf3a1402f04e8e835f2c382f7914",
69
- 196: "0x5507749f2c558bb3e162c6e90c314c092e7372ff"
69
+ 196: "0x5507749f2c558bb3e162c6e90c314c092e7372ff",
70
+ /** Robinhood Chain mainnet — Universal Router v2.1.1 (Trade API / interface) */
71
+ 4663: "0x8876789976decbfcbbbe364623c63652db8c0904"
70
72
  };
73
+ var UNIVERSAL_ROUTER_VERSION_BY_CHAIN = {
74
+ /** Robinhood has UR 2.1.1 only in the supported-chains table (no 2.0 Trade API column). */
75
+ 4663: "2.1.1"
76
+ };
77
+ function getUniswapUniversalRouterVersion(chainId) {
78
+ const v = UNIVERSAL_ROUTER_VERSION_BY_CHAIN[chainId];
79
+ return typeof v === "string" && v.trim() ? v.trim() : UNISWAP_UNIVERSAL_ROUTER_VERSION_DEFAULT;
80
+ }
71
81
  function isUniswapV4ChainSupported(chainId) {
72
82
  if (chainId == null) return false;
73
83
  const n = parseEvmChainIdToNumber(chainId);
@@ -104,8 +114,12 @@ var POSITION_MANAGER_BY_CHAIN = {
104
114
  11155111: "0x4B2C77d209D3405F41a037Ec6c77F7F5b8e2ca80",
105
115
  130: "0x0d97dc33264bfc1c226207428a79b26757fb9dc3",
106
116
  1868: "0x0e2850543f69f678257266e0907ff9a58b3f13de",
107
- 59144: "0x661e93cca42afacb172121ef892830ca3b70f08d"
117
+ 59144: "0x661e93cca42afacb172121ef892830ca3b70f08d",
118
+ /** Robinhood Chain mainnet */
119
+ 4663: "0x58daec3116aae6D93017bAAea7749052E8a04fA7"
108
120
  };
121
+ var ROBINHOOD_CHAIN_ID = 4663;
122
+ var ROBINHOOD_WETH_ADDRESS = "0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73";
109
123
  function getUniswapV4PositionManagerOrThrow(chainId) {
110
124
  const raw = POSITION_MANAGER_BY_CHAIN[chainId];
111
125
  if (!raw || !raw.startsWith("0x") || raw.length !== 42) {
@@ -158,8 +172,8 @@ function parseUniswapChainId(value) {
158
172
  function trimAddr(a) {
159
173
  return a.trim();
160
174
  }
161
- function isUniswapTokenInAddressNative(tokenIn) {
162
- const t = (tokenIn ?? "").toString().trim();
175
+ function isUniswapNativeTokenAddress(token) {
176
+ const t = (token ?? "").toString().trim();
163
177
  if (!t) return false;
164
178
  try {
165
179
  return getAddress(t) === zeroAddress;
@@ -167,6 +181,9 @@ function isUniswapTokenInAddressNative(tokenIn) {
167
181
  return t.toLowerCase() === "0x0000000000000000000000000000000000000000";
168
182
  }
169
183
  }
184
+ function isUniswapTokenInAddressNative(tokenIn) {
185
+ return isUniswapNativeTokenAddress(tokenIn);
186
+ }
170
187
  function isUniswapFullQuoteResponseNativeIn(stored) {
171
188
  if (!stored) return false;
172
189
  const q = stored.quote;
@@ -174,7 +191,16 @@ function isUniswapFullQuoteResponseNativeIn(stored) {
174
191
  const input = q.input;
175
192
  if (!input) return false;
176
193
  const raw = (input.address ?? input.token ?? "").toString().trim();
177
- return isUniswapTokenInAddressNative(raw);
194
+ return isUniswapNativeTokenAddress(raw);
195
+ }
196
+ function isUniswapFullQuoteResponseNativeOut(stored) {
197
+ if (!stored) return false;
198
+ const q = stored.quote;
199
+ if (!q || typeof q !== "object" || Array.isArray(q)) return false;
200
+ const output = q.output;
201
+ if (!output) return false;
202
+ const raw = (output.address ?? output.token ?? "").toString().trim();
203
+ return isUniswapNativeTokenAddress(raw);
178
204
  }
179
205
  async function fetchEthereumAddressForKeyGen(managementNodeUrl, keyGenId, readAuth = { bearerOnGet: true, jwt: null }, init) {
180
206
  const base = managementNodeUrl.trim().replace(/\/$/, "");
@@ -236,6 +262,31 @@ function buildUniswapQuoteRequestBody(args) {
236
262
  }
237
263
  return body;
238
264
  }
265
+ function resolveChainIdFromStoredUniswapQuote(stored) {
266
+ if (!stored) return 0;
267
+ const top = stored;
268
+ for (const key of ["tokenInChainId", "chainId"]) {
269
+ const raw = top[key];
270
+ if (raw != null && String(raw).trim() !== "") {
271
+ try {
272
+ return parseUniswapChainId(raw);
273
+ } catch {
274
+ }
275
+ }
276
+ }
277
+ const q = top.quote;
278
+ if (q && typeof q === "object" && !Array.isArray(q)) {
279
+ const input = q.input;
280
+ const chain = input?.chainId;
281
+ if (chain != null && String(chain).trim() !== "") {
282
+ try {
283
+ return parseUniswapChainId(chain);
284
+ } catch {
285
+ }
286
+ }
287
+ }
288
+ return 0;
289
+ }
239
290
  function errorMessageFromUniswapJsonBody(value) {
240
291
  if (value == null) return null;
241
292
  if (typeof value === "string") {
@@ -348,16 +399,18 @@ async function uniswapTradeQuote(args) {
348
399
  }
349
400
  const base = args.baseUrl && args.baseUrl.trim() ? args.baseUrl.trim().replace(/\/$/, "") : DEFAULT_TRADE_BASE;
350
401
  const quoteUrl = `${base}/quote`;
351
- const urv = args.universalRouterVersion && args.universalRouterVersion.trim() || "2.0";
402
+ const chainIdForRouter = parseUniswapChainId(resolveInputChainId(args));
403
+ const urv = args.universalRouterVersion && args.universalRouterVersion.trim() || getUniswapUniversalRouterVersion(chainIdForRouter);
352
404
  const body = buildUniswapQuoteRequestBody({ ...args, swapper });
353
- const nativeIn = isUniswapTokenInAddressNative(args.tokenIn);
405
+ const nativeIn = isUniswapNativeTokenAddress(args.tokenIn);
406
+ const nativeOut = isUniswapNativeTokenAddress(args.tokenOut);
354
407
  const headers = {
355
408
  ...UNISWAP_QUOTE_HEADERS_BASE,
356
409
  "x-api-key": apiKey,
357
410
  "x-universal-router-version": urv,
358
411
  "x-permit2-disabled": args.permit2Disabled === true ? "true" : "false",
359
- /** Native (0x0) as token in: Trade API needs this for classic routes and payable /swap. */
360
- "x-erc20eth-enabled": nativeIn ? "true" : "false"
412
+ /** Native (0x0) in or out: enables ERC20↔ETH routes (incl. UniswapX on Robinhood). */
413
+ "x-erc20eth-enabled": nativeIn || nativeOut ? "true" : "false"
361
414
  };
362
415
  const fetchFn = args.fetchImpl ?? globalThis.fetch;
363
416
  const res = await fetchFn(quoteUrl, {
@@ -368,7 +421,11 @@ async function uniswapTradeQuote(args) {
368
421
  });
369
422
  const text = await res.text();
370
423
  if (!res.ok) {
371
- throw new Error(messageFromUniswapHttpResponseBody(text, res.status, res.statusText));
424
+ let msg = messageFromUniswapHttpResponseBody(text, res.status, res.statusText);
425
+ if (res.status === 404 && chainIdForRouter === 4663) {
426
+ msg += " On Robinhood Chain, confirm USDG is 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168, use Native ETH or WETH (0x0Bd7\u2026) as output, and try at least ~$300 notional (UniswapX minimum).";
427
+ }
428
+ throw new Error(msg);
372
429
  }
373
430
  const forParse = text.replace(/^\uFEFF/, "");
374
431
  try {
@@ -596,6 +653,9 @@ function finalizeMultisign(input) {
596
653
  bodyForSign.value = valueWei.toString();
597
654
  }
598
655
  if (clientId) bodyForSign.clientId = clientId;
656
+ if (input.expiryDate != null && input.expiryDate > 0) {
657
+ bodyForSign.expiryDate = Math.floor(input.expiryDate);
658
+ }
599
659
  return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
600
660
  }
601
661
  function routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimit) {
@@ -759,7 +819,8 @@ async function buildEvmMultisignBatch(args) {
759
819
  destinationChainID: String(chainId),
760
820
  destinationAddress: args.destinationAddress ?? steps[0].to,
761
821
  legs,
762
- extraJSON: Object.keys(extraJSON).length > 0 ? extraJSON : void 0
822
+ extraJSON: Object.keys(extraJSON).length > 0 ? extraJSON : void 0,
823
+ expiryDate: context.expiryDate
763
824
  });
764
825
  const pv = args.payableValueWei;
765
826
  if (pv != null && pv > 0n) {
@@ -928,16 +989,17 @@ async function uniswapCreateSwap(args) {
928
989
  quote: classic,
929
990
  deadline
930
991
  };
931
- const urv = (args.universalRouterVersion ?? UNISWAP_UNIVERSAL_ROUTER_VERSION_DEFAULT).trim() || "2.0";
992
+ const urv = (args.universalRouterVersion ?? "").trim() || getUniswapUniversalRouterVersion(resolveChainIdFromStoredUniswapQuote(args.fullQuoteFromPermit));
932
993
  const fn = args.fetchImpl ?? globalThis.fetch;
933
994
  const nativeIn = isUniswapFullQuoteResponseNativeIn(args.fullQuoteFromPermit);
995
+ const nativeOut = isUniswapFullQuoteResponseNativeOut(args.fullQuoteFromPermit);
934
996
  const res = await fn(url, {
935
997
  method: "POST",
936
998
  headers: {
937
999
  "Content-Type": "application/json",
938
1000
  "x-api-key": args.uniswapApiKey.trim(),
939
1001
  "x-universal-router-version": urv,
940
- "x-erc20eth-enabled": nativeIn ? "true" : "false",
1002
+ "x-erc20eth-enabled": nativeIn || nativeOut ? "true" : "false",
941
1003
  "x-permit2-disabled": "true"
942
1004
  },
943
1005
  body: JSON.stringify(body)
@@ -2576,6 +2638,1271 @@ async function swapExactInput(args) {
2576
2638
  return swapFromQuote({ ...args, swap });
2577
2639
  }
2578
2640
 
2641
+ // src/core/defiProxy.ts
2642
+ async function postJsonViaOptionalProxy(args) {
2643
+ const proxy = args.proxyUrl?.trim();
2644
+ if (proxy) {
2645
+ const r2 = await fetch(proxy, {
2646
+ method: "POST",
2647
+ headers: { "content-type": "application/json" },
2648
+ body: JSON.stringify(args.proxyEnvelope ?? args.body)
2649
+ });
2650
+ if (!r2.ok) {
2651
+ const t = await r2.text().catch(() => "");
2652
+ throw new Error(t ? `Proxy HTTP ${r2.status}: ${t.slice(0, 200)}` : `Proxy HTTP ${r2.status}`);
2653
+ }
2654
+ return await r2.json();
2655
+ }
2656
+ const r = await fetch(args.directUrl, {
2657
+ method: "POST",
2658
+ headers: { "content-type": "application/json" },
2659
+ body: JSON.stringify(args.body)
2660
+ });
2661
+ if (!r.ok) {
2662
+ const t = await r.text().catch(() => "");
2663
+ throw new Error(t ? `HTTP ${r.status}: ${t.slice(0, 200)}` : `HTTP ${r.status}`);
2664
+ }
2665
+ return await r.json();
2666
+ }
2667
+
2668
+ // src/protocols/evm/uniswap-v4/theGraphApiKey.ts
2669
+ var THE_GRAPH_API_KEY_ENV = "THE_GRAPH_API_KEY";
2670
+ var THE_GRAPH_API_KEY_SIGNUP_URL = "https://thegraph.com/studio/apikeys/";
2671
+ var THE_GRAPH_API_KEY_DOCS_URL = "https://thegraph.com/docs/en/subgraphs/querying/managing-api-keys/";
2672
+ function isTheGraphRateLimitOrAuthError(message) {
2673
+ const m = message.toLowerCase();
2674
+ return m.includes("429") || m.includes("rate limit") || m.includes("too many requests") || m.includes("auth error") || m.includes("missing authorization") || m.includes("unauthorized");
2675
+ }
2676
+ function theGraphApiKeyVariablesHint() {
2677
+ return [
2678
+ `Set \`${THE_GRAPH_API_KEY_ENV}\` in Node \u2192 AI Agent \u2192 Variables (+ Add: paste the key as the value).`,
2679
+ `Get a free API key at ${THE_GRAPH_API_KEY_SIGNUP_URL}`
2680
+ ].join(" ");
2681
+ }
2682
+ function theGraphRateLimitErrorMessage(baseError) {
2683
+ return `${baseError}
2684
+
2685
+ The Graph rate limit or auth issue \u2014 ${theGraphApiKeyVariablesHint()}`;
2686
+ }
2687
+
2688
+ // src/protocols/evm/uniswap-v4/subgraph.ts
2689
+ var UNISWAP_V4_SUBGRAPH_ID_BY_CHAIN_ID = {
2690
+ 1: "DiYPVdygkfjDWhbxGSqAQxwBKmfKnkWQojqeM2rkLb3G",
2691
+ // Ethereum
2692
+ 10: "3Tn7Y1NJAr4ySKm7KFu1dwvH2WM3mHJnXzXAxQsdBDvW",
2693
+ // Optimism
2694
+ 56: "2qQpC8inZPZL4tYfRQPFGZhsE8mYzE67n5z3Yf5uuKMu",
2695
+ // BSC
2696
+ 130: "EoCvJ5tyMLMJcTnLQwWpjAtPdn74PcrZgzfcT5bYxNBH",
2697
+ // Unichain
2698
+ 137: "CwpebM66AH5uqS5sreKij8yEkkPcHvmyEs7EwFtdM5ND",
2699
+ // Polygon
2700
+ 8453: "HNCFA9TyBqpo5qpe6QreQABAA1kV8g46mhkCcicu6v2R",
2701
+ // Base
2702
+ 81457: "FCHYK3Ab6bBnkfeCKRhFbs1Q8rX4yt6rKJibpDTC74ns",
2703
+ // Blast
2704
+ 42161: "G5TsTKNi8yhPSV7kycaE23oWbqv9zzNqR49FoEQjzq1r",
2705
+ // Arbitrum
2706
+ 43114: "49JxRo9FGxWpSf5Y5GKQPj5NUpX2HhpoZHpGzNEWQZjq"
2707
+ // Avalanche
2708
+ };
2709
+ var SUBGRAPH_PAGE_SIZE = 1e3;
2710
+ function uniswapV4SubgraphIdForChain(chainId) {
2711
+ return UNISWAP_V4_SUBGRAPH_ID_BY_CHAIN_ID[chainId];
2712
+ }
2713
+ function uniswapV4SubgraphSupportedChainIds() {
2714
+ return Object.keys(UNISWAP_V4_SUBGRAPH_ID_BY_CHAIN_ID).map(Number).sort((a, b) => a - b);
2715
+ }
2716
+ function uniswapV4OhlcvSupportedChainIds() {
2717
+ const ids = new Set(uniswapV4SubgraphSupportedChainIds());
2718
+ ids.add(4663);
2719
+ return [...ids].sort((a, b) => a - b);
2720
+ }
2721
+ function uniswapV4SubgraphUrlForChain(chainId, apiKey) {
2722
+ const id = uniswapV4SubgraphIdForChain(chainId);
2723
+ if (!id) {
2724
+ throw new Error(
2725
+ `Uniswap V4 subgraph is not configured for chainId ${chainId}. Supported: ${uniswapV4SubgraphSupportedChainIds().join(", ")}`
2726
+ );
2727
+ }
2728
+ const key = apiKey?.trim();
2729
+ if (key) {
2730
+ return `https://gateway.thegraph.com/api/${key}/subgraphs/id/${id}`;
2731
+ }
2732
+ return `https://gateway.thegraph.com/api/subgraphs/id/${id}`;
2733
+ }
2734
+ function resolveTheGraphApiKey(override) {
2735
+ const fromOverride = override?.trim();
2736
+ if (fromOverride) return fromOverride;
2737
+ const raw = process.env[THE_GRAPH_API_KEY_ENV]?.trim();
2738
+ return raw || void 0;
2739
+ }
2740
+ async function postUniswapV4SubgraphGql(chainId, query, variables, theGraphApiKey) {
2741
+ const url = uniswapV4SubgraphUrlForChain(chainId, resolveTheGraphApiKey(theGraphApiKey));
2742
+ const body = { query, variables: variables ?? {} };
2743
+ let j;
2744
+ try {
2745
+ j = await postJsonViaOptionalProxy({ directUrl: url, body });
2746
+ } catch (e) {
2747
+ const msg = e instanceof Error ? e.message : String(e);
2748
+ if (isTheGraphRateLimitOrAuthError(msg)) {
2749
+ throw new Error(theGraphRateLimitErrorMessage(msg));
2750
+ }
2751
+ throw e;
2752
+ }
2753
+ if (j.errors?.length) {
2754
+ const msg = j.errors.map((e) => e.message ?? "GraphQL error").join("; ");
2755
+ if (isTheGraphRateLimitOrAuthError(msg)) {
2756
+ throw new Error(theGraphRateLimitErrorMessage(msg));
2757
+ }
2758
+ throw new Error(msg);
2759
+ }
2760
+ if (j.data == null) {
2761
+ throw new Error("Uniswap V4 subgraph: empty response");
2762
+ }
2763
+ return j.data;
2764
+ }
2765
+ var POOL_HOUR_PAGE_GQL = `
2766
+ query UniswapV4PoolHourDatas($pool: String!, $from: Int!, $to: Int!, $first: Int!, $skip: Int!) {
2767
+ poolHourDatas(
2768
+ where: { pool: $pool, periodStartUnix_gte: $from, periodStartUnix_lt: $to }
2769
+ orderBy: periodStartUnix
2770
+ orderDirection: asc
2771
+ first: $first
2772
+ skip: $skip
2773
+ ) {
2774
+ periodStartUnix
2775
+ open
2776
+ high
2777
+ low
2778
+ close
2779
+ volumeUSD
2780
+ }
2781
+ }`;
2782
+ var POOL_DAY_PAGE_GQL = `
2783
+ query UniswapV4PoolDayDatas($pool: String!, $from: Int!, $to: Int!, $first: Int!, $skip: Int!) {
2784
+ poolDayDatas(
2785
+ where: { pool: $pool, date_gte: $from, date_lt: $to }
2786
+ orderBy: date
2787
+ orderDirection: asc
2788
+ first: $first
2789
+ skip: $skip
2790
+ ) {
2791
+ date
2792
+ open
2793
+ high
2794
+ low
2795
+ close
2796
+ volumeUSD
2797
+ }
2798
+ }`;
2799
+ var SWAPS_PAGE_GQL = `
2800
+ query UniswapV4Swaps($pool: String!, $from: BigInt!, $to: BigInt!, $first: Int!, $skip: Int!) {
2801
+ swaps(
2802
+ where: { pool: $pool, timestamp_gte: $from, timestamp_lt: $to }
2803
+ orderBy: timestamp
2804
+ orderDirection: asc
2805
+ first: $first
2806
+ skip: $skip
2807
+ ) {
2808
+ timestamp
2809
+ amountUSD
2810
+ token0Price
2811
+ sqrtPriceX96
2812
+ }
2813
+ }`;
2814
+ var POOL_META_GQL = `
2815
+ query UniswapV4PoolMeta($id: ID!) {
2816
+ pool(id: $id) {
2817
+ id
2818
+ token0 { symbol }
2819
+ token1 { symbol }
2820
+ }
2821
+ }`;
2822
+ async function paginateSubgraphRows(fetchPage, maxPages) {
2823
+ const rows = [];
2824
+ for (let page = 0; page < maxPages; page++) {
2825
+ const batch = await fetchPage(page * SUBGRAPH_PAGE_SIZE);
2826
+ rows.push(...batch);
2827
+ if (batch.length < SUBGRAPH_PAGE_SIZE) {
2828
+ return { rows, truncated: false };
2829
+ }
2830
+ }
2831
+ return { rows, truncated: true };
2832
+ }
2833
+ function normalizePoolId(poolReference) {
2834
+ return poolReference.trim().toLowerCase();
2835
+ }
2836
+ async function fetchUniswapV4PoolHourDatas(args) {
2837
+ const pool = normalizePoolId(args.poolReference);
2838
+ const maxPages = args.maxPages ?? 20;
2839
+ return paginateSubgraphRows(async (skip) => {
2840
+ const data = await postUniswapV4SubgraphGql(args.chainId, POOL_HOUR_PAGE_GQL, {
2841
+ pool,
2842
+ from: args.fromSec,
2843
+ to: args.toSec,
2844
+ first: SUBGRAPH_PAGE_SIZE,
2845
+ skip
2846
+ });
2847
+ return data.poolHourDatas ?? [];
2848
+ }, maxPages);
2849
+ }
2850
+ async function fetchUniswapV4PoolDayDatas(args) {
2851
+ const pool = normalizePoolId(args.poolReference);
2852
+ const maxPages = args.maxPages ?? 20;
2853
+ return paginateSubgraphRows(async (skip) => {
2854
+ const data = await postUniswapV4SubgraphGql(args.chainId, POOL_DAY_PAGE_GQL, {
2855
+ pool,
2856
+ from: args.fromSec,
2857
+ to: args.toSec,
2858
+ first: SUBGRAPH_PAGE_SIZE,
2859
+ skip
2860
+ });
2861
+ return data.poolDayDatas ?? [];
2862
+ }, maxPages);
2863
+ }
2864
+ async function fetchUniswapV4Swaps(args) {
2865
+ const pool = normalizePoolId(args.poolReference);
2866
+ const maxPages = args.maxPages ?? 50;
2867
+ return paginateSubgraphRows(async (skip) => {
2868
+ const data = await postUniswapV4SubgraphGql(
2869
+ args.chainId,
2870
+ SWAPS_PAGE_GQL,
2871
+ {
2872
+ pool,
2873
+ from: String(args.fromSec),
2874
+ to: String(args.toSec),
2875
+ first: SUBGRAPH_PAGE_SIZE,
2876
+ skip
2877
+ }
2878
+ );
2879
+ return data.swaps ?? [];
2880
+ }, maxPages);
2881
+ }
2882
+ async function fetchUniswapV4PoolMeta(args) {
2883
+ const id = normalizePoolId(args.poolReference);
2884
+ const data = await postUniswapV4SubgraphGql(
2885
+ args.chainId,
2886
+ POOL_META_GQL,
2887
+ { id }
2888
+ );
2889
+ return data.pool ?? null;
2890
+ }
2891
+
2892
+ // src/protocols/evm/uniswap-v4/bitqueryApiKey.ts
2893
+ var BITQUERY_API_KEY_ENV = "BITQUERY_API_KEY";
2894
+ var BITQUERY_API_KEY_SIGNUP_URL = "https://account.bitquery.io/";
2895
+ var BITQUERY_API_KEY_DOCS_URL = "https://bitquery.io/blockchains/robinhood-chain-api";
2896
+ var BITQUERY_GRAPHQL_URL = "https://graphql.bitquery.io";
2897
+ function resolveBitqueryApiKey(override) {
2898
+ const fromOverride = override?.trim();
2899
+ if (fromOverride) return fromOverride;
2900
+ const raw = process.env[BITQUERY_API_KEY_ENV]?.trim();
2901
+ return raw || void 0;
2902
+ }
2903
+ function isBitqueryAuthOrRateLimitError(message) {
2904
+ const m = message.toLowerCase();
2905
+ const mentionsBitquery = m.includes("bitquery") || m.includes("bitquery_api_key") || m.includes("robinhood chain ohlcv");
2906
+ if (!mentionsBitquery) return false;
2907
+ return m.includes("429") || m.includes("rate limit") || m.includes("too many requests") || m.includes("auth") || m.includes("unauthorized") || m.includes("forbidden") || m.includes("api key") || m.includes("access token") || m.includes("required");
2908
+ }
2909
+ function bitqueryApiKeyVariablesHint() {
2910
+ return [
2911
+ `Set \`${BITQUERY_API_KEY_ENV}\` in Node \u2192 AI Agent \u2192 Variables (+ Add: paste the key as the value).`,
2912
+ `Get an API key at ${BITQUERY_API_KEY_SIGNUP_URL}`
2913
+ ].join(" ");
2914
+ }
2915
+ function bitqueryAuthErrorMessage(baseError) {
2916
+ return `${baseError}
2917
+
2918
+ Bitquery auth or rate limit \u2014 ${bitqueryApiKeyVariablesHint()}`;
2919
+ }
2920
+ function requireBitqueryApiKey(override) {
2921
+ const key = resolveBitqueryApiKey(override);
2922
+ if (!key) {
2923
+ throw new Error(
2924
+ `Bitquery API key is required for Robinhood Chain OHLCV. ${bitqueryApiKeyVariablesHint()}`
2925
+ );
2926
+ }
2927
+ return key;
2928
+ }
2929
+
2930
+ // src/protocols/evm/uniswap-v4/ohlcvInterval.ts
2931
+ var UNISWAP_V4_OHLCV_INTERVALS = [
2932
+ "1m",
2933
+ "5m",
2934
+ "15m",
2935
+ "30m",
2936
+ "1h",
2937
+ "2h",
2938
+ "4h",
2939
+ "6h",
2940
+ "8h",
2941
+ "12h",
2942
+ "1d",
2943
+ "2d",
2944
+ "3d",
2945
+ "1w",
2946
+ "1M"
2947
+ ];
2948
+ var INTERVAL_SECONDS = {
2949
+ "1m": 60,
2950
+ "5m": 300,
2951
+ "15m": 900,
2952
+ "30m": 1800,
2953
+ "1h": 3600,
2954
+ "2h": 7200,
2955
+ "4h": 14400,
2956
+ "6h": 21600,
2957
+ "8h": 28800,
2958
+ "12h": 43200,
2959
+ "1d": 86400,
2960
+ "2d": 172800,
2961
+ "3d": 259200,
2962
+ "1w": 604800,
2963
+ "1M": 2592e3
2964
+ };
2965
+ function assertUniswapV4OhlcvInterval(interval) {
2966
+ const normalized = interval.trim();
2967
+ if (!UNISWAP_V4_OHLCV_INTERVALS.includes(normalized)) {
2968
+ throw new Error(
2969
+ `Invalid interval "${interval}". Valid: ${UNISWAP_V4_OHLCV_INTERVALS.join(", ")}`
2970
+ );
2971
+ }
2972
+ return normalized;
2973
+ }
2974
+ function uniswapV4OhlcvIntervalToBucketSec(interval) {
2975
+ return INTERVAL_SECONDS[interval];
2976
+ }
2977
+ function resolveUniswapV4OhlcvDataTier(intervalRaw) {
2978
+ const interval = assertUniswapV4OhlcvInterval(intervalRaw);
2979
+ const bucketSec = uniswapV4OhlcvIntervalToBucketSec(interval);
2980
+ if (bucketSec === 3600) {
2981
+ return { kind: "subgraph_native", entity: "hourly", bucketSec, interval };
2982
+ }
2983
+ if (bucketSec === 86400) {
2984
+ return { kind: "subgraph_native", entity: "daily", bucketSec, interval };
2985
+ }
2986
+ if (bucketSec < 3600) {
2987
+ return { kind: "swaps", bucketSec, interval };
2988
+ }
2989
+ if (bucketSec < 86400 && bucketSec % 3600 === 0) {
2990
+ return { kind: "subgraph_aggregate", baseEntity: "hourly", bucketSec, interval };
2991
+ }
2992
+ if (bucketSec > 86400 && bucketSec % 86400 === 0) {
2993
+ return { kind: "subgraph_aggregate", baseEntity: "daily", bucketSec, interval };
2994
+ }
2995
+ throw new Error(
2996
+ `Interval "${interval}" is not supported for Uniswap V4 OHLCV routing. Valid: ${UNISWAP_V4_OHLCV_INTERVALS.join(", ")}`
2997
+ );
2998
+ }
2999
+
3000
+ // src/protocols/evm/uniswap-v4/bitqueryOhlcv.ts
3001
+ function intervalToBitquerySpec(interval) {
3002
+ const bucketSec = uniswapV4OhlcvIntervalToBucketSec(interval);
3003
+ if (bucketSec < 3600) {
3004
+ return { count: Math.max(1, Math.floor(bucketSec / 60)), in: "minutes" };
3005
+ }
3006
+ if (bucketSec < 86400) {
3007
+ return { count: Math.max(1, Math.floor(bucketSec / 3600)), in: "hours" };
3008
+ }
3009
+ return { count: Math.max(1, Math.floor(bucketSec / 86400)), in: "days" };
3010
+ }
3011
+ function parseIsoOrUnixToMs(raw) {
3012
+ if (raw == null) return null;
3013
+ if (typeof raw === "number" && Number.isFinite(raw)) {
3014
+ return raw > 1e12 ? raw : raw * 1e3;
3015
+ }
3016
+ const s = String(raw).trim();
3017
+ if (!s) return null;
3018
+ if (/^\d+$/.test(s)) {
3019
+ const n = Number(s);
3020
+ return n > 1e12 ? n : n * 1e3;
3021
+ }
3022
+ const ms = Date.parse(s);
3023
+ return Number.isFinite(ms) ? ms : null;
3024
+ }
3025
+ function numToString(raw) {
3026
+ if (raw == null) return null;
3027
+ const n = typeof raw === "number" ? raw : Number(String(raw).trim());
3028
+ if (!Number.isFinite(n)) return null;
3029
+ return String(n);
3030
+ }
3031
+ function mapBitqueryOhlcvRowsToCandles(rows) {
3032
+ const out = [];
3033
+ for (const row of rows) {
3034
+ const timestampMs = parseIsoOrUnixToMs(row.Block?.Time);
3035
+ if (timestampMs == null) continue;
3036
+ const open = numToString(row.Trade?.open);
3037
+ const high = numToString(row.Trade?.high);
3038
+ const low = numToString(row.Trade?.low);
3039
+ const close = numToString(row.Trade?.close);
3040
+ if (open == null || high == null || low == null || close == null) continue;
3041
+ const volume = numToString(row.volumeUsd) ?? numToString(row.volume) ?? void 0;
3042
+ out.push({
3043
+ timestampMs,
3044
+ open,
3045
+ high,
3046
+ low,
3047
+ close,
3048
+ ...volume != null ? { volume } : {}
3049
+ });
3050
+ }
3051
+ return out.sort((a, b) => a.timestampMs - b.timestampMs);
3052
+ }
3053
+ function buildCurrencyWhere(filter) {
3054
+ if (filter.kind === "symbol") {
3055
+ const symbol = filter.symbol.trim().replace(/"/g, "");
3056
+ return `Currency: { Symbol: { is: "${symbol}" } }`;
3057
+ }
3058
+ const address = getAddress(filter.address).toLowerCase();
3059
+ return `Currency: { SmartContract: { is: "${address}" } }`;
3060
+ }
3061
+ function buildBitqueryOhlcvQuery(args) {
3062
+ const spec = intervalToBitquerySpec(args.interval);
3063
+ const currencyWhere = buildCurrencyWhere(args.filter);
3064
+ return `query RobinhoodUniswapOhlcv {
3065
+ EVM(network: robinhood) {
3066
+ DEXTradeByTokens(
3067
+ limit: { count: ${args.limit} }
3068
+ orderBy: { ascendingByField: "Block_Time" }
3069
+ where: {
3070
+ TransactionStatus: { Success: true }
3071
+ Trade: {
3072
+ Dex: { ProtocolName: { is: "uniswap_v4" } }
3073
+ ${currencyWhere}
3074
+ }
3075
+ Block: { Time: { since: "${args.sinceIso}", till: "${args.tillIso}" } }
3076
+ }
3077
+ ) {
3078
+ Block { Time(interval: { count: ${spec.count}, in: ${spec.in} }) }
3079
+ Trade {
3080
+ open: PriceInUSD(minimum: Block_Number)
3081
+ high: PriceInUSD(maximum: Trade_PriceInUSD)
3082
+ low: PriceInUSD(minimum: Trade_PriceInUSD)
3083
+ close: PriceInUSD(maximum: Block_Number)
3084
+ }
3085
+ volumeUsd: sum(of: Trade_Side_AmountInUSD)
3086
+ }
3087
+ }
3088
+ }`;
3089
+ }
3090
+ function isRobinhoodOhlcvChain(chainId) {
3091
+ return chainId === ROBINHOOD_CHAIN_ID;
3092
+ }
3093
+ function resolveRobinhoodChartCurrency(args) {
3094
+ const symbol = args.currencySymbol?.trim();
3095
+ if (symbol) return { kind: "symbol", symbol };
3096
+ const direct = args.currencyAddress?.trim();
3097
+ if (direct && direct.startsWith("0x")) {
3098
+ return { kind: "address", address: getAddress(direct) };
3099
+ }
3100
+ const weth = ROBINHOOD_WETH_ADDRESS.toLowerCase();
3101
+ const zero = zeroAddress.toLowerCase();
3102
+ const candidates = [args.tokenOut, args.tokenIn].map((a) => a?.trim()).filter((a) => Boolean(a && a.startsWith("0x"))).map((a) => getAddress(a));
3103
+ const nonWeth = candidates.find((a) => {
3104
+ const lower = a.toLowerCase();
3105
+ return lower !== weth && lower !== zero;
3106
+ });
3107
+ if (nonWeth) return { kind: "address", address: nonWeth };
3108
+ if (candidates[0]) return { kind: "address", address: candidates[0] };
3109
+ throw new Error(
3110
+ "Robinhood OHLCV requires currencyAddress, currencySymbol, or tokenIn/tokenOut (existingPool)."
3111
+ );
3112
+ }
3113
+ function currencyFilterLabel(filter) {
3114
+ if (filter.kind === "symbol") return filter.symbol.toUpperCase();
3115
+ return `${filter.address.slice(0, 6)}\u2026${filter.address.slice(-4)}`;
3116
+ }
3117
+ async function fetchRobinhoodBitqueryOhlcv(args) {
3118
+ const interval = assertUniswapV4OhlcvInterval(args.interval);
3119
+ const apiKey = requireBitqueryApiKey(args.apiKey);
3120
+ const limit = Math.max(1, Math.min(1e3, Math.floor(args.limit ?? 500)));
3121
+ const sinceIso = new Date(args.startTimeMs).toISOString();
3122
+ const tillIso = new Date(args.endTimeMs).toISOString();
3123
+ const query = buildBitqueryOhlcvQuery({
3124
+ filter: args.filter,
3125
+ interval,
3126
+ sinceIso,
3127
+ tillIso,
3128
+ limit
3129
+ });
3130
+ let json;
3131
+ try {
3132
+ const res = await fetch(BITQUERY_GRAPHQL_URL, {
3133
+ method: "POST",
3134
+ headers: {
3135
+ "content-type": "application/json",
3136
+ Authorization: `Bearer ${apiKey}`
3137
+ },
3138
+ body: JSON.stringify({ query })
3139
+ });
3140
+ const text = await res.text();
3141
+ if (!res.ok) {
3142
+ const msg = text ? `Bitquery HTTP ${res.status}: ${text.slice(0, 300)}` : `Bitquery HTTP ${res.status}`;
3143
+ throw new Error(
3144
+ isBitqueryAuthOrRateLimitError(msg) ? bitqueryAuthErrorMessage(msg) : msg
3145
+ );
3146
+ }
3147
+ json = JSON.parse(text);
3148
+ } catch (e) {
3149
+ const msg = e instanceof Error ? e.message : String(e);
3150
+ if (isBitqueryAuthOrRateLimitError(msg) && !msg.includes(BITQUERY_GRAPHQL_URL)) {
3151
+ throw new Error(bitqueryAuthErrorMessage(msg));
3152
+ }
3153
+ throw e instanceof Error ? e : new Error(msg);
3154
+ }
3155
+ if (json.errors?.length) {
3156
+ const msg = json.errors.map((e) => e.message ?? "GraphQL error").join("; ");
3157
+ throw new Error(
3158
+ isBitqueryAuthOrRateLimitError(msg) ? bitqueryAuthErrorMessage(msg) : `Bitquery GraphQL: ${msg}`
3159
+ );
3160
+ }
3161
+ const rows = json.data?.EVM?.DEXTradeByTokens ?? [];
3162
+ const candles = mapBitqueryOhlcvRowsToCandles(rows);
3163
+ const warnings = [];
3164
+ if (candles.length === 0) {
3165
+ warnings.push("No Bitquery Uniswap v4 candles for this token and window on Robinhood Chain.");
3166
+ }
3167
+ if (rows.length >= limit) {
3168
+ warnings.push("Bitquery row limit reached; history may be truncated. Shorten lookback or coarsen interval.");
3169
+ }
3170
+ return { candles, warnings };
3171
+ }
3172
+
3173
+ // src/protocols/evm/uniswap-v4/swapCandles.ts
3174
+ function parseDecimal(raw) {
3175
+ if (raw == null) return null;
3176
+ const n = Number(raw);
3177
+ return Number.isFinite(n) && n > 0 ? n : null;
3178
+ }
3179
+ function priceFromSwapRow(row) {
3180
+ const fromToken0 = parseDecimal(row.token0Price);
3181
+ if (fromToken0 != null) return fromToken0;
3182
+ const sqrt = row.sqrtPriceX96?.trim();
3183
+ if (!sqrt) return null;
3184
+ try {
3185
+ const sqrtPrice = BigInt(sqrt);
3186
+ if (sqrtPrice <= 0n) return null;
3187
+ const ratio = Number(sqrtPrice) / 2 ** 96;
3188
+ const price = ratio * ratio;
3189
+ return Number.isFinite(price) && price > 0 ? price : null;
3190
+ } catch {
3191
+ return null;
3192
+ }
3193
+ }
3194
+ function bucketStartSec(timestampSec, bucketSec) {
3195
+ return Math.floor(timestampSec / bucketSec) * bucketSec;
3196
+ }
3197
+ function bucketUniswapV4SwapsIntoCandles(args) {
3198
+ const buckets = /* @__PURE__ */ new Map();
3199
+ for (const swap of args.swaps) {
3200
+ const ts = Number(swap.timestamp);
3201
+ const price = priceFromSwapRow(swap);
3202
+ if (!Number.isFinite(ts) || price == null) continue;
3203
+ const bucket = bucketStartSec(Math.floor(ts), args.bucketSec);
3204
+ const vol = parseDecimal(swap.amountUSD) ?? 0;
3205
+ const existing = buckets.get(bucket);
3206
+ if (!existing) {
3207
+ buckets.set(bucket, { open: price, high: price, low: price, close: price, volume: vol });
3208
+ continue;
3209
+ }
3210
+ existing.close = price;
3211
+ if (price > existing.high) existing.high = price;
3212
+ if (price < existing.low) existing.low = price;
3213
+ existing.volume += vol;
3214
+ }
3215
+ return [...buckets.entries()].sort(([a], [b]) => a - b).map(([bucketSec, bar]) => ({
3216
+ timestampMs: bucketSec * 1e3,
3217
+ open: String(bar.open),
3218
+ high: String(bar.high),
3219
+ low: String(bar.low),
3220
+ close: String(bar.close),
3221
+ volume: bar.volume > 0 ? String(bar.volume) : void 0
3222
+ }));
3223
+ }
3224
+
3225
+ // src/protocols/evm/uniswap-v4/ohlcv.ts
3226
+ var MAX_CANDLES = 2e3;
3227
+ function invertCandleRow(row) {
3228
+ const inv = (raw) => {
3229
+ const n = Number(raw);
3230
+ if (!Number.isFinite(n) || n <= 0) return raw;
3231
+ return String(1 / n);
3232
+ };
3233
+ const open = inv(row.open);
3234
+ const close = inv(row.close);
3235
+ const highRaw = Number(row.high);
3236
+ const lowRaw = Number(row.low);
3237
+ const high = Number.isFinite(highRaw) && Number.isFinite(lowRaw) && highRaw > 0 && lowRaw > 0 ? String(1 / Math.min(highRaw, lowRaw)) : inv(row.high);
3238
+ const low = Number.isFinite(highRaw) && Number.isFinite(lowRaw) && highRaw > 0 && lowRaw > 0 ? String(1 / Math.max(highRaw, lowRaw)) : inv(row.low);
3239
+ return {
3240
+ timestampMs: row.timestampMs,
3241
+ open,
3242
+ high,
3243
+ low,
3244
+ close,
3245
+ ...row.volume != null ? { volume: row.volume } : {}
3246
+ };
3247
+ }
3248
+ function mapSubgraphHourRow(row) {
3249
+ return {
3250
+ timestampMs: row.periodStartUnix * 1e3,
3251
+ open: row.open,
3252
+ high: row.high,
3253
+ low: row.low,
3254
+ close: row.close,
3255
+ volume: row.volumeUSD
3256
+ };
3257
+ }
3258
+ function mapSubgraphDayRow(row) {
3259
+ return {
3260
+ timestampMs: row.date * 1e3,
3261
+ open: row.open,
3262
+ high: row.high,
3263
+ low: row.low,
3264
+ close: row.close,
3265
+ volume: row.volumeUSD
3266
+ };
3267
+ }
3268
+ function aggregateOhlcvCandles(bars, bucketSec) {
3269
+ if (bars.length === 0) return [];
3270
+ const buckets = /* @__PURE__ */ new Map();
3271
+ for (const bar of bars) {
3272
+ const bucketStart = Math.floor(bar.timestampMs / 1e3 / bucketSec) * bucketSec;
3273
+ const list = buckets.get(bucketStart) ?? [];
3274
+ list.push(bar);
3275
+ buckets.set(bucketStart, list);
3276
+ }
3277
+ return [...buckets.entries()].sort(([a], [b]) => a - b).map(([bucketStartSec2, group]) => {
3278
+ const sorted = [...group].sort((a, b) => a.timestampMs - b.timestampMs);
3279
+ const first = sorted[0];
3280
+ const last = sorted[sorted.length - 1];
3281
+ let high = Number(first.high);
3282
+ let low = Number(first.low);
3283
+ let volume = 0;
3284
+ for (const row of sorted) {
3285
+ const h = Number(row.high);
3286
+ const l = Number(row.low);
3287
+ if (Number.isFinite(h) && h > high) high = h;
3288
+ if (Number.isFinite(l) && l < low) low = l;
3289
+ const v = row.volume != null ? Number(row.volume) : 0;
3290
+ if (Number.isFinite(v)) volume += v;
3291
+ }
3292
+ return {
3293
+ timestampMs: bucketStartSec2 * 1e3,
3294
+ open: first.open,
3295
+ high: String(high),
3296
+ low: String(low),
3297
+ close: last.close,
3298
+ volume: volume > 0 ? String(volume) : void 0
3299
+ };
3300
+ });
3301
+ }
3302
+ function windowFromArgs(args) {
3303
+ const nowMs = Date.now();
3304
+ if (args.startTimeMs != null || args.endTimeMs != null) {
3305
+ const endTimeMs = args.endTimeMs ?? nowMs;
3306
+ const startTimeMs = args.startTimeMs ?? endTimeMs - 7 * 864e5;
3307
+ if (startTimeMs >= endTimeMs) {
3308
+ throw new Error("startTimeMs must be before endTimeMs.");
3309
+ }
3310
+ return { startTimeMs, endTimeMs };
3311
+ }
3312
+ if (args.lookbackHours != null) {
3313
+ const hours = Math.max(1, Math.floor(args.lookbackHours));
3314
+ return { startTimeMs: nowMs - hours * 36e5, endTimeMs: nowMs };
3315
+ }
3316
+ const days = args.lookbackDays ?? 7;
3317
+ const lookbackDays = Math.max(1, Math.floor(days));
3318
+ return { startTimeMs: nowMs - lookbackDays * 864e5, endTimeMs: nowMs };
3319
+ }
3320
+ function candleWindowFromRows(candles) {
3321
+ if (candles.length === 0) return {};
3322
+ let startTimeMs = candles[0].timestampMs;
3323
+ let endTimeMs = candles[0].timestampMs;
3324
+ for (const c of candles) {
3325
+ if (c.timestampMs < startTimeMs) startTimeMs = c.timestampMs;
3326
+ if (c.timestampMs > endTimeMs) endTimeMs = c.timestampMs;
3327
+ }
3328
+ return { startTimeMs, endTimeMs };
3329
+ }
3330
+ async function resolveUniswapV4OhlcvPool(args) {
3331
+ const chainId = parseUniswapChainId(args.chainId);
3332
+ if (args.poolPreset?.trim()) {
3333
+ const preset = resolveUniswapV4LpPoolPreset({ chainId, presetId: args.poolPreset });
3334
+ return {
3335
+ poolReference: preset.poolReference,
3336
+ pairLabel: preset.pairLabel,
3337
+ token0Symbol: preset.token0Symbol,
3338
+ token1Symbol: preset.token1Symbol
3339
+ };
3340
+ }
3341
+ if (args.poolReference?.trim()) {
3342
+ const poolReference = args.poolReference.trim();
3343
+ let pairLabel = poolReference.slice(0, 10) + "\u2026";
3344
+ let token0Symbol;
3345
+ let token1Symbol;
3346
+ try {
3347
+ const meta = await fetchUniswapV4PoolMeta({ chainId, poolReference });
3348
+ if (meta?.token0?.symbol && meta?.token1?.symbol) {
3349
+ token0Symbol = meta.token0.symbol;
3350
+ token1Symbol = meta.token1.symbol;
3351
+ pairLabel = `${meta.token0.symbol}/${meta.token1.symbol}`;
3352
+ }
3353
+ } catch {
3354
+ }
3355
+ return { poolReference, pairLabel, token0Symbol, token1Symbol };
3356
+ }
3357
+ const existing = args.existingPool;
3358
+ if (existing?.token0Address && existing?.token1Address) {
3359
+ const resolved = resolveUniswapV4LpExistingPoolFromKey({
3360
+ token0Address: existing.token0Address,
3361
+ token1Address: existing.token1Address,
3362
+ fee: existing.fee ?? 0,
3363
+ tickSpacing: existing.tickSpacing ?? 60,
3364
+ hooks: existing.hooks,
3365
+ poolReference: existing.poolReference
3366
+ });
3367
+ const t0 = existing.token0Address;
3368
+ const t1 = existing.token1Address;
3369
+ return {
3370
+ poolReference: resolved.poolReference,
3371
+ pairLabel: `${t0.slice(0, 6)}\u2026/${t1.slice(0, 6)}\u2026`
3372
+ };
3373
+ }
3374
+ throw new Error(
3375
+ "Pool is required: pass poolPreset (from ctm_uniswap_v4_list_lp_pools), poolReference, or existingPool."
3376
+ );
3377
+ }
3378
+ async function fetchFromSubgraphTier(args) {
3379
+ const warnings = [];
3380
+ if (args.tier.kind === "subgraph_native") {
3381
+ if (args.tier.entity === "hourly") {
3382
+ const { rows: rows2, truncated: truncated2 } = await fetchUniswapV4PoolHourDatas({
3383
+ chainId: args.chainId,
3384
+ poolReference: args.poolReference,
3385
+ fromSec: args.fromSec,
3386
+ toSec: args.toSec
3387
+ });
3388
+ if (truncated2) warnings.push("Hourly subgraph page limit reached; history may be truncated.");
3389
+ return { candles: rows2.map(mapSubgraphHourRow), warnings };
3390
+ }
3391
+ const { rows, truncated } = await fetchUniswapV4PoolDayDatas({
3392
+ chainId: args.chainId,
3393
+ poolReference: args.poolReference,
3394
+ fromSec: args.fromSec,
3395
+ toSec: args.toSec
3396
+ });
3397
+ if (truncated) warnings.push("Daily subgraph page limit reached; history may be truncated.");
3398
+ return { candles: rows.map(mapSubgraphDayRow), warnings };
3399
+ }
3400
+ if (args.tier.kind === "subgraph_aggregate") {
3401
+ if (args.tier.baseEntity === "hourly") {
3402
+ const { rows: rows2, truncated: truncated2 } = await fetchUniswapV4PoolHourDatas({
3403
+ chainId: args.chainId,
3404
+ poolReference: args.poolReference,
3405
+ fromSec: args.fromSec,
3406
+ toSec: args.toSec
3407
+ });
3408
+ if (truncated2) warnings.push("Hourly subgraph page limit reached before aggregation.");
3409
+ const base2 = rows2.map(mapSubgraphHourRow);
3410
+ return { candles: aggregateOhlcvCandles(base2, args.tier.bucketSec), warnings };
3411
+ }
3412
+ const { rows, truncated } = await fetchUniswapV4PoolDayDatas({
3413
+ chainId: args.chainId,
3414
+ poolReference: args.poolReference,
3415
+ fromSec: args.fromSec,
3416
+ toSec: args.toSec
3417
+ });
3418
+ if (truncated) warnings.push("Daily subgraph page limit reached before aggregation.");
3419
+ const base = rows.map(mapSubgraphDayRow);
3420
+ return { candles: aggregateOhlcvCandles(base, args.tier.bucketSec), warnings };
3421
+ }
3422
+ return { candles: [], warnings };
3423
+ }
3424
+ async function fetchFromSwapsTier(args) {
3425
+ const warnings = [];
3426
+ const { rows, truncated } = await fetchUniswapV4Swaps({
3427
+ chainId: args.chainId,
3428
+ poolReference: args.poolReference,
3429
+ fromSec: args.fromSec,
3430
+ toSec: args.toSec
3431
+ });
3432
+ if (truncated) {
3433
+ warnings.push(
3434
+ "Swap pagination limit reached; sub-hour candles may be incomplete. Try a shorter lookback or a coarser interval (1h+)."
3435
+ );
3436
+ }
3437
+ if (rows.length === 0) {
3438
+ warnings.push("No swaps found in the requested window for this pool.");
3439
+ }
3440
+ return {
3441
+ candles: bucketUniswapV4SwapsIntoCandles({ swaps: rows, bucketSec: args.bucketSec }),
3442
+ warnings
3443
+ };
3444
+ }
3445
+ async function uniswapV4FetchOhlcv(args) {
3446
+ const chainId = parseUniswapChainId(args.chainId);
3447
+ const interval = assertUniswapV4OhlcvInterval(args.interval ?? "1h");
3448
+ const { startTimeMs, endTimeMs } = windowFromArgs(args);
3449
+ const fromSec = Math.floor(startTimeMs / 1e3);
3450
+ const toSec = Math.ceil(endTimeMs / 1e3);
3451
+ if (isRobinhoodOhlcvChain(chainId)) {
3452
+ const filter = resolveRobinhoodChartCurrency({
3453
+ currencyAddress: args.currencyAddress,
3454
+ currencySymbol: args.currencySymbol,
3455
+ tokenIn: args.existingPool?.token0Address,
3456
+ tokenOut: args.existingPool?.token1Address
3457
+ });
3458
+ const { candles: candles2, warnings: warnings2 } = await fetchRobinhoodBitqueryOhlcv({
3459
+ filter,
3460
+ interval,
3461
+ startTimeMs,
3462
+ endTimeMs
3463
+ });
3464
+ const label = currencyFilterLabel(filter);
3465
+ const window2 = candleWindowFromRows(candles2);
3466
+ return {
3467
+ symbol: label,
3468
+ timeframe: interval,
3469
+ chainId,
3470
+ poolReference: filter.kind === "address" ? filter.address.toLowerCase() : `symbol:${filter.symbol}`,
3471
+ dataSource: "bitquery",
3472
+ priceQuote: "token0PerToken1",
3473
+ ...window2,
3474
+ candleCount: candles2.length,
3475
+ candles: candles2,
3476
+ fetchedAtMs: Date.now(),
3477
+ ...warnings2.length > 0 ? { warnings: warnings2 } : {}
3478
+ };
3479
+ }
3480
+ const tier = resolveUniswapV4OhlcvDataTier(interval);
3481
+ const pool = await resolveUniswapV4OhlcvPool(args);
3482
+ const expectedBars = Math.ceil((toSec - fromSec) / tier.bucketSec);
3483
+ if (expectedBars > MAX_CANDLES) {
3484
+ throw new Error(
3485
+ `Requested window would produce ~${expectedBars} bars (max ${MAX_CANDLES}). Use a coarser interval or shorter lookback.`
3486
+ );
3487
+ }
3488
+ let candles;
3489
+ let warnings = [];
3490
+ let dataSource;
3491
+ let baseSubgraphEntity;
3492
+ if (tier.kind === "swaps") {
3493
+ const result = await fetchFromSwapsTier({
3494
+ chainId,
3495
+ poolReference: pool.poolReference,
3496
+ bucketSec: tier.bucketSec,
3497
+ fromSec,
3498
+ toSec
3499
+ });
3500
+ candles = result.candles;
3501
+ warnings = result.warnings;
3502
+ dataSource = "swaps";
3503
+ } else {
3504
+ const result = await fetchFromSubgraphTier({
3505
+ chainId,
3506
+ poolReference: pool.poolReference,
3507
+ tier,
3508
+ fromSec,
3509
+ toSec
3510
+ });
3511
+ candles = result.candles;
3512
+ warnings = result.warnings;
3513
+ dataSource = tier.kind;
3514
+ if (tier.kind === "subgraph_aggregate") {
3515
+ baseSubgraphEntity = tier.baseEntity;
3516
+ }
3517
+ }
3518
+ const quoteToken = args.quoteToken ?? "token0";
3519
+ if (quoteToken === "token1") {
3520
+ candles = candles.map(invertCandleRow);
3521
+ }
3522
+ if (candles.length === 0 && warnings.length === 0) {
3523
+ warnings.push("No candles returned for this pool and window.");
3524
+ }
3525
+ const window = candleWindowFromRows(candles);
3526
+ return {
3527
+ symbol: pool.pairLabel,
3528
+ timeframe: interval,
3529
+ chainId,
3530
+ poolReference: pool.poolReference,
3531
+ dataSource,
3532
+ ...baseSubgraphEntity ? { baseSubgraphEntity } : {},
3533
+ priceQuote: quoteToken === "token1" ? "token1PerToken0" : "token0PerToken1",
3534
+ ...window,
3535
+ candleCount: candles.length,
3536
+ candles,
3537
+ fetchedAtMs: Date.now(),
3538
+ ...warnings.length > 0 ? { warnings } : {}
3539
+ };
3540
+ }
3541
+
3542
+ // src/protocols/evm/uniswap-v4/limitOrder.ts
3543
+ function trimAddr3(a) {
3544
+ return a.trim();
3545
+ }
3546
+ var UNISWAP_LIMIT_ORDER_CHAIN_ID = 1;
3547
+ var DEFAULT_UNISWAPX_ORDER_BASE = "https://api.uniswap.org/v2";
3548
+ var DEFAULT_LIMIT_ORDER_QUOTE_PATH = "/limit_order_quote";
3549
+ var DEFAULT_ORDER_SUBMIT_PATH = "/order";
3550
+ var UNISWAP_QUOTE_HEADERS_BASE2 = {
3551
+ "Content-Type": "application/json",
3552
+ "User-Agent": "ctm-mpc-defi uniswapLimitOrderQuote/1.0 (TS)"
3553
+ };
3554
+ function isUniswapLimitOrderChainSupported(chainId) {
3555
+ return parseUniswapChainId(chainId) === UNISWAP_LIMIT_ORDER_CHAIN_ID;
3556
+ }
3557
+ function assertUniswapLimitOrderChain(chainId) {
3558
+ if (!isUniswapLimitOrderChainSupported(chainId)) {
3559
+ throw new Error(
3560
+ `UniswapX limit orders are only supported on Ethereum mainnet (chainId ${UNISWAP_LIMIT_ORDER_CHAIN_ID}). Got ${chainId}. Use market swap on this chain or wait for UniswapX limit support.`
3561
+ );
3562
+ }
3563
+ }
3564
+ function inferPrimaryTypeFromPermitTypes(types) {
3565
+ for (const key of Object.keys(types)) {
3566
+ if (key !== "EIP712Domain") {
3567
+ return key;
3568
+ }
3569
+ }
3570
+ throw new Error("permitData.types missing primaryType (no non-EIP712Domain entry)");
3571
+ }
3572
+ function permitDataToEip712Payload(permitData) {
3573
+ const domain = permitData.domain;
3574
+ const types = permitData.types;
3575
+ const message = permitData.values ?? permitData.message;
3576
+ if (domain == null || typeof domain !== "object" || Array.isArray(domain)) {
3577
+ throw new Error("limit order quote: permitData.domain missing");
3578
+ }
3579
+ if (types == null || typeof types !== "object" || Array.isArray(types)) {
3580
+ throw new Error("limit order quote: permitData.types missing");
3581
+ }
3582
+ if (message == null || typeof message !== "object" || Array.isArray(message)) {
3583
+ throw new Error("limit order quote: permitData.values (message) missing");
3584
+ }
3585
+ const primaryType = typeof permitData.primaryType === "string" && permitData.primaryType.trim() ? permitData.primaryType.trim() : inferPrimaryTypeFromPermitTypes(
3586
+ types
3587
+ );
3588
+ return {
3589
+ domain,
3590
+ types,
3591
+ primaryType,
3592
+ message
3593
+ };
3594
+ }
3595
+ function extractPermitDataFromLimitQuote(quoteResponse) {
3596
+ const permitData = quoteResponse.permitData;
3597
+ if (permitData == null) return null;
3598
+ if (typeof permitData !== "object" || Array.isArray(permitData)) {
3599
+ throw new Error("limit order quote: permitData is not an object");
3600
+ }
3601
+ return permitData;
3602
+ }
3603
+ function buildLimitOrderQuoteRequestBody(args) {
3604
+ const base = buildUniswapQuoteRequestBody({
3605
+ type: args.type,
3606
+ amount: args.amount,
3607
+ tokenIn: args.tokenIn,
3608
+ tokenOut: args.tokenOut,
3609
+ chainId: args.chainId,
3610
+ tokenInChainId: args.tokenInChainId,
3611
+ tokenOutChainId: args.tokenOutChainId,
3612
+ swapper: args.swapper
3613
+ });
3614
+ assertUniswapLimitOrderChain(base.tokenInChainId);
3615
+ return {
3616
+ ...base,
3617
+ limitPrice: String(args.limitPrice).trim(),
3618
+ orderDeadline: args.orderDeadline
3619
+ };
3620
+ }
3621
+ async function uniswapLimitOrderQuote(args) {
3622
+ const apiKey = (args.uniswapApiKey || "").trim();
3623
+ if (!apiKey) {
3624
+ throw new Error("uniswapApiKey (x-api-key) is required");
3625
+ }
3626
+ let swapper;
3627
+ if ((args.swapper || "").trim()) {
3628
+ swapper = trimAddr3(args.swapper);
3629
+ } else {
3630
+ const keyGen = (args.keyGen || "").trim();
3631
+ if (!keyGen) {
3632
+ throw new Error("keyGen is required when swapper is not provided");
3633
+ }
3634
+ const mpc = (args.managementNodeUrl || "").trim();
3635
+ if (!mpc) {
3636
+ throw new Error("managementNodeUrl is required when swapper is not provided (to resolve keyGen)");
3637
+ }
3638
+ swapper = await fetchEthereumAddressForKeyGen(
3639
+ mpc,
3640
+ keyGen,
3641
+ args.nodeReadAuth ?? { bearerOnGet: true, jwt: null }
3642
+ );
3643
+ }
3644
+ const chainIdForGuard = parseUniswapChainId(resolveInputChainId(args));
3645
+ assertUniswapLimitOrderChain(chainIdForGuard);
3646
+ const base = args.baseUrl && args.baseUrl.trim() ? args.baseUrl.trim().replace(/\/$/, "") : "https://trade-api.gateway.uniswap.org/v1";
3647
+ const quoteUrl = `${base}${DEFAULT_LIMIT_ORDER_QUOTE_PATH}`;
3648
+ const body = buildLimitOrderQuoteRequestBody({ ...args, swapper });
3649
+ const headers = {
3650
+ ...UNISWAP_QUOTE_HEADERS_BASE2,
3651
+ "x-api-key": apiKey
3652
+ };
3653
+ const fetchFn = args.fetchImpl ?? globalThis.fetch;
3654
+ const res = await fetchFn(quoteUrl, {
3655
+ method: "POST",
3656
+ headers,
3657
+ body: JSON.stringify(body),
3658
+ signal: args.signal
3659
+ });
3660
+ const text = await res.text();
3661
+ if (!res.ok) {
3662
+ throw new Error(messageFromUniswapHttpResponseBody(text, res.status, res.statusText));
3663
+ }
3664
+ const forParse = text.replace(/^\uFEFF/, "");
3665
+ try {
3666
+ return JSON.parse(forParse);
3667
+ } catch {
3668
+ throw new Error(
3669
+ `Limit order quote: ${messageFromUniswapHttpResponseBody(text, res.status, res.statusText)}`
3670
+ );
3671
+ }
3672
+ }
3673
+ function buildUniswapXLimitOrderSubmitBody(args) {
3674
+ const quote = args.quoteResponse.quote;
3675
+ if (quote == null || typeof quote !== "object" || Array.isArray(quote)) {
3676
+ throw new Error("limit order submit: quoteResponse.quote missing");
3677
+ }
3678
+ const routing = typeof args.quoteResponse.routing === "string" && args.quoteResponse.routing.trim() ? args.quoteResponse.routing.trim() : "LIMIT_ORDER";
3679
+ const chainId = args.quoteResponse.chainId ?? quote.chainId ?? UNISWAP_LIMIT_ORDER_CHAIN_ID;
3680
+ const quoteId = typeof args.quoteResponse.quoteId === "string" ? args.quoteResponse.quoteId : void 0;
3681
+ const requestId = typeof args.quoteResponse.requestId === "string" ? args.quoteResponse.requestId : void 0;
3682
+ return {
3683
+ signature: args.signatureHex.startsWith("0x") ? args.signatureHex : `0x${args.signatureHex}`,
3684
+ quote,
3685
+ chainId: parseUniswapChainId(chainId),
3686
+ orderType: "Limit",
3687
+ routing,
3688
+ ...quoteId ? { quoteId } : {},
3689
+ ...requestId ? { requestId } : {}
3690
+ };
3691
+ }
3692
+ async function submitUniswapXLimitOrder(args) {
3693
+ const apiKey = (args.uniswapApiKey || "").trim();
3694
+ if (!apiKey) {
3695
+ throw new Error("uniswapApiKey (x-api-key) is required");
3696
+ }
3697
+ const base = args.baseUrl && args.baseUrl.trim() ? args.baseUrl.trim().replace(/\/$/, "") : "https://trade-api.gateway.uniswap.org/v1";
3698
+ const orderUrl = `${base}${DEFAULT_ORDER_SUBMIT_PATH}`;
3699
+ const fetchFn = args.fetchImpl ?? globalThis.fetch;
3700
+ const res = await fetchFn(orderUrl, {
3701
+ method: "POST",
3702
+ headers: {
3703
+ "Content-Type": "application/json",
3704
+ "x-api-key": apiKey,
3705
+ "User-Agent": "ctm-mpc-defi uniswapLimitOrderSubmit/1.0 (TS)"
3706
+ },
3707
+ body: JSON.stringify(args.submitBody)
3708
+ });
3709
+ const text = await res.text();
3710
+ if (!res.ok) {
3711
+ throw new Error(messageFromUniswapHttpResponseBody(text, res.status, res.statusText));
3712
+ }
3713
+ const forParse = text.replace(/^\uFEFF/, "");
3714
+ try {
3715
+ return JSON.parse(forParse);
3716
+ } catch {
3717
+ throw new Error(
3718
+ `Limit order submit: ${messageFromUniswapHttpResponseBody(text, res.status, res.statusText)}`
3719
+ );
3720
+ }
3721
+ }
3722
+ async function fetchUniswapXLimitOrders(args) {
3723
+ const chainId = args.chainId ?? UNISWAP_LIMIT_ORDER_CHAIN_ID;
3724
+ assertUniswapLimitOrderChain(chainId);
3725
+ const base = args.baseUrl && args.baseUrl.trim() ? args.baseUrl.trim().replace(/\/$/, "") : DEFAULT_UNISWAPX_ORDER_BASE;
3726
+ const params = new URLSearchParams({
3727
+ chainId: String(chainId),
3728
+ swapper: trimAddr3(args.swapper)
3729
+ });
3730
+ if (args.limit != null && args.limit > 0) {
3731
+ params.set("limit", String(Math.floor(args.limit)));
3732
+ }
3733
+ const url = `${base}/limit-orders?${params.toString()}`;
3734
+ const headers = {
3735
+ Accept: "application/json",
3736
+ "User-Agent": "ctm-mpc-defi uniswapLimitOrdersList/1.0 (TS)"
3737
+ };
3738
+ const apiKey = (args.uniswapApiKey || "").trim();
3739
+ if (apiKey) {
3740
+ headers["x-api-key"] = apiKey;
3741
+ }
3742
+ const fetchFn = args.fetchImpl ?? globalThis.fetch;
3743
+ const res = await fetchFn(url, { method: "GET", headers });
3744
+ const text = await res.text();
3745
+ if (!res.ok) {
3746
+ const parsed = (() => {
3747
+ try {
3748
+ return JSON.parse(text);
3749
+ } catch {
3750
+ return null;
3751
+ }
3752
+ })();
3753
+ const msg = errorMessageFromUniswapJsonBody(parsed) ?? (text.trim() || res.statusText);
3754
+ throw new Error(`List limit orders HTTP ${res.status}: ${msg}`);
3755
+ }
3756
+ return JSON.parse(text.replace(/^\uFEFF/, ""));
3757
+ }
3758
+ async function uniswapFetchLimitOrdersMcp(args) {
3759
+ let swapper;
3760
+ if ((args.swapper || "").trim()) {
3761
+ swapper = trimAddr3(args.swapper);
3762
+ } else {
3763
+ const keyGen = (args.keyGen || "").trim();
3764
+ if (!keyGen) {
3765
+ throw new Error("swapper or keyGen is required");
3766
+ }
3767
+ const mpc = (args.managementNodeUrl || "").trim();
3768
+ if (!mpc) {
3769
+ throw new Error("managementNodeUrl is required when swapper is not provided");
3770
+ }
3771
+ swapper = await fetchEthereumAddressForKeyGen(
3772
+ mpc,
3773
+ keyGen,
3774
+ args.nodeReadAuth ?? { bearerOnGet: true, jwt: null }
3775
+ );
3776
+ }
3777
+ return fetchUniswapXLimitOrders({
3778
+ swapper,
3779
+ chainId: args.chainId,
3780
+ uniswapApiKey: args.uniswapApiKey,
3781
+ limit: args.limit,
3782
+ baseUrl: args.baseUrl,
3783
+ fetchImpl: args.fetchImpl
3784
+ });
3785
+ }
3786
+ var EIP712_SIGN_REQUEST_KIND = "eip712";
3787
+ function msgHashNo0x(digest) {
3788
+ return digest.startsWith("0x") ? digest.slice(2) : digest;
3789
+ }
3790
+ function eip712SignatureText(domain, primaryType) {
3791
+ return JSON.stringify({
3792
+ kind: "EIP-712",
3793
+ ...typeof domain.name === "string" ? { name: domain.name } : {},
3794
+ primaryType
3795
+ });
3796
+ }
3797
+ function jsonStringifyForAudit(value) {
3798
+ return JSON.stringify(value, (_, v) => typeof v === "bigint" ? v.toString() : v);
3799
+ }
3800
+ function eip712MsgRawHex(typedData, delivery) {
3801
+ const envelope = {
3802
+ version: 1,
3803
+ ...typedData,
3804
+ delivery
3805
+ };
3806
+ return stringToHex(jsonStringifyForAudit(envelope));
3807
+ }
3808
+ function buildEip712MultisignBody(args) {
3809
+ const { typedData, delivery } = args;
3810
+ const digest = hashTypedData({
3811
+ domain: typedData.domain,
3812
+ types: typedData.types,
3813
+ primaryType: typedData.primaryType,
3814
+ message: typedData.message
3815
+ });
3816
+ const extraJSON = {
3817
+ signRequestKind: EIP712_SIGN_REQUEST_KIND,
3818
+ eip712: {
3819
+ version: 1,
3820
+ digest,
3821
+ domain: typedData.domain,
3822
+ types: typedData.types,
3823
+ primaryType: typedData.primaryType,
3824
+ message: typedData.message
3825
+ },
3826
+ delivery
3827
+ };
3828
+ return finalizeMultisign({
3829
+ keyGen: args.keyGen,
3830
+ purposeText: args.purposeText,
3831
+ purposeSuffix: args.purposeSuffix,
3832
+ destinationChainID: args.destinationChainID,
3833
+ destinationAddress: args.destinationAddress,
3834
+ extraJSON,
3835
+ expiryDate: args.expiryDate,
3836
+ legs: [
3837
+ {
3838
+ msgHash: msgHashNo0x(digest),
3839
+ msgRaw: eip712MsgRawHex(typedData, delivery),
3840
+ destinationAddress: args.destinationAddress,
3841
+ signatureText: eip712SignatureText(typedData.domain, typedData.primaryType),
3842
+ audit: {
3843
+ kind: "EIP712",
3844
+ primaryType: typedData.primaryType,
3845
+ deliveryKind: delivery.kind,
3846
+ ...args.audit ?? {}
3847
+ },
3848
+ feeSnapshot: {}
3849
+ }
3850
+ ]
3851
+ });
3852
+ }
3853
+
3854
+ // src/protocols/evm/uniswap-v4/limitOrderMultisign.ts
3855
+ async function buildEvmMultisignBodyUniswapV4LimitOrderBatch(args) {
3856
+ assertUniswapLimitOrderChain(args.chainId);
3857
+ const permitRaw = extractPermitDataFromLimitQuote(args.fullLimitQuote);
3858
+ if (!permitRaw) {
3859
+ throw new Error(
3860
+ "Limit order quote has no permitData \u2014 cannot build EIP-712 sign request. Re-quote or confirm mainnet limit order support for this pair."
3861
+ );
3862
+ }
3863
+ const typedData = permitDataToEip712Payload(permitRaw);
3864
+ const submitTemplate = buildUniswapXLimitOrderSubmitBody({
3865
+ quoteResponse: args.fullLimitQuote,
3866
+ signatureHex: "0x"
3867
+ });
3868
+ return buildEip712MultisignBody({
3869
+ keyGen: args.keyGen,
3870
+ purposeText: args.purposeText,
3871
+ destinationChainID: String(UNISWAP_LIMIT_ORDER_CHAIN_ID),
3872
+ destinationAddress: PERMIT2_ADDRESS,
3873
+ typedData,
3874
+ delivery: {
3875
+ kind: "uniswapx_limit_order",
3876
+ chainId: UNISWAP_LIMIT_ORDER_CHAIN_ID,
3877
+ swapper: getAddress(args.swapper),
3878
+ submitBodyTemplate: submitTemplate,
3879
+ quoteResponse: args.fullLimitQuote,
3880
+ uniswapApiKey: args.uniswapApiKey,
3881
+ tradeApiBaseUrl: args.tradeApiBaseUrl ?? "https://trade-api.gateway.uniswap.org/v1"
3882
+ },
3883
+ audit: {
3884
+ protocol: "uniswap-v4",
3885
+ action: "limitOrder",
3886
+ routing: args.fullLimitQuote.routing ?? "LIMIT_ORDER",
3887
+ orderType: "Limit",
3888
+ primaryType: typedData.primaryType
3889
+ },
3890
+ expiryDate: args.expiryDate
3891
+ });
3892
+ }
3893
+ async function buildEvmMultisignBodyUniswapV4LimitOrderBatchFromMcp(args) {
3894
+ return buildEvmMultisignBodyUniswapV4LimitOrderBatch({
3895
+ keyGen: args.keyGen,
3896
+ chainId: args.chainId,
3897
+ purposeText: args.purposeText,
3898
+ fullLimitQuote: args.fullLimitQuote,
3899
+ swapper: getAddress(args.executorAddress),
3900
+ uniswapApiKey: args.uniswapApiKey,
3901
+ tradeApiBaseUrl: args.tradeApiBaseUrl,
3902
+ expiryDate: args.expiryDate
3903
+ });
3904
+ }
3905
+
2579
3906
  // src/protocols/evm/uniswap-v4/index.ts
2580
3907
  var UNISWAP_V4_PROTOCOL_ID = "uniswap-v4";
2581
3908
  var uniswapV4ProtocolModule = {
@@ -2622,6 +3949,17 @@ var uniswapV4ProtocolModule = {
2622
3949
  type: { type: "EXACT_INPUT | EXACT_OUTPUT", required: true, description: "Trade type" }
2623
3950
  }
2624
3951
  },
3952
+ {
3953
+ id: "uniswap-v4.limit-order",
3954
+ protocolId: UNISWAP_V4_PROTOCOL_ID,
3955
+ chainCategory: "evm",
3956
+ description: "Place UniswapX limit order via EIP-712 sign + POST /order (Ethereum mainnet only)",
3957
+ commonParams: ["keyGen", "purposeText"],
3958
+ params: {
3959
+ fullLimitQuote: { type: "object", required: true, description: "Full limit order quote from Trade API" },
3960
+ uniswapApiKey: { type: "string", required: true, description: "Uniswap Trade API key" }
3961
+ }
3962
+ },
2625
3963
  {
2626
3964
  id: "uniswap-v4.mint-liquidity",
2627
3965
  protocolId: UNISWAP_V4_PROTOCOL_ID,
@@ -2677,6 +4015,10 @@ var uniswapV4 = {
2677
4015
  swapExactInput,
2678
4016
  swapFromQuote,
2679
4017
  buildSwapMultisignBody: buildEvmMultisignBodyUniswapV4SkipPermit2Batch,
4018
+ limitOrderQuote: uniswapLimitOrderQuote,
4019
+ buildLimitOrderMultisignBody: buildEvmMultisignBodyUniswapV4LimitOrderBatchFromMcp,
4020
+ fetchLimitOrders: fetchUniswapXLimitOrders,
4021
+ fetchLimitOrdersMcp: uniswapFetchLimitOrdersMcp,
2680
4022
  quote: uniswapTradeQuote,
2681
4023
  createLiquidityPosition: uniswapLpCreatePosition,
2682
4024
  increaseLiquidityPosition: uniswapLpIncreasePosition,
@@ -2690,6 +4032,6 @@ var uniswapV4 = {
2690
4032
  isChainSupported: isUniswapV4ChainSupported
2691
4033
  };
2692
4034
 
2693
- export { MAX_UINT160, MAX_UINT48, PERMIT2_ADDRESS, UNISWAP_LP_API_BASE_DEFAULT, UNISWAP_LP_DEFAULT_DEADLINE_SEC_OFFSET, UNISWAP_LP_DEFAULT_EXPIRY_MINUTES, UNISWAP_LP_DEFAULT_SLIPPAGE_PERCENT, UNISWAP_SWAP_DEFAULT_DEADLINE_SEC_OFFSET, UNISWAP_SWAP_DEFAULT_EXPIRY_MINUTES, UNISWAP_TRADE_BASE_DEFAULT, UNISWAP_UNIVERSAL_ROUTER_DEFAULT_GAS_UNITS, UNISWAP_UNIVERSAL_ROUTER_VERSION_DEFAULT, UNISWAP_V4_LP_COLLECT_DEFAULT_GAS_UNITS, UNISWAP_V4_LP_DECREASE_DEFAULT_GAS_UNITS, UNISWAP_V4_LP_ERC20_APPROVE_FALLBACK, UNISWAP_V4_LP_INCREASE_DEFAULT_GAS_UNITS, UNISWAP_V4_LP_LOG_CHUNK_SIZE_DEFAULT, UNISWAP_V4_LP_LOG_CHUNK_SIZE_MIN, UNISWAP_V4_LP_MINT_DEFAULT_GAS_UNITS, UNISWAP_V4_LP_POOL_LOOKUP_HINT, UNISWAP_V4_LP_POSITION_REGISTRY_HINT, UNISWAP_V4_LP_WETH_DEPOSIT_FALLBACK, UNISWAP_V4_PROTOCOL_ID, applySlippagePercentToApproveWei, buildEvmMultisignBodyUniswapV4CollectFeesBatch, buildEvmMultisignBodyUniswapV4DecreaseLiquidityBatch, buildEvmMultisignBodyUniswapV4IncreaseLiquidityBatch, buildEvmMultisignBodyUniswapV4MintLiquidityBatch, buildEvmMultisignBodyUniswapV4SkipPermit2Batch, buildUniswapQuoteRequestBody, buildUniswapV4PurposePrefill, computeUniswapV4PoolReference, computeUniswapV4Session, createSwap, errorMessageFromUniswapJsonBody, extractUniswapLpTokenAmounts, extractUniswapLpTransaction, fetchEthereumAddressForKeyGen, formatUniswapAmountFieldFromWei, formatUniswapLpAmountHuman, formatUniswapPercentForUi, formatUniswapQuoteForDisplay, formatUniswapV4PositionNotFoundError, getClassicQuoteFromStoredUniswapResponse, getUniswapUniversalRouterSpenderOrThrow, getUniswapV4PositionManagerDeployBlock, getUniswapV4PositionManagerOrThrow, isEthGetLogsBlockRangeTooLargeError, isNativeUniswapLpTokenAddress, isUniswapFullQuoteResponseNativeIn, isUniswapTokenInAddressNative, isUniswapV4ChainSupported, isUniswapV4LiquidityEvmSignRequest, isUniswapV4SwapEvmSignRequest, listUniswapV4PositionsForWallet, listUniswapV4StandardLpPools, messageFromUniswapHttpResponseBody, parseUniswapChainId, parseUniswapLpApiSnapshot, parseUniswapQuoteClassicInOut, parseUniswapQuoteSlippageInfo, parseUniswapV4ScanFromDateYmd, quoteSwap, readUniswapV4PositionInfoRaw, resolveBlockAtOrAfterUnixTime, resolveRouterSwapGasUnitsFromSignRequest, resolveUniswapV4LpExistingPoolFromKey, resolveUniswapV4LpPoolPreset, resolveUniswapV4PositionScanFromDate, sortUniswapV4PoolCurrencies, swapExactInput, swapFromQuote, swapTransactionDeadlineUnixFromExpiryMinutes, tryGetUniswapV4PositionManager, uniswapCreateSwap, uniswapLpClaimFees, uniswapLpCreatePosition, uniswapLpDecreasePosition, uniswapLpIncreasePosition, uniswapLpTxFieldForAction, uniswapQuoteToJsonQuoteOneLine, uniswapTradeQuote, uniswapV4, uniswapV4ListPositionsForMcp, uniswapV4ListPositionsFromRegistryForMcp, uniswapV4ListPositionsRegistryMcpPlaceholder, uniswapV4ListStandardLpPools, uniswapV4PositionMintedTokenIdsFromReceipt, uniswapV4ProtocolModule, uniswapV4RegisterPositionFromMintTxPlaceholder, uniswapV4RegisterPositionNftPlaceholder };
4035
+ export { BITQUERY_API_KEY_DOCS_URL, BITQUERY_API_KEY_ENV, BITQUERY_API_KEY_SIGNUP_URL, BITQUERY_GRAPHQL_URL, DEFAULT_LIMIT_ORDER_QUOTE_PATH, DEFAULT_ORDER_SUBMIT_PATH, DEFAULT_UNISWAPX_ORDER_BASE, MAX_UINT160, MAX_UINT48, PERMIT2_ADDRESS, ROBINHOOD_CHAIN_ID, ROBINHOOD_WETH_ADDRESS, THE_GRAPH_API_KEY_DOCS_URL, THE_GRAPH_API_KEY_ENV, THE_GRAPH_API_KEY_SIGNUP_URL, UNISWAP_LIMIT_ORDER_CHAIN_ID, UNISWAP_LP_API_BASE_DEFAULT, UNISWAP_LP_DEFAULT_DEADLINE_SEC_OFFSET, UNISWAP_LP_DEFAULT_EXPIRY_MINUTES, UNISWAP_LP_DEFAULT_SLIPPAGE_PERCENT, UNISWAP_SWAP_DEFAULT_DEADLINE_SEC_OFFSET, UNISWAP_SWAP_DEFAULT_EXPIRY_MINUTES, UNISWAP_TRADE_BASE_DEFAULT, UNISWAP_UNIVERSAL_ROUTER_DEFAULT_GAS_UNITS, UNISWAP_UNIVERSAL_ROUTER_VERSION_DEFAULT, UNISWAP_V4_LP_COLLECT_DEFAULT_GAS_UNITS, UNISWAP_V4_LP_DECREASE_DEFAULT_GAS_UNITS, UNISWAP_V4_LP_ERC20_APPROVE_FALLBACK, UNISWAP_V4_LP_INCREASE_DEFAULT_GAS_UNITS, UNISWAP_V4_LP_LOG_CHUNK_SIZE_DEFAULT, UNISWAP_V4_LP_LOG_CHUNK_SIZE_MIN, UNISWAP_V4_LP_MINT_DEFAULT_GAS_UNITS, UNISWAP_V4_LP_POOL_LOOKUP_HINT, UNISWAP_V4_LP_POSITION_REGISTRY_HINT, UNISWAP_V4_LP_WETH_DEPOSIT_FALLBACK, UNISWAP_V4_OHLCV_INTERVALS, UNISWAP_V4_PROTOCOL_ID, UNISWAP_V4_SUBGRAPH_ID_BY_CHAIN_ID, aggregateOhlcvCandles, applySlippagePercentToApproveWei, assertUniswapLimitOrderChain, assertUniswapV4OhlcvInterval, bitqueryApiKeyVariablesHint, bitqueryAuthErrorMessage, buildEvmMultisignBodyUniswapV4CollectFeesBatch, buildEvmMultisignBodyUniswapV4DecreaseLiquidityBatch, buildEvmMultisignBodyUniswapV4IncreaseLiquidityBatch, buildEvmMultisignBodyUniswapV4LimitOrderBatch, buildEvmMultisignBodyUniswapV4LimitOrderBatchFromMcp, buildEvmMultisignBodyUniswapV4MintLiquidityBatch, buildEvmMultisignBodyUniswapV4SkipPermit2Batch, buildLimitOrderQuoteRequestBody, buildUniswapQuoteRequestBody, buildUniswapV4PurposePrefill, buildUniswapXLimitOrderSubmitBody, computeUniswapV4PoolReference, computeUniswapV4Session, createSwap, currencyFilterLabel, errorMessageFromUniswapJsonBody, extractPermitDataFromLimitQuote, extractUniswapLpTokenAmounts, extractUniswapLpTransaction, fetchEthereumAddressForKeyGen, fetchRobinhoodBitqueryOhlcv, fetchUniswapV4PoolDayDatas, fetchUniswapV4PoolHourDatas, fetchUniswapV4PoolMeta, fetchUniswapV4Swaps, fetchUniswapXLimitOrders, formatUniswapAmountFieldFromWei, formatUniswapLpAmountHuman, formatUniswapPercentForUi, formatUniswapQuoteForDisplay, formatUniswapV4PositionNotFoundError, getClassicQuoteFromStoredUniswapResponse, getUniswapUniversalRouterSpenderOrThrow, getUniswapUniversalRouterVersion, getUniswapV4PositionManagerDeployBlock, getUniswapV4PositionManagerOrThrow, isBitqueryAuthOrRateLimitError, isEthGetLogsBlockRangeTooLargeError, isNativeUniswapLpTokenAddress, isRobinhoodOhlcvChain, isTheGraphRateLimitOrAuthError, isUniswapFullQuoteResponseNativeIn, isUniswapFullQuoteResponseNativeOut, isUniswapLimitOrderChainSupported, isUniswapNativeTokenAddress, isUniswapTokenInAddressNative, isUniswapV4ChainSupported, isUniswapV4LiquidityEvmSignRequest, isUniswapV4SwapEvmSignRequest, listUniswapV4PositionsForWallet, listUniswapV4StandardLpPools, mapBitqueryOhlcvRowsToCandles, messageFromUniswapHttpResponseBody, parseUniswapChainId, parseUniswapLpApiSnapshot, parseUniswapQuoteClassicInOut, parseUniswapQuoteSlippageInfo, parseUniswapV4ScanFromDateYmd, permitDataToEip712Payload, quoteSwap, readUniswapV4PositionInfoRaw, requireBitqueryApiKey, resolveBitqueryApiKey, resolveBlockAtOrAfterUnixTime, resolveChainIdFromStoredUniswapQuote, resolveInputChainId, resolveRobinhoodChartCurrency, resolveRouterSwapGasUnitsFromSignRequest, resolveTheGraphApiKey, resolveUniswapV4LpExistingPoolFromKey, resolveUniswapV4LpPoolPreset, resolveUniswapV4OhlcvDataTier, resolveUniswapV4OhlcvPool, resolveUniswapV4PositionScanFromDate, sortUniswapV4PoolCurrencies, submitUniswapXLimitOrder, swapExactInput, swapFromQuote, swapTransactionDeadlineUnixFromExpiryMinutes, theGraphApiKeyVariablesHint, theGraphRateLimitErrorMessage, tryGetUniswapV4PositionManager, uniswapCreateSwap, uniswapFetchLimitOrdersMcp, uniswapLimitOrderQuote, uniswapLpClaimFees, uniswapLpCreatePosition, uniswapLpDecreasePosition, uniswapLpIncreasePosition, uniswapLpTxFieldForAction, uniswapQuoteToJsonQuoteOneLine, uniswapTradeQuote, uniswapV4, uniswapV4FetchOhlcv, uniswapV4ListPositionsForMcp, uniswapV4ListPositionsFromRegistryForMcp, uniswapV4ListPositionsRegistryMcpPlaceholder, uniswapV4ListStandardLpPools, uniswapV4OhlcvIntervalToBucketSec, uniswapV4OhlcvSupportedChainIds, uniswapV4PositionMintedTokenIdsFromReceipt, uniswapV4ProtocolModule, uniswapV4RegisterPositionFromMintTxPlaceholder, uniswapV4RegisterPositionNftPlaceholder, uniswapV4SubgraphIdForChain, uniswapV4SubgraphSupportedChainIds, uniswapV4SubgraphUrlForChain };
2694
4036
  //# sourceMappingURL=index.js.map
2695
4037
  //# sourceMappingURL=index.js.map