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