@continuumdao/ctm-mpc-defi 0.2.26 → 0.2.28
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 +681 -46
- package/dist/agent/catalog.cjs.map +1 -1
- package/dist/agent/catalog.d.ts +1209 -21
- package/dist/agent/catalog.js +668 -47
- package/dist/agent/catalog.js.map +1 -1
- package/dist/agent/skills/morpho/SKILL.md +38 -6
- package/dist/agent/skills/uniswap-v4/SKILL.md +15 -0
- package/dist/core/index.cjs +19 -0
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.ts +8 -1
- package/dist/core/index.js +17 -1
- package/dist/core/index.js.map +1 -1
- package/dist/index.cjs +363 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +361 -23
- package/dist/index.js.map +1 -1
- package/dist/protocols/evm/aave-v4/index.cjs.map +1 -1
- package/dist/protocols/evm/aave-v4/index.js.map +1 -1
- package/dist/protocols/evm/euler-v2/index.cjs.map +1 -1
- package/dist/protocols/evm/euler-v2/index.js.map +1 -1
- package/dist/protocols/evm/maple/index.cjs.map +1 -1
- package/dist/protocols/evm/maple/index.js.map +1 -1
- package/dist/protocols/evm/morpho/index.cjs +1153 -142
- package/dist/protocols/evm/morpho/index.cjs.map +1 -1
- package/dist/protocols/evm/morpho/index.d.ts +314 -8
- package/dist/protocols/evm/morpho/index.js +1131 -144
- package/dist/protocols/evm/morpho/index.js.map +1 -1
- package/dist/protocols/evm/permit2/index.cjs.map +1 -1
- package/dist/protocols/evm/permit2/index.js.map +1 -1
- package/dist/protocols/evm/sky/index.cjs.map +1 -1
- package/dist/protocols/evm/sky/index.js.map +1 -1
- package/dist/protocols/evm/uniswap-v4/index.cjs +863 -25
- package/dist/protocols/evm/uniswap-v4/index.cjs.map +1 -1
- package/dist/protocols/evm/uniswap-v4/index.d.ts +266 -9
- package/dist/protocols/evm/uniswap-v4/index.js +834 -27
- package/dist/protocols/evm/uniswap-v4/index.js.map +1 -1
- package/package.json +2 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parseAbi, parseAbiParameters, parseAbiItem, getAddress,
|
|
1
|
+
import { parseAbi, parseAbiParameters, parseAbiItem, getAddress, createPublicClient, http, defineChain, zeroAddress, formatUnits, parseUnits, 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
|
|
@@ -70,11 +70,23 @@ var UNIVERSAL_ROUTER_SPENDER = {
|
|
|
70
70
|
/** Robinhood Chain mainnet — Universal Router v2.1.1 (Trade API / interface) */
|
|
71
71
|
4663: "0x8876789976decbfcbbbe364623c63652db8c0904"
|
|
72
72
|
};
|
|
73
|
+
var UNIVERSAL_ROUTER_V2_2_SPENDER = {
|
|
74
|
+
1: "0xCb640A86855f1A828c27241bA364348de28abe66",
|
|
75
|
+
11155111: "0xB0C89059d7190EDb17eFF19829cc009cEe923916"
|
|
76
|
+
};
|
|
73
77
|
var UNIVERSAL_ROUTER_VERSION_BY_CHAIN = {
|
|
74
78
|
/** Robinhood has UR 2.1.1 only in the supported-chains table (no 2.0 Trade API column). */
|
|
75
79
|
4663: "2.1.1"
|
|
76
80
|
};
|
|
77
|
-
|
|
81
|
+
var UNISWAP_UNIVERSAL_ROUTER_VERSION_DEFAULT = "2.0";
|
|
82
|
+
var UNISWAP_UNIVERSAL_ROUTER_VERSION_PERMISSIONED = "2.2.0";
|
|
83
|
+
function getUniswapUniversalRouterVersion(chainId, opts) {
|
|
84
|
+
if (opts?.permissioned) {
|
|
85
|
+
const v22 = UNIVERSAL_ROUTER_V2_2_SPENDER[chainId];
|
|
86
|
+
if (typeof v22 === "string" && v22.startsWith("0x")) {
|
|
87
|
+
return UNISWAP_UNIVERSAL_ROUTER_VERSION_PERMISSIONED;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
78
90
|
const v = UNIVERSAL_ROUTER_VERSION_BY_CHAIN[chainId];
|
|
79
91
|
return typeof v === "string" && v.trim() ? v.trim() : UNISWAP_UNIVERSAL_ROUTER_VERSION_DEFAULT;
|
|
80
92
|
}
|
|
@@ -85,7 +97,11 @@ function isUniswapV4ChainSupported(chainId) {
|
|
|
85
97
|
const a = UNIVERSAL_ROUTER_SPENDER[n];
|
|
86
98
|
return typeof a === "string" && a.startsWith("0x");
|
|
87
99
|
}
|
|
88
|
-
function getUniswapUniversalRouterSpenderOrThrow(chainId) {
|
|
100
|
+
function getUniswapUniversalRouterSpenderOrThrow(chainId, opts) {
|
|
101
|
+
if (opts?.permissioned) {
|
|
102
|
+
const raw22 = UNIVERSAL_ROUTER_V2_2_SPENDER[chainId];
|
|
103
|
+
if (raw22 && raw22.startsWith("0x")) return getAddress(raw22);
|
|
104
|
+
}
|
|
89
105
|
const raw = UNIVERSAL_ROUTER_SPENDER[chainId];
|
|
90
106
|
if (!raw || !raw.startsWith("0x")) {
|
|
91
107
|
throw new Error(
|
|
@@ -94,12 +110,16 @@ function getUniswapUniversalRouterSpenderOrThrow(chainId) {
|
|
|
94
110
|
}
|
|
95
111
|
return getAddress(raw);
|
|
96
112
|
}
|
|
113
|
+
function tryGetUniswapUniversalRouterV22(chainId) {
|
|
114
|
+
const raw = UNIVERSAL_ROUTER_V2_2_SPENDER[chainId];
|
|
115
|
+
if (!raw || !raw.startsWith("0x")) return null;
|
|
116
|
+
return getAddress(raw);
|
|
117
|
+
}
|
|
97
118
|
var MAX_UINT160 = (1n << 160n) - 1n;
|
|
98
119
|
var MAX_UINT48 = (1n << 48n) - 1n;
|
|
99
120
|
var UNISWAP_UNIVERSAL_ROUTER_DEFAULT_GAS_UNITS = 1500000n;
|
|
100
121
|
var UNISWAP_TRADE_BASE_DEFAULT = "https://trade-api.gateway.uniswap.org/v1";
|
|
101
122
|
var UNISWAP_LP_API_BASE_DEFAULT = "https://api.uniswap.org";
|
|
102
|
-
var UNISWAP_UNIVERSAL_ROUTER_VERSION_DEFAULT = "2.0";
|
|
103
123
|
var UNISWAP_SWAP_DEFAULT_EXPIRY_MINUTES = 30;
|
|
104
124
|
var UNISWAP_SWAP_DEFAULT_DEADLINE_SEC_OFFSET = UNISWAP_SWAP_DEFAULT_EXPIRY_MINUTES * 60;
|
|
105
125
|
var UNISWAP_LP_DEFAULT_EXPIRY_MINUTES = 30;
|
|
@@ -118,6 +138,18 @@ var POSITION_MANAGER_BY_CHAIN = {
|
|
|
118
138
|
/** Robinhood Chain mainnet */
|
|
119
139
|
4663: "0x58daec3116aae6D93017bAAea7749052E8a04fA7"
|
|
120
140
|
};
|
|
141
|
+
var PERMISSIONED_POSITION_MANAGER_BY_CHAIN = {
|
|
142
|
+
1: "0x89628C9B4CE81951a9BC1F36F0688Fad6A6ee248",
|
|
143
|
+
11155111: "0x68fC145BB20b388965bED184Df5ef912215bb3C7"
|
|
144
|
+
};
|
|
145
|
+
var PERMISSIONED_HOOKS_BY_CHAIN = {
|
|
146
|
+
1: "0x69603ab16110Eb0bB5f5E9C8019749eE41A128C0",
|
|
147
|
+
11155111: "0x8B0E8d467af81D9F5B49165e104a2fe1b98328C0"
|
|
148
|
+
};
|
|
149
|
+
var PERMISSIONS_ADAPTER_FACTORY_BY_CHAIN = {
|
|
150
|
+
1: "0x38f9Ab57FBE2704EbB727cB20e33201aF0E5F961",
|
|
151
|
+
11155111: "0xEe258C31574fb59660C23534E76AF6497c2e5683"
|
|
152
|
+
};
|
|
121
153
|
var ROBINHOOD_CHAIN_ID = 4663;
|
|
122
154
|
var ROBINHOOD_WETH_ADDRESS = "0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73";
|
|
123
155
|
function getUniswapV4PositionManagerOrThrow(chainId) {
|
|
@@ -136,6 +168,76 @@ function tryGetUniswapV4PositionManager(chainId) {
|
|
|
136
168
|
return null;
|
|
137
169
|
}
|
|
138
170
|
}
|
|
171
|
+
function getUniswapV4PermissionedPositionManagerOrThrow(chainId) {
|
|
172
|
+
const raw = PERMISSIONED_POSITION_MANAGER_BY_CHAIN[chainId];
|
|
173
|
+
if (!raw || !raw.startsWith("0x") || raw.length !== 42) {
|
|
174
|
+
throw new Error(
|
|
175
|
+
`No Uniswap V4 Permissioned PositionManager is configured for chainId ${chainId}.`
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
return getAddress(raw);
|
|
179
|
+
}
|
|
180
|
+
function tryGetUniswapV4PermissionedPositionManager(chainId) {
|
|
181
|
+
try {
|
|
182
|
+
return getUniswapV4PermissionedPositionManagerOrThrow(chainId);
|
|
183
|
+
} catch {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function getUniswapV4PermissionedHooksOrThrow(chainId) {
|
|
188
|
+
const raw = PERMISSIONED_HOOKS_BY_CHAIN[chainId];
|
|
189
|
+
if (!raw || !raw.startsWith("0x") || raw.length !== 42) {
|
|
190
|
+
throw new Error(`No Uniswap V4 PermissionedHooks is configured for chainId ${chainId}.`);
|
|
191
|
+
}
|
|
192
|
+
return getAddress(raw);
|
|
193
|
+
}
|
|
194
|
+
function tryGetUniswapV4PermissionedHooks(chainId) {
|
|
195
|
+
try {
|
|
196
|
+
return getUniswapV4PermissionedHooksOrThrow(chainId);
|
|
197
|
+
} catch {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function getUniswapV4PermissionsAdapterFactoryOrThrow(chainId) {
|
|
202
|
+
const raw = PERMISSIONS_ADAPTER_FACTORY_BY_CHAIN[chainId];
|
|
203
|
+
if (!raw || !raw.startsWith("0x") || raw.length !== 42) {
|
|
204
|
+
throw new Error(
|
|
205
|
+
`No Uniswap V4 PermissionsAdapterFactory is configured for chainId ${chainId}.`
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
return getAddress(raw);
|
|
209
|
+
}
|
|
210
|
+
function tryGetUniswapV4PermissionsAdapterFactory(chainId) {
|
|
211
|
+
try {
|
|
212
|
+
return getUniswapV4PermissionsAdapterFactoryOrThrow(chainId);
|
|
213
|
+
} catch {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function isUniswapV4PermissionedPositionManager(chainId, address) {
|
|
218
|
+
if (!address?.toString().trim()) return false;
|
|
219
|
+
const perm = tryGetUniswapV4PermissionedPositionManager(chainId);
|
|
220
|
+
if (!perm) return false;
|
|
221
|
+
try {
|
|
222
|
+
return getAddress(address) === perm;
|
|
223
|
+
} catch {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function isUniswapV4AnyPositionManager(chainId, address) {
|
|
228
|
+
if (!address?.toString().trim()) return false;
|
|
229
|
+
try {
|
|
230
|
+
const a = getAddress(address);
|
|
231
|
+
const std = tryGetUniswapV4PositionManager(chainId);
|
|
232
|
+
const perm = tryGetUniswapV4PermissionedPositionManager(chainId);
|
|
233
|
+
return std != null && a === std || perm != null && a === perm;
|
|
234
|
+
} catch {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function resolveUniswapV4PositionManager(chainId, kind = "standard") {
|
|
239
|
+
return kind === "permissioned" ? getUniswapV4PermissionedPositionManagerOrThrow(chainId) : getUniswapV4PositionManagerOrThrow(chainId);
|
|
240
|
+
}
|
|
139
241
|
var POSITION_MANAGER_DEPLOY_BLOCK_BY_CHAIN = {
|
|
140
242
|
/** Ethereum mainnet — Uniswap v4 launch (Jan 2025). */
|
|
141
243
|
1: 22938741n
|
|
@@ -151,6 +253,231 @@ var UNISWAP_V4_LP_DECREASE_DEFAULT_GAS_UNITS = 1200000n;
|
|
|
151
253
|
var UNISWAP_V4_LP_COLLECT_DEFAULT_GAS_UNITS = 900000n;
|
|
152
254
|
var UNISWAP_V4_LP_ERC20_APPROVE_FALLBACK = 100000n;
|
|
153
255
|
var UNISWAP_V4_LP_WETH_DEPOSIT_FALLBACK = 120000n;
|
|
256
|
+
var UNISWAP_V4_LP_PERMIT2_APPROVE_FALLBACK = 100000n;
|
|
257
|
+
var UNISWAP_V4_PERMISSION_FLAG_SWAP = 1;
|
|
258
|
+
var UNISWAP_V4_PERMISSION_FLAG_LIQUIDITY = 2;
|
|
259
|
+
var permissionsAdapterAbi = parseAbi([
|
|
260
|
+
"function isAllowed(address account, uint8 permission) view returns (bool)",
|
|
261
|
+
"function PERMISSIONED_TOKEN() view returns (address)"
|
|
262
|
+
]);
|
|
263
|
+
var factoryAbi = parseAbi([
|
|
264
|
+
"function verifiedPermissionsAdapterOf(address permissionsAdapter) view returns (address)"
|
|
265
|
+
]);
|
|
266
|
+
function normalizeTokenList(tokens) {
|
|
267
|
+
const out = [];
|
|
268
|
+
const seen = /* @__PURE__ */ new Set();
|
|
269
|
+
for (const raw of tokens) {
|
|
270
|
+
const t = (raw ?? "").toString().trim();
|
|
271
|
+
if (!t) continue;
|
|
272
|
+
if (isUniswapNativeTokenAddress(t)) continue;
|
|
273
|
+
let addr;
|
|
274
|
+
try {
|
|
275
|
+
addr = getAddress(t);
|
|
276
|
+
} catch {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
if (addr === zeroAddress) continue;
|
|
280
|
+
const key = addr.toLowerCase();
|
|
281
|
+
if (seen.has(key)) continue;
|
|
282
|
+
seen.add(key);
|
|
283
|
+
out.push(addr);
|
|
284
|
+
if (out.length >= 2) break;
|
|
285
|
+
}
|
|
286
|
+
return out;
|
|
287
|
+
}
|
|
288
|
+
function parsePermissionRow(raw) {
|
|
289
|
+
const tokenRaw = (raw.token ?? raw.address ?? "").toString().trim();
|
|
290
|
+
if (!tokenRaw) return null;
|
|
291
|
+
let token;
|
|
292
|
+
try {
|
|
293
|
+
token = getAddress(tokenRaw);
|
|
294
|
+
} catch {
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
const isPermissioned = Boolean(raw.isPermissioned);
|
|
298
|
+
const isAllowlisted = isPermissioned ? Boolean(raw.isAllowlisted) : true;
|
|
299
|
+
let adapterTokenAddress;
|
|
300
|
+
const adapterRaw = (raw.adapterTokenAddress ?? "").toString().trim();
|
|
301
|
+
if (adapterRaw) {
|
|
302
|
+
try {
|
|
303
|
+
adapterTokenAddress = getAddress(adapterRaw);
|
|
304
|
+
} catch {
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
const kycUrl = typeof raw.kycUrl === "string" && raw.kycUrl.trim() ? raw.kycUrl.trim() : void 0;
|
|
308
|
+
const issuer = typeof raw.issuer === "string" && raw.issuer.trim() ? raw.issuer.trim() : void 0;
|
|
309
|
+
return { token, isPermissioned, isAllowlisted, adapterTokenAddress, kycUrl, issuer };
|
|
310
|
+
}
|
|
311
|
+
async function uniswapV4CheckPermissions(args) {
|
|
312
|
+
const apiKey = args.uniswapApiKey?.trim();
|
|
313
|
+
if (!apiKey) throw new Error("uniswapApiKey is required for POST /v1/permissions");
|
|
314
|
+
const walletAddress = getAddress(args.walletAddress.trim());
|
|
315
|
+
const chainId = parseUniswapChainId(args.chainId);
|
|
316
|
+
const tokens = normalizeTokenList(args.tokens);
|
|
317
|
+
if (tokens.length === 0) {
|
|
318
|
+
return {
|
|
319
|
+
results: [],
|
|
320
|
+
anyPermissioned: false,
|
|
321
|
+
allAllowlisted: true,
|
|
322
|
+
requiresUniversalRouterV22: false
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
const base = (args.baseUrl?.trim() || UNISWAP_TRADE_BASE_DEFAULT).replace(/\/$/, "");
|
|
326
|
+
const url = `${base}/permissions`;
|
|
327
|
+
const fetchFn = args.fetchImpl ?? globalThis.fetch;
|
|
328
|
+
const res = await fetchFn(url, {
|
|
329
|
+
method: "POST",
|
|
330
|
+
headers: {
|
|
331
|
+
"Content-Type": "application/json",
|
|
332
|
+
"User-Agent": "ctm-mpc-defi uniswapPermissions/1.0 (TS)",
|
|
333
|
+
"x-api-key": apiKey
|
|
334
|
+
},
|
|
335
|
+
body: JSON.stringify({
|
|
336
|
+
walletAddress,
|
|
337
|
+
tokens,
|
|
338
|
+
chainId
|
|
339
|
+
}),
|
|
340
|
+
signal: args.signal
|
|
341
|
+
});
|
|
342
|
+
const text = await res.text();
|
|
343
|
+
if (!res.ok) {
|
|
344
|
+
throw new Error(
|
|
345
|
+
`Uniswap POST /permissions failed: ${messageFromUniswapHttpResponseBody(text, res.status, res.statusText)}`
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
let parsed;
|
|
349
|
+
try {
|
|
350
|
+
parsed = JSON.parse(text.replace(/^\uFEFF/, ""));
|
|
351
|
+
} catch {
|
|
352
|
+
throw new Error(
|
|
353
|
+
`Uniswap POST /permissions: ${messageFromUniswapHttpResponseBody(text, res.status, res.statusText)}`
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
const rawResults = Array.isArray(parsed.results) ? parsed.results : [];
|
|
357
|
+
const results = [];
|
|
358
|
+
for (const row of rawResults) {
|
|
359
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
360
|
+
const parsedRow = parsePermissionRow(row);
|
|
361
|
+
if (parsedRow) results.push(parsedRow);
|
|
362
|
+
}
|
|
363
|
+
for (const token of tokens) {
|
|
364
|
+
if (!results.some((r) => r.token === token)) {
|
|
365
|
+
results.push({ token, isPermissioned: false, isAllowlisted: true });
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
const anyPermissioned = results.some((r) => r.isPermissioned);
|
|
369
|
+
const allAllowlisted = results.every((r) => !r.isPermissioned || r.isAllowlisted);
|
|
370
|
+
return {
|
|
371
|
+
requestId: typeof parsed.requestId === "string" ? parsed.requestId : void 0,
|
|
372
|
+
results,
|
|
373
|
+
anyPermissioned,
|
|
374
|
+
allAllowlisted,
|
|
375
|
+
requiresUniversalRouterV22: anyPermissioned
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
var UniswapV4PermissionDeniedError = class extends Error {
|
|
379
|
+
code = "UNISWAP_V4_PERMISSION_DENIED";
|
|
380
|
+
permissions;
|
|
381
|
+
blocked;
|
|
382
|
+
constructor(permissions) {
|
|
383
|
+
const blocked = permissions.results.filter((r) => r.isPermissioned && !r.isAllowlisted);
|
|
384
|
+
const issuer = blocked.find((b) => b.issuer)?.issuer;
|
|
385
|
+
const kycUrl = blocked.find((b) => b.kycUrl)?.kycUrl;
|
|
386
|
+
const parts = [
|
|
387
|
+
"Wallet is not allowlisted for one or more permissioned Uniswap V4 tokens.",
|
|
388
|
+
issuer ? `Issuer: ${issuer}.` : null,
|
|
389
|
+
kycUrl ? `Complete KYC at: ${kycUrl}` : "Open the issuer KYC URL from check_permissions."
|
|
390
|
+
].filter(Boolean);
|
|
391
|
+
super(parts.join(" "));
|
|
392
|
+
this.name = "UniswapV4PermissionDeniedError";
|
|
393
|
+
this.permissions = permissions;
|
|
394
|
+
this.blocked = blocked;
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
function assertUniswapV4PermissionsAllowlisted(permissions) {
|
|
398
|
+
if (!permissions.allAllowlisted) {
|
|
399
|
+
throw new UniswapV4PermissionDeniedError(permissions);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
async function readUniswapV4OnChainPermissionFlags(args) {
|
|
403
|
+
const factory = tryGetUniswapV4PermissionsAdapterFactory(args.chainId);
|
|
404
|
+
if (!factory) return [];
|
|
405
|
+
const client = args.publicClient ?? createPublicClient({
|
|
406
|
+
chain: defineChain({
|
|
407
|
+
id: args.chainId,
|
|
408
|
+
name: `uniswap-perm-${args.chainId}`,
|
|
409
|
+
nativeCurrency: { name: "ETH", symbol: "ETH", decimals: 18 },
|
|
410
|
+
rpcUrls: { default: { http: [args.rpcUrl] } }
|
|
411
|
+
}),
|
|
412
|
+
transport: http(args.rpcUrl)
|
|
413
|
+
});
|
|
414
|
+
const wallet = getAddress(args.walletAddress);
|
|
415
|
+
const out = [];
|
|
416
|
+
for (const raw of args.currencyOrAdapterAddresses) {
|
|
417
|
+
let address;
|
|
418
|
+
try {
|
|
419
|
+
address = getAddress(raw);
|
|
420
|
+
} catch {
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
if (address === zeroAddress) continue;
|
|
424
|
+
let underlying;
|
|
425
|
+
try {
|
|
426
|
+
underlying = getAddress(
|
|
427
|
+
await client.readContract({
|
|
428
|
+
address: factory,
|
|
429
|
+
abi: factoryAbi,
|
|
430
|
+
functionName: "verifiedPermissionsAdapterOf",
|
|
431
|
+
args: [address]
|
|
432
|
+
})
|
|
433
|
+
);
|
|
434
|
+
} catch {
|
|
435
|
+
out.push({
|
|
436
|
+
address,
|
|
437
|
+
isVerifiedAdapter: false,
|
|
438
|
+
swapAllowed: true,
|
|
439
|
+
liquidityAllowed: true
|
|
440
|
+
});
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
if (underlying === zeroAddress) {
|
|
444
|
+
out.push({
|
|
445
|
+
address,
|
|
446
|
+
isVerifiedAdapter: false,
|
|
447
|
+
swapAllowed: true,
|
|
448
|
+
liquidityAllowed: true
|
|
449
|
+
});
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
const [swapAllowed, liquidityAllowed] = await Promise.all([
|
|
453
|
+
client.readContract({
|
|
454
|
+
address,
|
|
455
|
+
abi: permissionsAdapterAbi,
|
|
456
|
+
functionName: "isAllowed",
|
|
457
|
+
args: [wallet, UNISWAP_V4_PERMISSION_FLAG_SWAP]
|
|
458
|
+
}),
|
|
459
|
+
client.readContract({
|
|
460
|
+
address,
|
|
461
|
+
abi: permissionsAdapterAbi,
|
|
462
|
+
functionName: "isAllowed",
|
|
463
|
+
args: [wallet, UNISWAP_V4_PERMISSION_FLAG_LIQUIDITY]
|
|
464
|
+
})
|
|
465
|
+
]);
|
|
466
|
+
out.push({
|
|
467
|
+
address,
|
|
468
|
+
isVerifiedAdapter: true,
|
|
469
|
+
underlyingToken: underlying,
|
|
470
|
+
swapAllowed: Boolean(swapAllowed),
|
|
471
|
+
liquidityAllowed: Boolean(liquidityAllowed)
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
return out;
|
|
475
|
+
}
|
|
476
|
+
async function uniswapV4CheckPermissionsMcp(args) {
|
|
477
|
+
return uniswapV4CheckPermissions(args);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// src/protocols/evm/uniswap-v4/quote.ts
|
|
154
481
|
var DEFAULT_TRADE_BASE = "https://trade-api.gateway.uniswap.org/v1";
|
|
155
482
|
var UNISWAP_QUOTE_HEADERS_BASE = {
|
|
156
483
|
"Content-Type": "application/json",
|
|
@@ -400,7 +727,25 @@ async function uniswapTradeQuote(args) {
|
|
|
400
727
|
const base = args.baseUrl && args.baseUrl.trim() ? args.baseUrl.trim().replace(/\/$/, "") : DEFAULT_TRADE_BASE;
|
|
401
728
|
const quoteUrl = `${base}/quote`;
|
|
402
729
|
const chainIdForRouter = parseUniswapChainId(resolveInputChainId(args));
|
|
403
|
-
|
|
730
|
+
let permissions;
|
|
731
|
+
const shouldCheckPermissions = args.forcePermissionedRouter !== true && args.skipPermissionCheck !== true && args.checkPermissions !== false;
|
|
732
|
+
if (shouldCheckPermissions) {
|
|
733
|
+
try {
|
|
734
|
+
permissions = await uniswapV4CheckPermissions({
|
|
735
|
+
walletAddress: swapper,
|
|
736
|
+
tokens: [args.tokenIn, args.tokenOut],
|
|
737
|
+
chainId: chainIdForRouter,
|
|
738
|
+
uniswapApiKey: apiKey,
|
|
739
|
+
baseUrl: base,
|
|
740
|
+
signal: args.signal,
|
|
741
|
+
fetchImpl: args.fetchImpl
|
|
742
|
+
});
|
|
743
|
+
} catch {
|
|
744
|
+
permissions = void 0;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
const permissionedRouter = args.forcePermissionedRouter === true || Boolean(permissions?.requiresUniversalRouterV22);
|
|
748
|
+
const urv = args.universalRouterVersion && args.universalRouterVersion.trim() || getUniswapUniversalRouterVersion(chainIdForRouter, { permissioned: permissionedRouter });
|
|
404
749
|
const body = buildUniswapQuoteRequestBody({ ...args, swapper });
|
|
405
750
|
const nativeIn = isUniswapNativeTokenAddress(args.tokenIn);
|
|
406
751
|
const nativeOut = isUniswapNativeTokenAddress(args.tokenOut);
|
|
@@ -429,13 +774,27 @@ async function uniswapTradeQuote(args) {
|
|
|
429
774
|
}
|
|
430
775
|
const forParse = text.replace(/^\uFEFF/, "");
|
|
431
776
|
try {
|
|
432
|
-
|
|
777
|
+
const parsed = JSON.parse(forParse);
|
|
778
|
+
if (permissionedRouter || urv === UNISWAP_UNIVERSAL_ROUTER_VERSION_PERMISSIONED) {
|
|
779
|
+
parsed._continuumPermissioned = true;
|
|
780
|
+
parsed._continuumUniversalRouterVersion = urv;
|
|
781
|
+
}
|
|
782
|
+
if (permissions) {
|
|
783
|
+
parsed._continuumPermissions = permissions;
|
|
784
|
+
}
|
|
785
|
+
return parsed;
|
|
433
786
|
} catch {
|
|
434
787
|
throw new Error(
|
|
435
788
|
`Trade API: ${messageFromUniswapHttpResponseBody(text, res.status, res.statusText)}`
|
|
436
789
|
);
|
|
437
790
|
}
|
|
438
791
|
}
|
|
792
|
+
function isUniswapStoredQuotePermissioned(stored) {
|
|
793
|
+
if (!stored) return false;
|
|
794
|
+
if (stored._continuumPermissioned === true) return true;
|
|
795
|
+
const v = stored._continuumUniversalRouterVersion;
|
|
796
|
+
return typeof v === "string" && v.trim() === UNISWAP_UNIVERSAL_ROUTER_VERSION_PERMISSIONED;
|
|
797
|
+
}
|
|
439
798
|
function uniswapQuoteToJsonQuoteOneLine(quote) {
|
|
440
799
|
return JSON.stringify(quote);
|
|
441
800
|
}
|
|
@@ -989,7 +1348,9 @@ async function uniswapCreateSwap(args) {
|
|
|
989
1348
|
quote: classic,
|
|
990
1349
|
deadline
|
|
991
1350
|
};
|
|
992
|
-
const
|
|
1351
|
+
const chainIdForRouter = resolveChainIdFromStoredUniswapQuote(args.fullQuoteFromPermit);
|
|
1352
|
+
const permissioned = isUniswapStoredQuotePermissioned(args.fullQuoteFromPermit);
|
|
1353
|
+
const urv = (args.universalRouterVersion ?? "").trim() || getUniswapUniversalRouterVersion(chainIdForRouter, { permissioned });
|
|
993
1354
|
const fn = args.fetchImpl ?? globalThis.fetch;
|
|
994
1355
|
const nativeIn = isUniswapFullQuoteResponseNativeIn(args.fullQuoteFromPermit);
|
|
995
1356
|
const nativeOut = isUniswapFullQuoteResponseNativeOut(args.fullQuoteFromPermit);
|
|
@@ -1117,8 +1478,8 @@ function applySlippagePercentToApproveWei(baseWei, slippagePercent) {
|
|
|
1117
1478
|
if (bps <= 0) return baseWei;
|
|
1118
1479
|
return (baseWei * BigInt(1e4 + bps) + 9999n) / 10000n;
|
|
1119
1480
|
}
|
|
1120
|
-
function permit2SpenderAndApproveWeiFromSwapCalldata(dataHex, tokenIn, quoteInputWei, chainId) {
|
|
1121
|
-
const canonical = getUniswapUniversalRouterSpenderOrThrow(chainId);
|
|
1481
|
+
function permit2SpenderAndApproveWeiFromSwapCalldata(dataHex, tokenIn, quoteInputWei, chainId, permissioned = false) {
|
|
1482
|
+
const canonical = getUniswapUniversalRouterSpenderOrThrow(chainId, { permissioned });
|
|
1122
1483
|
try {
|
|
1123
1484
|
const d = decodeFunctionData({ abi: uniswapDispatchExecuteAbi, data: dataHex });
|
|
1124
1485
|
if (d.functionName !== "execute") {
|
|
@@ -1254,6 +1615,27 @@ async function buildEvmMultisignBodyUniswapV4SkipPermit2Batch(args) {
|
|
|
1254
1615
|
"Could not read a positive input amount from the quote. Refresh the quote and ensure the Trade API returned classic quote.input.amount."
|
|
1255
1616
|
);
|
|
1256
1617
|
}
|
|
1618
|
+
const permissioned = isUniswapStoredQuotePermissioned(args.fullQuoteSnapshot);
|
|
1619
|
+
if (args.skipPermissionCheck !== true) {
|
|
1620
|
+
const cached = args.fullQuoteSnapshot._continuumPermissions;
|
|
1621
|
+
if (cached && typeof cached === "object" && cached.allAllowlisted === false) {
|
|
1622
|
+
assertUniswapV4PermissionsAllowlisted({
|
|
1623
|
+
results: cached.results ?? [],
|
|
1624
|
+
anyPermissioned: Boolean(cached.anyPermissioned),
|
|
1625
|
+
allAllowlisted: false,
|
|
1626
|
+
requiresUniversalRouterV22: Boolean(cached.requiresUniversalRouterV22)
|
|
1627
|
+
});
|
|
1628
|
+
} else if (args.uniswapApiKey?.trim() && (permissioned || args.tokenOut)) {
|
|
1629
|
+
const tokens = [args.tokenIn, args.tokenOut].filter(Boolean);
|
|
1630
|
+
const permissions = await uniswapV4CheckPermissions({
|
|
1631
|
+
walletAddress: args.executorAddress,
|
|
1632
|
+
tokens,
|
|
1633
|
+
chainId: args.chainId,
|
|
1634
|
+
uniswapApiKey: args.uniswapApiKey.trim()
|
|
1635
|
+
});
|
|
1636
|
+
assertUniswapV4PermissionsAllowlisted(permissions);
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1257
1639
|
if (tokenIn === zeroAddress) {
|
|
1258
1640
|
return buildEvmMultisignBodyUniswapV4NativeInOnly(args, quoteInputWei);
|
|
1259
1641
|
}
|
|
@@ -1264,12 +1646,15 @@ async function buildEvmMultisignBodyUniswapV4SkipPermit2Batch(args) {
|
|
|
1264
1646
|
const d = (args.swap.data ?? "0x").toString().trim();
|
|
1265
1647
|
return d.startsWith("0x") ? d : `0x${d}`;
|
|
1266
1648
|
})();
|
|
1267
|
-
const canonicalUniversalRouter = getUniswapUniversalRouterSpenderOrThrow(args.chainId
|
|
1649
|
+
const canonicalUniversalRouter = getUniswapUniversalRouterSpenderOrThrow(args.chainId, {
|
|
1650
|
+
permissioned
|
|
1651
|
+
});
|
|
1268
1652
|
const { permit2Spender, approveWei: baseApproveWei, calldataAmountWei } = permit2SpenderAndApproveWeiFromSwapCalldata(
|
|
1269
1653
|
dataHex,
|
|
1270
1654
|
tokenIn,
|
|
1271
1655
|
quoteInputWei,
|
|
1272
|
-
args.chainId
|
|
1656
|
+
args.chainId,
|
|
1657
|
+
permissioned
|
|
1273
1658
|
);
|
|
1274
1659
|
const approveAmountWei = applySlippagePercentToApproveWei(baseApproveWei, args.slippagePercent);
|
|
1275
1660
|
const usePermit2Triple = toRouter.toLowerCase() === permit2Spender.toLowerCase();
|
|
@@ -2319,6 +2704,9 @@ var erc20AllowanceAbi2 = parseAbi([
|
|
|
2319
2704
|
"function allowance(address owner, address spender) view returns (uint256)",
|
|
2320
2705
|
"function decimals() view returns (uint8)"
|
|
2321
2706
|
]);
|
|
2707
|
+
var permit2ApproveAbi = parseAbi([
|
|
2708
|
+
"function approve(address token, address spender, uint160 amount, uint48 expiration)"
|
|
2709
|
+
]);
|
|
2322
2710
|
function parseOptionalGasLimitString2(raw) {
|
|
2323
2711
|
if (raw == null) return null;
|
|
2324
2712
|
const s = String(raw).trim();
|
|
@@ -2442,7 +2830,9 @@ async function buildEvmMultisignBodyUniswapV4LiquidityBatchInternal(args) {
|
|
|
2442
2830
|
const dataHex = dataHexFromTx(tx);
|
|
2443
2831
|
const valueWei = parseTxValueWei(tx);
|
|
2444
2832
|
const executor = getAddress(args.executorAddress);
|
|
2445
|
-
const
|
|
2833
|
+
const positionManagerKind = args.positionManagerKind === "permissioned" || args.positionManagerKind === "standard" ? args.positionManagerKind : isUniswapV4PermissionedPositionManager(args.chainId, to) ? "permissioned" : "standard";
|
|
2834
|
+
const usePermit2 = args.usePermit2Approvals === true || args.usePermit2Approvals !== false && positionManagerKind === "permissioned";
|
|
2835
|
+
const erc20Spender = usePermit2 ? PERMIT2_ADDRESS : to;
|
|
2446
2836
|
const chain = defineChain({
|
|
2447
2837
|
id: args.chainId,
|
|
2448
2838
|
name: `uniswap-v4-lp-${args.chainId}`,
|
|
@@ -2450,15 +2840,21 @@ async function buildEvmMultisignBodyUniswapV4LiquidityBatchInternal(args) {
|
|
|
2450
2840
|
rpcUrls: { default: { http: [args.rpcUrl] } }
|
|
2451
2841
|
});
|
|
2452
2842
|
const publicClient = createPublicClient({ chain, transport: http(args.rpcUrl) });
|
|
2453
|
-
const
|
|
2843
|
+
const needsTokenApproves = args.action === "mint" || args.action === "increase";
|
|
2844
|
+
const approveTargets = needsTokenApproves ? await resolveApproveTargets({
|
|
2454
2845
|
publicClient,
|
|
2455
2846
|
executor,
|
|
2456
|
-
spender,
|
|
2847
|
+
spender: erc20Spender,
|
|
2457
2848
|
token0: parsed.token0,
|
|
2458
2849
|
token1: parsed.token1,
|
|
2459
2850
|
nativeWrapped: args.nativeWrapped
|
|
2460
|
-
});
|
|
2851
|
+
}) : [];
|
|
2461
2852
|
const steps = [];
|
|
2853
|
+
let permit2Expiration48 = 0n;
|
|
2854
|
+
if (usePermit2 && needsTokenApproves) {
|
|
2855
|
+
permit2Expiration48 = BigInt(Math.floor(Date.now() / 1e3) + UNISWAP_LP_DEFAULT_DEADLINE_SEC_OFFSET);
|
|
2856
|
+
if (permit2Expiration48 > MAX_UINT48) permit2Expiration48 = MAX_UINT48;
|
|
2857
|
+
}
|
|
2462
2858
|
for (const target of approveTargets) {
|
|
2463
2859
|
if (target.kind === "weth_deposit") {
|
|
2464
2860
|
steps.push({
|
|
@@ -2471,13 +2867,17 @@ async function buildEvmMultisignBodyUniswapV4LiquidityBatchInternal(args) {
|
|
|
2471
2867
|
address: target.token,
|
|
2472
2868
|
abi: erc20AllowanceAbi2,
|
|
2473
2869
|
functionName: "allowance",
|
|
2474
|
-
args: [executor,
|
|
2870
|
+
args: [executor, erc20Spender]
|
|
2475
2871
|
});
|
|
2476
2872
|
if (wethAllowance < target.amount) {
|
|
2477
|
-
if (wethAllowance > 0n) {
|
|
2873
|
+
if (wethAllowance > 0n && !usePermit2) {
|
|
2478
2874
|
steps.push({
|
|
2479
2875
|
to: target.token,
|
|
2480
|
-
data: encodeFunctionData({
|
|
2876
|
+
data: encodeFunctionData({
|
|
2877
|
+
abi: erc20Abi,
|
|
2878
|
+
functionName: "approve",
|
|
2879
|
+
args: [erc20Spender, 0n]
|
|
2880
|
+
}),
|
|
2481
2881
|
value: 0n,
|
|
2482
2882
|
fallbackGas: UNISWAP_V4_LP_ERC20_APPROVE_FALLBACK
|
|
2483
2883
|
});
|
|
@@ -2487,7 +2887,7 @@ async function buildEvmMultisignBodyUniswapV4LiquidityBatchInternal(args) {
|
|
|
2487
2887
|
data: encodeFunctionData({
|
|
2488
2888
|
abi: erc20Abi,
|
|
2489
2889
|
functionName: "approve",
|
|
2490
|
-
args: [
|
|
2890
|
+
args: [erc20Spender, target.amount]
|
|
2491
2891
|
}),
|
|
2492
2892
|
value: 0n,
|
|
2493
2893
|
fallbackGas: UNISWAP_V4_LP_ERC20_APPROVE_FALLBACK
|
|
@@ -2499,13 +2899,66 @@ async function buildEvmMultisignBodyUniswapV4LiquidityBatchInternal(args) {
|
|
|
2499
2899
|
data: encodeFunctionData({
|
|
2500
2900
|
abi: erc20Abi,
|
|
2501
2901
|
functionName: "approve",
|
|
2502
|
-
args: [
|
|
2902
|
+
args: [erc20Spender, target.amount]
|
|
2503
2903
|
}),
|
|
2504
2904
|
value: 0n,
|
|
2505
2905
|
fallbackGas: UNISWAP_V4_LP_ERC20_APPROVE_FALLBACK
|
|
2506
2906
|
});
|
|
2507
2907
|
}
|
|
2508
2908
|
}
|
|
2909
|
+
if (usePermit2 && needsTokenApproves) {
|
|
2910
|
+
const pairs = [parsed.token0, parsed.token1].filter(Boolean);
|
|
2911
|
+
for (const row of pairs) {
|
|
2912
|
+
const amt = BigInt(row.amount);
|
|
2913
|
+
if (amt <= 0n) continue;
|
|
2914
|
+
let token;
|
|
2915
|
+
if (isNativeUniswapLpTokenAddress(row.tokenAddress)) {
|
|
2916
|
+
if (!args.nativeWrapped) {
|
|
2917
|
+
throw new Error("nativeWrapped is required when LP uses native ETH (0x0) token.");
|
|
2918
|
+
}
|
|
2919
|
+
token = getAddress(args.nativeWrapped);
|
|
2920
|
+
if (!approveTargets.some((a) => a.token === token && a.kind === "weth_deposit")) {
|
|
2921
|
+
steps.push({
|
|
2922
|
+
to: token,
|
|
2923
|
+
data: encodeFunctionData({ abi: wethDepositAbi, functionName: "deposit" }),
|
|
2924
|
+
value: amt,
|
|
2925
|
+
fallbackGas: UNISWAP_V4_LP_WETH_DEPOSIT_FALLBACK
|
|
2926
|
+
});
|
|
2927
|
+
const wethAllowance = await publicClient.readContract({
|
|
2928
|
+
address: token,
|
|
2929
|
+
abi: erc20AllowanceAbi2,
|
|
2930
|
+
functionName: "allowance",
|
|
2931
|
+
args: [executor, PERMIT2_ADDRESS]
|
|
2932
|
+
});
|
|
2933
|
+
if (wethAllowance < amt) {
|
|
2934
|
+
steps.push({
|
|
2935
|
+
to: token,
|
|
2936
|
+
data: encodeFunctionData({
|
|
2937
|
+
abi: erc20Abi,
|
|
2938
|
+
functionName: "approve",
|
|
2939
|
+
args: [PERMIT2_ADDRESS, amt]
|
|
2940
|
+
}),
|
|
2941
|
+
value: 0n,
|
|
2942
|
+
fallbackGas: UNISWAP_V4_LP_ERC20_APPROVE_FALLBACK
|
|
2943
|
+
});
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
2946
|
+
} else {
|
|
2947
|
+
token = getAddress(row.tokenAddress);
|
|
2948
|
+
}
|
|
2949
|
+
const amount160 = amt > MAX_UINT160 ? MAX_UINT160 : amt;
|
|
2950
|
+
steps.push({
|
|
2951
|
+
to: PERMIT2_ADDRESS,
|
|
2952
|
+
data: encodeFunctionData({
|
|
2953
|
+
abi: permit2ApproveAbi,
|
|
2954
|
+
functionName: "approve",
|
|
2955
|
+
args: [token, to, amount160, Number(permit2Expiration48)]
|
|
2956
|
+
}),
|
|
2957
|
+
value: 0n,
|
|
2958
|
+
fallbackGas: UNISWAP_V4_LP_PERMIT2_APPROVE_FALLBACK
|
|
2959
|
+
});
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2509
2962
|
const lpFallbackGas = defaultGasForAction(args.action);
|
|
2510
2963
|
const fromTradeApi = parseOptionalGasLimitString2(tx.gasLimit) ?? parseOptionalGasLimitString2(tx.gas);
|
|
2511
2964
|
steps.push({
|
|
@@ -2517,6 +2970,7 @@ async function buildEvmMultisignBodyUniswapV4LiquidityBatchInternal(args) {
|
|
|
2517
2970
|
const actionLabel = args.action === "mint" ? "mint liquidity" : args.action === "increase" ? "increase liquidity" : args.action === "decrease" ? "decrease liquidity" : "collect fees";
|
|
2518
2971
|
const dataNo0x = dataHex.startsWith("0x") ? dataHex.slice(2) : dataHex;
|
|
2519
2972
|
const lpIndex = steps.length - 1;
|
|
2973
|
+
const purposeSuffix = usePermit2 ? `Uniswap V4 permissioned: ${steps.length}-tx batch \u2014 ${actionLabel} (ERC-20\u2192Permit2, Permit2\u2192PosM, Position Manager).` : `Uniswap V4: ${steps.length}-tx batch \u2014 ${actionLabel} (classic ERC-20 approve + Position Manager).`;
|
|
2520
2974
|
return buildEvmMultisignBatch({
|
|
2521
2975
|
context: {
|
|
2522
2976
|
chainCategory: "evm",
|
|
@@ -2530,7 +2984,7 @@ async function buildEvmMultisignBodyUniswapV4LiquidityBatchInternal(args) {
|
|
|
2530
2984
|
customGasChainDetails: args.customGasChainDetails
|
|
2531
2985
|
},
|
|
2532
2986
|
steps,
|
|
2533
|
-
purposeSuffix
|
|
2987
|
+
purposeSuffix,
|
|
2534
2988
|
firstMsgRawNo0x: dataNo0x,
|
|
2535
2989
|
destinationAddress: to,
|
|
2536
2990
|
buildBatchMeta: ({ index }) => {
|
|
@@ -2540,13 +2994,17 @@ async function buildEvmMultisignBodyUniswapV4LiquidityBatchInternal(args) {
|
|
|
2540
2994
|
kind: "UniswapV4Liquidity",
|
|
2541
2995
|
action: args.action,
|
|
2542
2996
|
step: index,
|
|
2543
|
-
lpTx: isLpStep
|
|
2997
|
+
lpTx: isLpStep,
|
|
2998
|
+
positionManagerKind,
|
|
2999
|
+
usePermit2
|
|
2544
3000
|
}),
|
|
2545
3001
|
...isLpStep ? {
|
|
2546
3002
|
evm: { type: "uniswap_v4_liquidity_tx", version: 1, chainId: String(args.chainId) },
|
|
2547
3003
|
uniswapV4Liquidity: {
|
|
2548
3004
|
action: args.action,
|
|
2549
|
-
skipPermit2Batch:
|
|
3005
|
+
skipPermit2Batch: !usePermit2,
|
|
3006
|
+
usePermit2Approvals: usePermit2,
|
|
3007
|
+
positionManagerKind,
|
|
2550
3008
|
nftTokenId: args.nftTokenId != null ? String(args.nftTokenId) : void 0,
|
|
2551
3009
|
poolReference: args.poolReference,
|
|
2552
3010
|
lpResponseSnapshot: args.lpResponse,
|
|
@@ -2579,6 +3037,260 @@ async function buildEvmMultisignBodyUniswapV4DecreaseLiquidityBatch(args) {
|
|
|
2579
3037
|
async function buildEvmMultisignBodyUniswapV4CollectFeesBatch(args) {
|
|
2580
3038
|
return buildEvmMultisignBodyUniswapV4LiquidityBatchInternal({ ...args, action: "collect" });
|
|
2581
3039
|
}
|
|
3040
|
+
var PERMISSIONED_LP_PRESETS = [
|
|
3041
|
+
// No public launch-partner presets hard-coded yet — use custom existingPool with hooks.
|
|
3042
|
+
];
|
|
3043
|
+
function buildUniswapV4PermissionedLpPoolRow(args) {
|
|
3044
|
+
const chainId = parseUniswapChainId(args.chainId);
|
|
3045
|
+
const hooks = (args.hooks?.trim() ? getAddress(args.hooks.trim()) : tryGetUniswapV4PermissionedHooks(chainId)) ?? (() => {
|
|
3046
|
+
throw new Error(`No PermissionedHooks for chainId ${chainId}; pass hooks explicitly.`);
|
|
3047
|
+
})();
|
|
3048
|
+
const positionManager = tryGetUniswapV4PermissionedPositionManager(chainId);
|
|
3049
|
+
if (!positionManager) {
|
|
3050
|
+
throw new Error(`No Permissioned PositionManager for chainId ${chainId}.`);
|
|
3051
|
+
}
|
|
3052
|
+
const adapter = getAddress(args.adapterAddress);
|
|
3053
|
+
const other = getAddress(args.otherCurrencyAddress);
|
|
3054
|
+
const sorted = sortUniswapV4PoolCurrencies(adapter, other);
|
|
3055
|
+
const poolReference = computeUniswapV4PoolReference({
|
|
3056
|
+
token0Address: sorted.currency0,
|
|
3057
|
+
token1Address: sorted.currency1,
|
|
3058
|
+
fee: args.fee,
|
|
3059
|
+
tickSpacing: args.tickSpacing,
|
|
3060
|
+
hooks
|
|
3061
|
+
});
|
|
3062
|
+
return {
|
|
3063
|
+
presetId: args.presetId,
|
|
3064
|
+
pairSlug: args.pairSlug,
|
|
3065
|
+
pairLabel: args.pairLabel,
|
|
3066
|
+
fee: args.fee,
|
|
3067
|
+
feeLabel: args.feeLabel,
|
|
3068
|
+
tickSpacing: args.tickSpacing,
|
|
3069
|
+
token0Symbol: sorted.currency0 === adapter ? args.adapterSymbol ?? "v4PT" : args.otherSymbol ?? "TOKEN",
|
|
3070
|
+
token1Symbol: sorted.currency1 === adapter ? args.adapterSymbol ?? "v4PT" : args.otherSymbol ?? "TOKEN",
|
|
3071
|
+
token0Address: sorted.currency0,
|
|
3072
|
+
token1Address: sorted.currency1,
|
|
3073
|
+
poolReference,
|
|
3074
|
+
hooks,
|
|
3075
|
+
nativeWrapped: args.nativeWrapped ? getAddress(args.nativeWrapped) : void 0,
|
|
3076
|
+
usesNativeEth: Boolean(args.usesNativeEth),
|
|
3077
|
+
positionManagerKind: "permissioned",
|
|
3078
|
+
positionManager,
|
|
3079
|
+
underlyingPermissionedToken: args.underlyingPermissionedToken ? getAddress(args.underlyingPermissionedToken) : void 0,
|
|
3080
|
+
adapterAddress: adapter,
|
|
3081
|
+
issuer: args.issuer
|
|
3082
|
+
};
|
|
3083
|
+
}
|
|
3084
|
+
function listUniswapV4PermissionedLpPools(args) {
|
|
3085
|
+
const chainId = parseUniswapChainId(args.chainId);
|
|
3086
|
+
const positionManager = tryGetUniswapV4PermissionedPositionManager(chainId);
|
|
3087
|
+
const hooks = tryGetUniswapV4PermissionedHooks(chainId);
|
|
3088
|
+
if (!positionManager || !hooks) {
|
|
3089
|
+
return {
|
|
3090
|
+
chainId,
|
|
3091
|
+
chainLabel: `chain ${chainId}`,
|
|
3092
|
+
pools: [],
|
|
3093
|
+
positionManagerKind: "permissioned",
|
|
3094
|
+
notes: "No Permissioned PositionManager / PermissionedHooks configured for this chain. Ethereum mainnet (1) and Sepolia (11155111) are supported. Pass custom existingPool with adapter currency + hooks."
|
|
3095
|
+
};
|
|
3096
|
+
}
|
|
3097
|
+
let pools = PERMISSIONED_LP_PRESETS.filter(
|
|
3098
|
+
(p) => p.chainId === chainId
|
|
3099
|
+
).map(
|
|
3100
|
+
(p) => buildUniswapV4PermissionedLpPoolRow({
|
|
3101
|
+
chainId,
|
|
3102
|
+
presetId: p.presetId,
|
|
3103
|
+
pairSlug: p.pairSlug,
|
|
3104
|
+
pairLabel: p.pairLabel,
|
|
3105
|
+
fee: p.fee,
|
|
3106
|
+
feeLabel: p.feeLabel,
|
|
3107
|
+
tickSpacing: p.tickSpacing,
|
|
3108
|
+
adapterAddress: p.adapterAddress,
|
|
3109
|
+
otherCurrencyAddress: p.otherCurrency,
|
|
3110
|
+
adapterSymbol: p.adapterSymbol,
|
|
3111
|
+
otherSymbol: p.otherSymbol,
|
|
3112
|
+
underlyingPermissionedToken: p.underlyingPermissionedToken,
|
|
3113
|
+
usesNativeEth: p.usesNativeEth,
|
|
3114
|
+
nativeWrapped: p.nativeWrapped,
|
|
3115
|
+
issuer: p.issuer,
|
|
3116
|
+
hooks
|
|
3117
|
+
})
|
|
3118
|
+
);
|
|
3119
|
+
const pairFilter = args.pair?.trim().toLowerCase();
|
|
3120
|
+
if (pairFilter) {
|
|
3121
|
+
pools = pools.filter(
|
|
3122
|
+
(row) => row.pairSlug.includes(pairFilter) || row.pairLabel.toLowerCase().includes(pairFilter) || row.presetId.includes(pairFilter)
|
|
3123
|
+
);
|
|
3124
|
+
}
|
|
3125
|
+
const chainLabel = PERMISSIONED_LP_PRESETS.find((p) => p.chainId === chainId)?.chainLabel ?? (chainId === 1 ? "Ethereum" : chainId === 11155111 ? "Sepolia" : `chain ${chainId}`);
|
|
3126
|
+
return {
|
|
3127
|
+
chainId,
|
|
3128
|
+
chainLabel,
|
|
3129
|
+
pools,
|
|
3130
|
+
positionManagerKind: "permissioned",
|
|
3131
|
+
notes: `Permissioned LP uses Permissioned Position Manager (UNI-V4-PERM-POSM) and PermissionedHooks. Pool currencies use the Permissions Adapter address, not the underlying token. Requires LIQUIDITY_ALLOWED on the wallet. Curated presets may be empty \u2014 pass existingPool with adapter + hooks + fee/tickSpacing. positionManager=${positionManager} hooks=${hooks}`
|
|
3132
|
+
};
|
|
3133
|
+
}
|
|
3134
|
+
function resolveUniswapV4PermissionedLpPoolPreset(args) {
|
|
3135
|
+
const listed = listUniswapV4PermissionedLpPools({ chainId: args.chainId });
|
|
3136
|
+
const preset = args.presetId.trim().toLowerCase();
|
|
3137
|
+
const row = listed.pools.find((p) => p.presetId.toLowerCase() === preset);
|
|
3138
|
+
if (!row) {
|
|
3139
|
+
throw new Error(
|
|
3140
|
+
`Unknown permissioned LP pool preset "${args.presetId}" on chainId ${listed.chainId}. Call ctm_uniswap_v4_list_lp_pools with permissioned=true, or pass custom existingPool.`
|
|
3141
|
+
);
|
|
3142
|
+
}
|
|
3143
|
+
return row;
|
|
3144
|
+
}
|
|
3145
|
+
function uniswapV4ListLpPools(args) {
|
|
3146
|
+
if (args.permissioned) {
|
|
3147
|
+
return listUniswapV4PermissionedLpPools(args);
|
|
3148
|
+
}
|
|
3149
|
+
const std = listUniswapV4StandardLpPools(args);
|
|
3150
|
+
return {
|
|
3151
|
+
...std,
|
|
3152
|
+
positionManagerKind: "standard"
|
|
3153
|
+
};
|
|
3154
|
+
}
|
|
3155
|
+
function buildUniswapV4KycApplyLink(args) {
|
|
3156
|
+
const walletAddress = getAddress(args.walletAddress.trim());
|
|
3157
|
+
const chainId = parseUniswapChainId(args.chainId);
|
|
3158
|
+
const kycUrl = args.kycUrl.trim();
|
|
3159
|
+
if (!kycUrl) throw new Error("kycUrl is required");
|
|
3160
|
+
let token;
|
|
3161
|
+
let adapterTokenAddress;
|
|
3162
|
+
if (args.token?.trim()) {
|
|
3163
|
+
try {
|
|
3164
|
+
token = getAddress(args.token.trim());
|
|
3165
|
+
} catch {
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
if (args.adapterTokenAddress?.trim()) {
|
|
3169
|
+
try {
|
|
3170
|
+
adapterTokenAddress = getAddress(args.adapterTokenAddress.trim());
|
|
3171
|
+
} catch {
|
|
3172
|
+
}
|
|
3173
|
+
}
|
|
3174
|
+
let applyUrl = kycUrl;
|
|
3175
|
+
try {
|
|
3176
|
+
const u = new URL(kycUrl);
|
|
3177
|
+
if (!u.searchParams.has("wallet") && !u.searchParams.has("address")) {
|
|
3178
|
+
u.searchParams.set("wallet", walletAddress);
|
|
3179
|
+
u.searchParams.set("address", walletAddress);
|
|
3180
|
+
}
|
|
3181
|
+
if (!u.searchParams.has("chainId")) u.searchParams.set("chainId", String(chainId));
|
|
3182
|
+
if (token && !u.searchParams.has("token")) u.searchParams.set("token", token);
|
|
3183
|
+
applyUrl = u.toString();
|
|
3184
|
+
} catch {
|
|
3185
|
+
applyUrl = kycUrl;
|
|
3186
|
+
}
|
|
3187
|
+
return {
|
|
3188
|
+
walletAddress,
|
|
3189
|
+
chainId,
|
|
3190
|
+
issuer: args.issuer?.trim() || void 0,
|
|
3191
|
+
kycUrl,
|
|
3192
|
+
applyUrl,
|
|
3193
|
+
token,
|
|
3194
|
+
adapterTokenAddress
|
|
3195
|
+
};
|
|
3196
|
+
}
|
|
3197
|
+
function pickUniswapV4KycTarget(results) {
|
|
3198
|
+
return results.find((r) => r.isPermissioned && !r.isAllowlisted && r.kycUrl) ?? results.find((r) => r.isPermissioned && r.kycUrl) ?? null;
|
|
3199
|
+
}
|
|
3200
|
+
function uniswapV4KycApplyLinkMcp(args) {
|
|
3201
|
+
return buildUniswapV4KycApplyLink(args);
|
|
3202
|
+
}
|
|
3203
|
+
var ALLOWLIST_FINALIZE_FALLBACK_GAS = 250000n;
|
|
3204
|
+
async function buildEvmMultisignBodyUniswapV4PermissionedAllowlistFinalize(args) {
|
|
3205
|
+
const to = getAddress(args.to.trim());
|
|
3206
|
+
let dataHex = (args.data ?? "0x").toString().trim();
|
|
3207
|
+
if (!dataHex.startsWith("0x")) dataHex = `0x${dataHex}`;
|
|
3208
|
+
if (dataHex.length < 10) {
|
|
3209
|
+
throw new Error("allowlist finalize `data` must be non-empty ABI-encoded calldata");
|
|
3210
|
+
}
|
|
3211
|
+
let value = 0n;
|
|
3212
|
+
if (args.valueWei != null && String(args.valueWei).trim() !== "") {
|
|
3213
|
+
try {
|
|
3214
|
+
value = BigInt(String(args.valueWei).trim());
|
|
3215
|
+
} catch {
|
|
3216
|
+
value = 0n;
|
|
3217
|
+
}
|
|
3218
|
+
}
|
|
3219
|
+
const steps = [
|
|
3220
|
+
{
|
|
3221
|
+
to,
|
|
3222
|
+
data: dataHex,
|
|
3223
|
+
value,
|
|
3224
|
+
fallbackGas: ALLOWLIST_FINALIZE_FALLBACK_GAS
|
|
3225
|
+
}
|
|
3226
|
+
];
|
|
3227
|
+
const dataNo0x = dataHex.slice(2);
|
|
3228
|
+
const issuerLabel = args.issuer?.trim() || "issuer";
|
|
3229
|
+
return buildEvmMultisignBatch({
|
|
3230
|
+
context: {
|
|
3231
|
+
chainCategory: "evm",
|
|
3232
|
+
keyGen: args.keyGen,
|
|
3233
|
+
purposeText: args.purposeText,
|
|
3234
|
+
chainId: args.chainId,
|
|
3235
|
+
rpcUrl: args.rpcUrl,
|
|
3236
|
+
executorAddress: args.executorAddress,
|
|
3237
|
+
chainDetail: args.chainDetail,
|
|
3238
|
+
useCustomGas: args.useCustomGas,
|
|
3239
|
+
customGasChainDetails: args.customGasChainDetails
|
|
3240
|
+
},
|
|
3241
|
+
steps,
|
|
3242
|
+
purposeSuffix: `Uniswap V4 permissioned: 1-tx allowlist finalize (${issuerLabel}).`,
|
|
3243
|
+
firstMsgRawNo0x: dataNo0x,
|
|
3244
|
+
destinationAddress: to,
|
|
3245
|
+
buildBatchMeta: () => ({
|
|
3246
|
+
signatureText: JSON.stringify({
|
|
3247
|
+
kind: "UniswapV4PermissionedAllowlist",
|
|
3248
|
+
issuer: args.issuer,
|
|
3249
|
+
entityId: args.entityId != null ? String(args.entityId) : void 0
|
|
3250
|
+
}),
|
|
3251
|
+
evm: {
|
|
3252
|
+
type: "uniswap_v4_permissioned_allowlist",
|
|
3253
|
+
version: 1,
|
|
3254
|
+
chainId: String(args.chainId)
|
|
3255
|
+
},
|
|
3256
|
+
uniswapV4PermissionedAllowlist: {
|
|
3257
|
+
to,
|
|
3258
|
+
dataNibbles: dataNo0x.length,
|
|
3259
|
+
issuer: args.issuer,
|
|
3260
|
+
entityId: args.entityId != null ? String(args.entityId) : void 0,
|
|
3261
|
+
tokenAddress: args.tokenAddress,
|
|
3262
|
+
originalPurpose: args.purposeText
|
|
3263
|
+
}
|
|
3264
|
+
})
|
|
3265
|
+
});
|
|
3266
|
+
}
|
|
3267
|
+
function isUniswapV4PermissionedAllowlistEvmSignRequest(detail) {
|
|
3268
|
+
if (!detail) return false;
|
|
3269
|
+
const raw = detail.ExtraJSON ?? detail.extraJSON;
|
|
3270
|
+
let ex = null;
|
|
3271
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) ex = raw;
|
|
3272
|
+
else if (typeof raw === "string" && raw.trim()) {
|
|
3273
|
+
try {
|
|
3274
|
+
const p = JSON.parse(raw);
|
|
3275
|
+
if (p && typeof p === "object" && !Array.isArray(p)) ex = p;
|
|
3276
|
+
} catch {
|
|
3277
|
+
return false;
|
|
3278
|
+
}
|
|
3279
|
+
}
|
|
3280
|
+
if (!ex) return false;
|
|
3281
|
+
const bm = ex.batchMeta;
|
|
3282
|
+
if (Array.isArray(bm) && bm[0] && typeof bm[0] === "object" && !Array.isArray(bm[0])) {
|
|
3283
|
+
const evm0 = bm[0].evm;
|
|
3284
|
+
if (evm0 && typeof evm0 === "object" && !Array.isArray(evm0)) {
|
|
3285
|
+
if (String(evm0.type ?? "") === "uniswap_v4_permissioned_allowlist") {
|
|
3286
|
+
return true;
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3290
|
+
const evm = ex.evm;
|
|
3291
|
+
if (!evm || typeof evm !== "object" || Array.isArray(evm)) return false;
|
|
3292
|
+
return String(evm.type ?? "") === "uniswap_v4_permissioned_allowlist";
|
|
3293
|
+
}
|
|
2582
3294
|
|
|
2583
3295
|
// src/protocols/evm/uniswap-v4/support.ts
|
|
2584
3296
|
function computeUniswapV4Session(args) {
|
|
@@ -2597,7 +3309,12 @@ function computeUniswapV4Session(args) {
|
|
|
2597
3309
|
chainId: chainIdNum,
|
|
2598
3310
|
rpcUrl,
|
|
2599
3311
|
chainSupported,
|
|
2600
|
-
universalRouter
|
|
3312
|
+
universalRouter,
|
|
3313
|
+
universalRouterV22: tryGetUniswapUniversalRouterV22(chainIdNum) ?? void 0,
|
|
3314
|
+
positionManager: tryGetUniswapV4PositionManager(chainIdNum) ?? void 0,
|
|
3315
|
+
permissionedPositionManager: tryGetUniswapV4PermissionedPositionManager(chainIdNum) ?? void 0,
|
|
3316
|
+
permissionedHooks: tryGetUniswapV4PermissionedHooks(chainIdNum) ?? void 0,
|
|
3317
|
+
permissionsAdapterFactory: tryGetUniswapV4PermissionsAdapterFactory(chainIdNum) ?? void 0
|
|
2601
3318
|
};
|
|
2602
3319
|
}
|
|
2603
3320
|
|
|
@@ -2888,6 +3605,28 @@ async function fetchUniswapV4PoolMeta(args) {
|
|
|
2888
3605
|
);
|
|
2889
3606
|
return data.pool ?? null;
|
|
2890
3607
|
}
|
|
3608
|
+
var LATEST_SWAP_GQL = `
|
|
3609
|
+
query UniswapV4LatestSwap($pool: String!) {
|
|
3610
|
+
swaps(
|
|
3611
|
+
where: { pool: $pool }
|
|
3612
|
+
orderBy: timestamp
|
|
3613
|
+
orderDirection: desc
|
|
3614
|
+
first: 1
|
|
3615
|
+
) {
|
|
3616
|
+
token0Price
|
|
3617
|
+
sqrtPriceX96
|
|
3618
|
+
}
|
|
3619
|
+
}`;
|
|
3620
|
+
async function fetchUniswapV4LatestSwap(args) {
|
|
3621
|
+
const pool = normalizePoolId(args.poolReference);
|
|
3622
|
+
const data = await postUniswapV4SubgraphGql(
|
|
3623
|
+
args.chainId,
|
|
3624
|
+
LATEST_SWAP_GQL,
|
|
3625
|
+
{ pool },
|
|
3626
|
+
args.theGraphApiKey
|
|
3627
|
+
);
|
|
3628
|
+
return data.swaps?.[0] ?? null;
|
|
3629
|
+
}
|
|
2891
3630
|
|
|
2892
3631
|
// src/protocols/evm/uniswap-v4/bitqueryApiKey.ts
|
|
2893
3632
|
var BITQUERY_API_KEY_ENV = "BITQUERY_API_KEY";
|
|
@@ -3176,7 +3915,7 @@ function parseDecimal(raw) {
|
|
|
3176
3915
|
const n = Number(raw);
|
|
3177
3916
|
return Number.isFinite(n) && n > 0 ? n : null;
|
|
3178
3917
|
}
|
|
3179
|
-
function
|
|
3918
|
+
function priceFromUniswapV4SwapRow(row) {
|
|
3180
3919
|
const fromToken0 = parseDecimal(row.token0Price);
|
|
3181
3920
|
if (fromToken0 != null) return fromToken0;
|
|
3182
3921
|
const sqrt = row.sqrtPriceX96?.trim();
|
|
@@ -3198,7 +3937,7 @@ function bucketUniswapV4SwapsIntoCandles(args) {
|
|
|
3198
3937
|
const buckets = /* @__PURE__ */ new Map();
|
|
3199
3938
|
for (const swap of args.swaps) {
|
|
3200
3939
|
const ts = Number(swap.timestamp);
|
|
3201
|
-
const price =
|
|
3940
|
+
const price = priceFromUniswapV4SwapRow(swap);
|
|
3202
3941
|
if (!Number.isFinite(ts) || price == null) continue;
|
|
3203
3942
|
const bucket = bucketStartSec(Math.floor(ts), args.bucketSec);
|
|
3204
3943
|
const vol = parseDecimal(swap.amountUSD) ?? 0;
|
|
@@ -3539,6 +4278,74 @@ async function uniswapV4FetchOhlcv(args) {
|
|
|
3539
4278
|
};
|
|
3540
4279
|
}
|
|
3541
4280
|
|
|
4281
|
+
// src/protocols/evm/uniswap-v4/livePrice.ts
|
|
4282
|
+
function applyPriceQuote(price, quote) {
|
|
4283
|
+
if (!Number.isFinite(price) || price <= 0) return null;
|
|
4284
|
+
if (quote === "token1PerToken0") {
|
|
4285
|
+
const inverted = 1 / price;
|
|
4286
|
+
return Number.isFinite(inverted) && inverted > 0 ? inverted : null;
|
|
4287
|
+
}
|
|
4288
|
+
return price;
|
|
4289
|
+
}
|
|
4290
|
+
function bitqueryFilterFromPoolReference(poolReference) {
|
|
4291
|
+
const ref = poolReference.trim();
|
|
4292
|
+
if (!ref) return null;
|
|
4293
|
+
if (ref.startsWith("symbol:")) {
|
|
4294
|
+
return { kind: "symbol", symbol: ref.slice("symbol:".length).trim() };
|
|
4295
|
+
}
|
|
4296
|
+
if (ref.startsWith("0x")) {
|
|
4297
|
+
return { kind: "address", address: ref };
|
|
4298
|
+
}
|
|
4299
|
+
return null;
|
|
4300
|
+
}
|
|
4301
|
+
async function fetchUniswapV4BitqueryLatestClose(args) {
|
|
4302
|
+
const filter = bitqueryFilterFromPoolReference(args.poolReference);
|
|
4303
|
+
if (!filter) return null;
|
|
4304
|
+
const apiKey = resolveBitqueryApiKey(args.bitqueryApiKey);
|
|
4305
|
+
if (!apiKey) return null;
|
|
4306
|
+
const interval = args.interval?.trim() || "15m";
|
|
4307
|
+
const endTimeMs = Date.now();
|
|
4308
|
+
const startTimeMs = endTimeMs - 6 * 3600 * 1e3;
|
|
4309
|
+
const { candles } = await fetchRobinhoodBitqueryOhlcv({
|
|
4310
|
+
filter,
|
|
4311
|
+
interval,
|
|
4312
|
+
startTimeMs,
|
|
4313
|
+
endTimeMs,
|
|
4314
|
+
apiKey,
|
|
4315
|
+
limit: 5
|
|
4316
|
+
});
|
|
4317
|
+
if (candles.length === 0) return null;
|
|
4318
|
+
const last = candles[candles.length - 1];
|
|
4319
|
+
const close = Number(last.close);
|
|
4320
|
+
return Number.isFinite(close) && close > 0 ? close : null;
|
|
4321
|
+
}
|
|
4322
|
+
async function fetchUniswapV4ChartLivePrice(args) {
|
|
4323
|
+
const chainId = Math.floor(Number(args.chainId));
|
|
4324
|
+
if (!Number.isFinite(chainId) || chainId <= 0) return null;
|
|
4325
|
+
const poolReference = args.poolReference.trim();
|
|
4326
|
+
if (!poolReference) return null;
|
|
4327
|
+
const dataSource = String(args.dataSource ?? "").trim().toLowerCase();
|
|
4328
|
+
if (dataSource === "bitquery" || isRobinhoodOhlcvChain(chainId)) {
|
|
4329
|
+
return fetchUniswapV4BitqueryLatestClose({
|
|
4330
|
+
poolReference,
|
|
4331
|
+
interval: args.interval,
|
|
4332
|
+
bitqueryApiKey: args.bitqueryApiKey
|
|
4333
|
+
});
|
|
4334
|
+
}
|
|
4335
|
+
if (!uniswapV4SubgraphSupportedChainIds().includes(chainId)) {
|
|
4336
|
+
return null;
|
|
4337
|
+
}
|
|
4338
|
+
const row = await fetchUniswapV4LatestSwap({
|
|
4339
|
+
chainId,
|
|
4340
|
+
poolReference,
|
|
4341
|
+
theGraphApiKey: resolveTheGraphApiKey(args.theGraphApiKey)
|
|
4342
|
+
});
|
|
4343
|
+
if (!row) return null;
|
|
4344
|
+
const raw = priceFromUniswapV4SwapRow(row);
|
|
4345
|
+
if (raw == null) return null;
|
|
4346
|
+
return applyPriceQuote(raw, args.priceQuote);
|
|
4347
|
+
}
|
|
4348
|
+
|
|
3542
4349
|
// src/protocols/evm/uniswap-v4/limitOrder.ts
|
|
3543
4350
|
function trimAddr3(a) {
|
|
3544
4351
|
return a.trim();
|
|
@@ -4032,6 +4839,6 @@ var uniswapV4 = {
|
|
|
4032
4839
|
isChainSupported: isUniswapV4ChainSupported
|
|
4033
4840
|
};
|
|
4034
4841
|
|
|
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 };
|
|
4842
|
+
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_UNIVERSAL_ROUTER_VERSION_PERMISSIONED, 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_PERMIT2_APPROVE_FALLBACK, UNISWAP_V4_LP_POOL_LOOKUP_HINT, UNISWAP_V4_LP_POSITION_REGISTRY_HINT, UNISWAP_V4_LP_WETH_DEPOSIT_FALLBACK, UNISWAP_V4_OHLCV_INTERVALS, UNISWAP_V4_PERMISSION_FLAG_LIQUIDITY, UNISWAP_V4_PERMISSION_FLAG_SWAP, UNISWAP_V4_PROTOCOL_ID, UNISWAP_V4_SUBGRAPH_ID_BY_CHAIN_ID, UniswapV4PermissionDeniedError, aggregateOhlcvCandles, applySlippagePercentToApproveWei, assertUniswapLimitOrderChain, assertUniswapV4OhlcvInterval, assertUniswapV4PermissionsAllowlisted, bitqueryApiKeyVariablesHint, bitqueryAuthErrorMessage, buildEvmMultisignBodyUniswapV4CollectFeesBatch, buildEvmMultisignBodyUniswapV4DecreaseLiquidityBatch, buildEvmMultisignBodyUniswapV4IncreaseLiquidityBatch, buildEvmMultisignBodyUniswapV4LimitOrderBatch, buildEvmMultisignBodyUniswapV4LimitOrderBatchFromMcp, buildEvmMultisignBodyUniswapV4MintLiquidityBatch, buildEvmMultisignBodyUniswapV4PermissionedAllowlistFinalize, buildEvmMultisignBodyUniswapV4SkipPermit2Batch, buildLimitOrderQuoteRequestBody, buildUniswapQuoteRequestBody, buildUniswapV4KycApplyLink, buildUniswapV4PermissionedLpPoolRow, buildUniswapV4PurposePrefill, buildUniswapXLimitOrderSubmitBody, computeUniswapV4PoolReference, computeUniswapV4Session, createSwap, currencyFilterLabel, errorMessageFromUniswapJsonBody, extractPermitDataFromLimitQuote, extractUniswapLpTokenAmounts, extractUniswapLpTransaction, fetchEthereumAddressForKeyGen, fetchRobinhoodBitqueryOhlcv, fetchUniswapV4ChartLivePrice, fetchUniswapV4LatestSwap, fetchUniswapV4PoolDayDatas, fetchUniswapV4PoolHourDatas, fetchUniswapV4PoolMeta, fetchUniswapV4Swaps, fetchUniswapXLimitOrders, formatUniswapAmountFieldFromWei, formatUniswapLpAmountHuman, formatUniswapPercentForUi, formatUniswapQuoteForDisplay, formatUniswapV4PositionNotFoundError, getClassicQuoteFromStoredUniswapResponse, getUniswapUniversalRouterSpenderOrThrow, getUniswapUniversalRouterVersion, getUniswapV4PermissionedHooksOrThrow, getUniswapV4PermissionedPositionManagerOrThrow, getUniswapV4PermissionsAdapterFactoryOrThrow, getUniswapV4PositionManagerDeployBlock, getUniswapV4PositionManagerOrThrow, isBitqueryAuthOrRateLimitError, isEthGetLogsBlockRangeTooLargeError, isNativeUniswapLpTokenAddress, isRobinhoodOhlcvChain, isTheGraphRateLimitOrAuthError, isUniswapFullQuoteResponseNativeIn, isUniswapFullQuoteResponseNativeOut, isUniswapLimitOrderChainSupported, isUniswapNativeTokenAddress, isUniswapStoredQuotePermissioned, isUniswapTokenInAddressNative, isUniswapV4AnyPositionManager, isUniswapV4ChainSupported, isUniswapV4LiquidityEvmSignRequest, isUniswapV4PermissionedAllowlistEvmSignRequest, isUniswapV4PermissionedPositionManager, isUniswapV4SwapEvmSignRequest, listUniswapV4PermissionedLpPools, listUniswapV4PositionsForWallet, listUniswapV4StandardLpPools, mapBitqueryOhlcvRowsToCandles, messageFromUniswapHttpResponseBody, parseUniswapChainId, parseUniswapLpApiSnapshot, parseUniswapQuoteClassicInOut, parseUniswapQuoteSlippageInfo, parseUniswapV4ScanFromDateYmd, permitDataToEip712Payload, pickUniswapV4KycTarget, quoteSwap, readUniswapV4OnChainPermissionFlags, readUniswapV4PositionInfoRaw, requireBitqueryApiKey, resolveBitqueryApiKey, resolveBlockAtOrAfterUnixTime, resolveChainIdFromStoredUniswapQuote, resolveInputChainId, resolveRobinhoodChartCurrency, resolveRouterSwapGasUnitsFromSignRequest, resolveTheGraphApiKey, resolveUniswapV4LpExistingPoolFromKey, resolveUniswapV4LpPoolPreset, resolveUniswapV4OhlcvDataTier, resolveUniswapV4OhlcvPool, resolveUniswapV4PermissionedLpPoolPreset, resolveUniswapV4PositionManager, resolveUniswapV4PositionScanFromDate, sortUniswapV4PoolCurrencies, submitUniswapXLimitOrder, swapExactInput, swapFromQuote, swapTransactionDeadlineUnixFromExpiryMinutes, theGraphApiKeyVariablesHint, theGraphRateLimitErrorMessage, tryGetUniswapUniversalRouterV22, tryGetUniswapV4PermissionedHooks, tryGetUniswapV4PermissionedPositionManager, tryGetUniswapV4PermissionsAdapterFactory, tryGetUniswapV4PositionManager, uniswapCreateSwap, uniswapFetchLimitOrdersMcp, uniswapLimitOrderQuote, uniswapLpClaimFees, uniswapLpCreatePosition, uniswapLpDecreasePosition, uniswapLpIncreasePosition, uniswapLpTxFieldForAction, uniswapQuoteToJsonQuoteOneLine, uniswapTradeQuote, uniswapV4, uniswapV4CheckPermissions, uniswapV4CheckPermissionsMcp, uniswapV4FetchOhlcv, uniswapV4KycApplyLinkMcp, uniswapV4ListLpPools, uniswapV4ListPositionsForMcp, uniswapV4ListPositionsFromRegistryForMcp, uniswapV4ListPositionsRegistryMcpPlaceholder, uniswapV4ListStandardLpPools, uniswapV4OhlcvIntervalToBucketSec, uniswapV4OhlcvSupportedChainIds, uniswapV4PositionMintedTokenIdsFromReceipt, uniswapV4ProtocolModule, uniswapV4RegisterPositionFromMintTxPlaceholder, uniswapV4RegisterPositionNftPlaceholder, uniswapV4SubgraphIdForChain, uniswapV4SubgraphSupportedChainIds, uniswapV4SubgraphUrlForChain };
|
|
4036
4843
|
//# sourceMappingURL=index.js.map
|
|
4037
4844
|
//# sourceMappingURL=index.js.map
|