@net-protocol/score 0.1.7 → 0.1.9

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/react.js CHANGED
@@ -5,6 +5,8 @@ var react = require('react');
5
5
  var viem = require('viem');
6
6
  var storage = require('@net-protocol/storage');
7
7
  var core = require('@net-protocol/core');
8
+ var reactQuery = require('@tanstack/react-query');
9
+ var actions = require('viem/actions');
8
10
 
9
11
  // src/hooks/useUpvotes.ts
10
12
 
@@ -162,6 +164,372 @@ var upvote_app_default = [
162
164
  { type: "error", name: "WithdrawFailed", inputs: [] }
163
165
  ];
164
166
 
167
+ // src/abis/multi-version-uniswap-bulk-pool-finder.json
168
+ var multi_version_uniswap_bulk_pool_finder_default = [
169
+ {
170
+ type: "function",
171
+ name: "V2_FACTORY",
172
+ inputs: [],
173
+ outputs: [{ name: "", type: "address", internalType: "address" }],
174
+ stateMutability: "view"
175
+ },
176
+ {
177
+ type: "function",
178
+ name: "V3_FACTORY",
179
+ inputs: [],
180
+ outputs: [{ name: "", type: "address", internalType: "address" }],
181
+ stateMutability: "view"
182
+ },
183
+ {
184
+ type: "function",
185
+ name: "V4_POOL_MANAGER",
186
+ inputs: [],
187
+ outputs: [{ name: "", type: "address", internalType: "address" }],
188
+ stateMutability: "view"
189
+ },
190
+ {
191
+ type: "function",
192
+ name: "_checkV4PoolExternal",
193
+ inputs: [
194
+ {
195
+ name: "poolKey",
196
+ type: "tuple",
197
+ internalType: "struct PoolKey",
198
+ components: [
199
+ {
200
+ name: "currency0",
201
+ type: "address",
202
+ internalType: "Currency"
203
+ },
204
+ {
205
+ name: "currency1",
206
+ type: "address",
207
+ internalType: "Currency"
208
+ },
209
+ { name: "fee", type: "uint24", internalType: "uint24" },
210
+ { name: "tickSpacing", type: "int24", internalType: "int24" },
211
+ {
212
+ name: "hooks",
213
+ type: "address",
214
+ internalType: "contract IHooks"
215
+ }
216
+ ]
217
+ }
218
+ ],
219
+ outputs: [{ name: "exists", type: "bool", internalType: "bool" }],
220
+ stateMutability: "view"
221
+ },
222
+ {
223
+ type: "function",
224
+ name: "getPoolsMultiVersion",
225
+ inputs: [
226
+ {
227
+ name: "params",
228
+ type: "tuple",
229
+ internalType: "struct MultiVersionUniswapBulkPoolFinder.PoolFinderParams",
230
+ components: [
231
+ {
232
+ name: "tokenAs",
233
+ type: "address[]",
234
+ internalType: "address[]"
235
+ },
236
+ {
237
+ name: "tokenBs",
238
+ type: "address[]",
239
+ internalType: "address[]"
240
+ },
241
+ { name: "fees", type: "uint24[]", internalType: "uint24[]" },
242
+ {
243
+ name: "v4TickSpacings",
244
+ type: "int24[]",
245
+ internalType: "int24[]"
246
+ },
247
+ {
248
+ name: "v4Hooks",
249
+ type: "address[]",
250
+ internalType: "address[]"
251
+ }
252
+ ]
253
+ }
254
+ ],
255
+ outputs: [
256
+ {
257
+ name: "pools",
258
+ type: "tuple[]",
259
+ internalType: "struct MultiVersionUniswapBulkPoolFinder.PoolDiscovery[]",
260
+ components: [
261
+ {
262
+ name: "pairIndex",
263
+ type: "uint256",
264
+ internalType: "uint256"
265
+ },
266
+ { name: "version", type: "uint8", internalType: "uint8" },
267
+ {
268
+ name: "poolAddress",
269
+ type: "address",
270
+ internalType: "address"
271
+ },
272
+ {
273
+ name: "poolKey",
274
+ type: "tuple",
275
+ internalType: "struct PoolKey",
276
+ components: [
277
+ {
278
+ name: "currency0",
279
+ type: "address",
280
+ internalType: "Currency"
281
+ },
282
+ {
283
+ name: "currency1",
284
+ type: "address",
285
+ internalType: "Currency"
286
+ },
287
+ { name: "fee", type: "uint24", internalType: "uint24" },
288
+ {
289
+ name: "tickSpacing",
290
+ type: "int24",
291
+ internalType: "int24"
292
+ },
293
+ {
294
+ name: "hooks",
295
+ type: "address",
296
+ internalType: "contract IHooks"
297
+ }
298
+ ]
299
+ },
300
+ { name: "fee", type: "uint24", internalType: "uint24" },
301
+ { name: "tickSpacing", type: "int24", internalType: "int24" },
302
+ { name: "hooks", type: "address", internalType: "address" }
303
+ ]
304
+ },
305
+ { name: "count", type: "uint256", internalType: "uint256" }
306
+ ],
307
+ stateMutability: "view"
308
+ }
309
+ ];
310
+
311
+ // src/abis/multi-version-uniswap-pool-info-retriever.json
312
+ var multi_version_uniswap_pool_info_retriever_default = [
313
+ {
314
+ type: "function",
315
+ name: "V4_POOL_MANAGER",
316
+ inputs: [],
317
+ outputs: [
318
+ {
319
+ name: "",
320
+ type: "address",
321
+ internalType: "address"
322
+ }
323
+ ],
324
+ stateMutability: "view"
325
+ },
326
+ {
327
+ type: "function",
328
+ name: "_getV4Liquidity",
329
+ inputs: [
330
+ {
331
+ name: "poolKey",
332
+ type: "tuple",
333
+ internalType: "struct PoolKey",
334
+ components: [
335
+ {
336
+ name: "currency0",
337
+ type: "address",
338
+ internalType: "Currency"
339
+ },
340
+ {
341
+ name: "currency1",
342
+ type: "address",
343
+ internalType: "Currency"
344
+ },
345
+ {
346
+ name: "fee",
347
+ type: "uint24",
348
+ internalType: "uint24"
349
+ },
350
+ {
351
+ name: "tickSpacing",
352
+ type: "int24",
353
+ internalType: "int24"
354
+ },
355
+ {
356
+ name: "hooks",
357
+ type: "address",
358
+ internalType: "contract IHooks"
359
+ }
360
+ ]
361
+ }
362
+ ],
363
+ outputs: [
364
+ {
365
+ name: "liquidity",
366
+ type: "uint128",
367
+ internalType: "uint128"
368
+ }
369
+ ],
370
+ stateMutability: "view"
371
+ },
372
+ {
373
+ type: "function",
374
+ name: "_getV4Slot0",
375
+ inputs: [
376
+ {
377
+ name: "poolKey",
378
+ type: "tuple",
379
+ internalType: "struct PoolKey",
380
+ components: [
381
+ {
382
+ name: "currency0",
383
+ type: "address",
384
+ internalType: "Currency"
385
+ },
386
+ {
387
+ name: "currency1",
388
+ type: "address",
389
+ internalType: "Currency"
390
+ },
391
+ {
392
+ name: "fee",
393
+ type: "uint24",
394
+ internalType: "uint24"
395
+ },
396
+ {
397
+ name: "tickSpacing",
398
+ type: "int24",
399
+ internalType: "int24"
400
+ },
401
+ {
402
+ name: "hooks",
403
+ type: "address",
404
+ internalType: "contract IHooks"
405
+ }
406
+ ]
407
+ }
408
+ ],
409
+ outputs: [
410
+ {
411
+ name: "sqrtPriceX96",
412
+ type: "uint160",
413
+ internalType: "uint160"
414
+ }
415
+ ],
416
+ stateMutability: "view"
417
+ },
418
+ {
419
+ type: "function",
420
+ name: "getPoolsFullInfoMultiVersion",
421
+ inputs: [
422
+ {
423
+ name: "v2PoolAddresses",
424
+ type: "address[]",
425
+ internalType: "address[]"
426
+ },
427
+ {
428
+ name: "v3PoolAddresses",
429
+ type: "address[]",
430
+ internalType: "address[]"
431
+ },
432
+ {
433
+ name: "v4PoolKeys",
434
+ type: "tuple[]",
435
+ internalType: "struct PoolKey[]",
436
+ components: [
437
+ {
438
+ name: "currency0",
439
+ type: "address",
440
+ internalType: "Currency"
441
+ },
442
+ {
443
+ name: "currency1",
444
+ type: "address",
445
+ internalType: "Currency"
446
+ },
447
+ {
448
+ name: "fee",
449
+ type: "uint24",
450
+ internalType: "uint24"
451
+ },
452
+ {
453
+ name: "tickSpacing",
454
+ type: "int24",
455
+ internalType: "int24"
456
+ },
457
+ {
458
+ name: "hooks",
459
+ type: "address",
460
+ internalType: "contract IHooks"
461
+ }
462
+ ]
463
+ },
464
+ {
465
+ name: "baseToken",
466
+ type: "address",
467
+ internalType: "address"
468
+ }
469
+ ],
470
+ outputs: [
471
+ {
472
+ name: "",
473
+ type: "tuple[]",
474
+ internalType: "struct MultiVersionPoolInfoRetriever.PoolFullInfo[]",
475
+ components: [
476
+ {
477
+ name: "poolAddress",
478
+ type: "address",
479
+ internalType: "address"
480
+ },
481
+ {
482
+ name: "token0",
483
+ type: "address",
484
+ internalType: "address"
485
+ },
486
+ {
487
+ name: "token1",
488
+ type: "address",
489
+ internalType: "address"
490
+ },
491
+ {
492
+ name: "token0Decimals",
493
+ type: "uint8",
494
+ internalType: "uint8"
495
+ },
496
+ {
497
+ name: "token1Decimals",
498
+ type: "uint8",
499
+ internalType: "uint8"
500
+ },
501
+ {
502
+ name: "sqrtPriceX96",
503
+ type: "uint256",
504
+ internalType: "uint256"
505
+ },
506
+ {
507
+ name: "baseTokenBalance",
508
+ type: "uint256",
509
+ internalType: "uint256"
510
+ },
511
+ {
512
+ name: "token0Balance",
513
+ type: "uint256",
514
+ internalType: "uint256"
515
+ },
516
+ {
517
+ name: "token1Balance",
518
+ type: "uint256",
519
+ internalType: "uint256"
520
+ },
521
+ {
522
+ name: "liquidity",
523
+ type: "uint128",
524
+ internalType: "uint128"
525
+ }
526
+ ]
527
+ }
528
+ ],
529
+ stateMutability: "view"
530
+ }
531
+ ];
532
+
165
533
  // src/abis/user-upvote.json
166
534
  var user_upvote_default = [
167
535
  {
@@ -639,6 +1007,27 @@ var ALL_STRATEGY_ADDRESSES = [
639
1007
  PURE_ALPHA_STRATEGY.address,
640
1008
  DYNAMIC_SPLIT_STRATEGY.address
641
1009
  ];
1010
+ var MULTI_VERSION_UNISWAP_BULK_POOL_FINDER = {
1011
+ address: "0xbc237dac4c74c170780fc12f353a258bdd31a8cf",
1012
+ abi: multi_version_uniswap_bulk_pool_finder_default
1013
+ };
1014
+ var MULTI_VERSION_UNISWAP_POOL_INFO_RETRIEVER = {
1015
+ address: "0x00000002986Ca76897216a8A3A7Db10FcB0d29Cf",
1016
+ abi: multi_version_uniswap_pool_info_retriever_default
1017
+ };
1018
+ var WETH_BY_CHAIN = {
1019
+ 8453: "0x4200000000000000000000000000000000000006",
1020
+ // Base (L2 predeploy)
1021
+ 1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
1022
+ // Ethereum mainnet
1023
+ };
1024
+ function getWethAddress(chainId) {
1025
+ const addr = WETH_BY_CHAIN[chainId];
1026
+ if (!addr) {
1027
+ throw new Error(`Score: No WETH address for chain ${chainId}`);
1028
+ }
1029
+ return addr;
1030
+ }
642
1031
  var NULL_ADDRESS = "0x0000000000000000000000000000000000000000";
643
1032
  var USER_UPVOTE_CONTRACT = {
644
1033
  address: "0xa4bc2c63dd0157692fd5f409389e5032e37d8895",
@@ -875,10 +1264,365 @@ function useUpvotePrice({
875
1264
  refetch
876
1265
  };
877
1266
  }
1267
+ var V3_V4_FEE_TIERS = [500, 3e3, 1e4, 12e3, 8388608];
1268
+ var V4_TICK_SPACINGS = [200];
1269
+ var V4_HOOKS = [
1270
+ NULL_ADDRESS,
1271
+ "0xDd5EeaFf7BD481AD55Db083062b13a3cdf0A68CC",
1272
+ "0x34a45c6B61876d739400Bd71228CbcbD4F53E8cC",
1273
+ "0xb429d62f8f3bFFb98CdB9569533eA23bF0Ba28CC",
1274
+ "0xd60D6B218116cFd801E28F78d011a203D2b068Cc",
1275
+ "0x3e342a06f9592459D75721d6956B570F02eF2Dc0",
1276
+ "0xbB7784A4d481184283Ed89619A3e3ed143e1Adc0",
1277
+ "0xBDF938149ac6a781F94FAa0ed45E6A0e984c6544"
1278
+ ];
1279
+ var Q96 = BigInt(2) ** BigInt(96);
1280
+ var ZERO_BI = BigInt(0);
1281
+ function deriveV4WethDepthWei(liquidity, sqrtPriceX96, wethIsCurrency1) {
1282
+ if (sqrtPriceX96 === ZERO_BI || liquidity === ZERO_BI) return ZERO_BI;
1283
+ if (wethIsCurrency1) {
1284
+ return liquidity * sqrtPriceX96 / Q96;
1285
+ }
1286
+ return liquidity * Q96 / sqrtPriceX96;
1287
+ }
1288
+ function deriveV2V3WethDepthWei(info, wethLower) {
1289
+ if ((info.token0 ?? "").toLowerCase() === wethLower) {
1290
+ return BigInt(info.token0Balance ?? 0);
1291
+ }
1292
+ if ((info.token1 ?? "").toLowerCase() === wethLower) {
1293
+ return BigInt(info.token1Balance ?? 0);
1294
+ }
1295
+ return BigInt(info.baseTokenBalance ?? 0);
1296
+ }
1297
+ function normalizeTokenPairs(pairs, wethAddress) {
1298
+ return pairs.map((pair) => {
1299
+ const tokenA = pair.tokenAddress;
1300
+ const tokenB = pair.baseTokenAddress || wethAddress;
1301
+ return {
1302
+ originalPair: pair,
1303
+ tokenA: tokenA.toLowerCase() < tokenB.toLowerCase() ? tokenA : tokenB,
1304
+ tokenB: tokenA.toLowerCase() < tokenB.toLowerCase() ? tokenB : tokenA
1305
+ };
1306
+ });
1307
+ }
1308
+ function buildDiscoveryArgs(normalizedPairs) {
1309
+ return {
1310
+ tokenAs: normalizedPairs.map((p) => p.tokenA),
1311
+ tokenBs: normalizedPairs.map((p) => p.tokenB),
1312
+ fees: V3_V4_FEE_TIERS,
1313
+ v4TickSpacings: V4_TICK_SPACINGS,
1314
+ v4Hooks: V4_HOOKS
1315
+ };
1316
+ }
1317
+ function parsePoolDiscoveries(poolResults, pairs, wethAddress) {
1318
+ if (!poolResults) {
1319
+ return {
1320
+ v2PoolAddresses: [],
1321
+ v3PoolAddresses: [],
1322
+ v4PoolKeys: [],
1323
+ v2PoolAddressToPair: {},
1324
+ v3PoolAddressToPair: {},
1325
+ v4PoolKeyToPair: {}
1326
+ };
1327
+ }
1328
+ const [discoveries, countRaw] = poolResults;
1329
+ const countNum = typeof countRaw === "bigint" ? Number(countRaw) : Number(countRaw || 0);
1330
+ const v2PoolAddresses = [];
1331
+ const v3PoolAddresses = [];
1332
+ const v4PoolKeys = [];
1333
+ const seenV2V3Addresses = /* @__PURE__ */ new Set();
1334
+ const seenV4Keys = /* @__PURE__ */ new Set();
1335
+ const v2PoolAddressToPair = {};
1336
+ const v3PoolAddressToPair = {};
1337
+ const v4PoolKeyToPair = {};
1338
+ for (let i = 0; i < Math.min(countNum, discoveries.length); i++) {
1339
+ const discovery = discoveries[i];
1340
+ const version = Number(discovery?.version ?? 0);
1341
+ const poolAddressValue = discovery?.poolAddress;
1342
+ const pairIndex = Number(discovery?.pairIndex ?? 0);
1343
+ const feeNum = Number(discovery?.fee ?? 0);
1344
+ const poolKey = discovery?.poolKey;
1345
+ const pair = pairs[pairIndex];
1346
+ if (!pair) continue;
1347
+ if (version === 2 && poolAddressValue && poolAddressValue !== NULL_ADDRESS) {
1348
+ const poolAddress = poolAddressValue.toLowerCase();
1349
+ if (!seenV2V3Addresses.has(poolAddress)) {
1350
+ seenV2V3Addresses.add(poolAddress);
1351
+ v2PoolAddresses.push(poolAddressValue);
1352
+ v2PoolAddressToPair[poolAddress] = {
1353
+ tokenAddress: pair.tokenAddress,
1354
+ baseTokenAddress: pair.baseTokenAddress || wethAddress,
1355
+ fee: feeNum
1356
+ };
1357
+ }
1358
+ } else if (version === 3 && poolAddressValue && poolAddressValue !== NULL_ADDRESS) {
1359
+ const poolAddress = poolAddressValue.toLowerCase();
1360
+ if (!seenV2V3Addresses.has(poolAddress)) {
1361
+ seenV2V3Addresses.add(poolAddress);
1362
+ v3PoolAddresses.push(poolAddressValue);
1363
+ v3PoolAddressToPair[poolAddress] = {
1364
+ tokenAddress: pair.tokenAddress,
1365
+ baseTokenAddress: pair.baseTokenAddress || wethAddress,
1366
+ fee: feeNum
1367
+ };
1368
+ }
1369
+ } else if (version === 4 && poolKey) {
1370
+ const poolKeyString = JSON.stringify(poolKey);
1371
+ if (!seenV4Keys.has(poolKeyString)) {
1372
+ seenV4Keys.add(poolKeyString);
1373
+ v4PoolKeys.push(poolKey);
1374
+ v4PoolKeyToPair[poolKeyString] = {
1375
+ tokenAddress: pair.tokenAddress,
1376
+ baseTokenAddress: pair.baseTokenAddress || wethAddress,
1377
+ fee: feeNum
1378
+ };
1379
+ }
1380
+ }
1381
+ }
1382
+ return {
1383
+ v2PoolAddresses,
1384
+ v3PoolAddresses,
1385
+ v4PoolKeys,
1386
+ v2PoolAddressToPair,
1387
+ v3PoolAddressToPair,
1388
+ v4PoolKeyToPair
1389
+ };
1390
+ }
1391
+ function determinePoolVersion(discoveries, index) {
1392
+ const {
1393
+ v2PoolAddresses,
1394
+ v3PoolAddresses,
1395
+ v4PoolKeys,
1396
+ v2PoolAddressToPair,
1397
+ v3PoolAddressToPair,
1398
+ v4PoolKeyToPair
1399
+ } = discoveries;
1400
+ if (index < v2PoolAddresses.length) {
1401
+ const poolAddress = v2PoolAddresses[index].toLowerCase();
1402
+ return {
1403
+ version: 2,
1404
+ pair: v2PoolAddressToPair[poolAddress],
1405
+ fee: 0,
1406
+ v4PoolKey: void 0
1407
+ };
1408
+ } else if (index < v2PoolAddresses.length + v3PoolAddresses.length) {
1409
+ const v3Index = index - v2PoolAddresses.length;
1410
+ const poolAddress = v3PoolAddresses[v3Index].toLowerCase();
1411
+ return {
1412
+ version: 3,
1413
+ pair: v3PoolAddressToPair[poolAddress],
1414
+ fee: v3PoolAddressToPair[poolAddress]?.fee || 0,
1415
+ v4PoolKey: void 0
1416
+ };
1417
+ } else {
1418
+ const v4Index = index - v2PoolAddresses.length - v3PoolAddresses.length;
1419
+ const poolKey = v4PoolKeys[v4Index];
1420
+ const poolKeyString = JSON.stringify(poolKey);
1421
+ return {
1422
+ version: 4,
1423
+ pair: v4PoolKeyToPair[poolKeyString],
1424
+ fee: v4PoolKeyToPair[poolKeyString]?.fee || 0,
1425
+ v4PoolKey: poolKey
1426
+ };
1427
+ }
1428
+ }
1429
+ function calculatePoolPrice(info, pair, version) {
1430
+ try {
1431
+ const sqrtPriceX96 = Number(info.sqrtPriceX96);
1432
+ const token0Decimals = Number(info.token0Decimals);
1433
+ const token1Decimals = Number(info.token1Decimals);
1434
+ const isToken0 = info.token0.toLowerCase() === pair.tokenAddress.toLowerCase();
1435
+ if (version === 2) {
1436
+ const token0Balance = Number(info.token0Balance) / 10 ** token0Decimals;
1437
+ const token1Balance = Number(info.token1Balance) / 10 ** token1Decimals;
1438
+ if (token0Balance === 0 || token1Balance === 0) return null;
1439
+ if (isToken0) {
1440
+ return token1Balance / token0Balance;
1441
+ } else {
1442
+ return token0Balance / token1Balance;
1443
+ }
1444
+ } else {
1445
+ if (sqrtPriceX96 === 0) return null;
1446
+ if (isToken0) {
1447
+ return (sqrtPriceX96 / 2 ** 96) ** 2 * 10 ** (token0Decimals - token1Decimals);
1448
+ } else {
1449
+ const priceX96 = (sqrtPriceX96 / 2 ** 96) ** 2;
1450
+ return 1 / priceX96 * 10 ** (token1Decimals - token0Decimals);
1451
+ }
1452
+ }
1453
+ } catch {
1454
+ return null;
1455
+ }
1456
+ }
1457
+ function constructPoolKey(info, pair, version, v4PoolKey) {
1458
+ try {
1459
+ if (!info.token0 || !info.token1 || typeof pair.fee !== "number") {
1460
+ return void 0;
1461
+ }
1462
+ if (!info.token0.startsWith("0x") || !info.token1.startsWith("0x")) {
1463
+ return void 0;
1464
+ }
1465
+ const currency0 = info.token0.toLowerCase() < info.token1.toLowerCase() ? info.token0 : info.token1;
1466
+ const currency1 = info.token0.toLowerCase() < info.token1.toLowerCase() ? info.token1 : info.token0;
1467
+ if (version === 2) {
1468
+ return {
1469
+ currency0,
1470
+ currency1,
1471
+ fee: 0,
1472
+ tickSpacing: 0,
1473
+ hooks: NULL_ADDRESS
1474
+ };
1475
+ } else if (version === 3) {
1476
+ return {
1477
+ currency0,
1478
+ currency1,
1479
+ fee: pair.fee,
1480
+ tickSpacing: 0,
1481
+ hooks: NULL_ADDRESS
1482
+ };
1483
+ } else {
1484
+ if (!v4PoolKey || !v4PoolKey.currency0 || !v4PoolKey.currency1) {
1485
+ return void 0;
1486
+ }
1487
+ return v4PoolKey;
1488
+ }
1489
+ } catch {
1490
+ return void 0;
1491
+ }
1492
+ }
1493
+ function selectBestPoolPerPair(allPools) {
1494
+ const poolsByPair = {};
1495
+ for (const pool of allPools) {
1496
+ const key = pool.tokenAddress.toLowerCase() + "_" + pool.baseTokenAddress.toLowerCase();
1497
+ if (!poolsByPair[key]) poolsByPair[key] = [];
1498
+ poolsByPair[key].push(pool);
1499
+ }
1500
+ const bestPools = [];
1501
+ for (const group of Object.values(poolsByPair)) {
1502
+ const withDepth = group.filter((p) => p.wethDepthWei > ZERO_BI);
1503
+ if (withDepth.length === 0) continue;
1504
+ const best = withDepth.reduce(
1505
+ (a, b) => a.wethDepthWei > b.wethDepthWei ? a : b
1506
+ );
1507
+ bestPools.push(best);
1508
+ }
1509
+ return bestPools;
1510
+ }
1511
+ async function discoverPools({
1512
+ publicClient,
1513
+ pairs,
1514
+ chainId = 8453
1515
+ }) {
1516
+ if (pairs.length === 0) return [];
1517
+ const wethAddress = getWethAddress(chainId);
1518
+ const normalizedPairs = normalizeTokenPairs(pairs, wethAddress);
1519
+ const discoveryArgs = buildDiscoveryArgs(normalizedPairs);
1520
+ const poolResults = await actions.readContract(publicClient, {
1521
+ address: MULTI_VERSION_UNISWAP_BULK_POOL_FINDER.address,
1522
+ abi: MULTI_VERSION_UNISWAP_BULK_POOL_FINDER.abi,
1523
+ functionName: "getPoolsMultiVersion",
1524
+ args: [discoveryArgs]
1525
+ });
1526
+ const discoveries = parsePoolDiscoveries(poolResults, pairs, wethAddress);
1527
+ const totalPools = discoveries.v2PoolAddresses.length + discoveries.v3PoolAddresses.length + discoveries.v4PoolKeys.length;
1528
+ if (totalPools === 0) return [];
1529
+ const poolInfos = await actions.readContract(publicClient, {
1530
+ address: MULTI_VERSION_UNISWAP_POOL_INFO_RETRIEVER.address,
1531
+ abi: MULTI_VERSION_UNISWAP_POOL_INFO_RETRIEVER.abi,
1532
+ functionName: "getPoolsFullInfoMultiVersion",
1533
+ args: [
1534
+ discoveries.v2PoolAddresses,
1535
+ discoveries.v3PoolAddresses,
1536
+ discoveries.v4PoolKeys,
1537
+ wethAddress
1538
+ ]
1539
+ });
1540
+ if (!Array.isArray(poolInfos) || poolInfos.length === 0) return [];
1541
+ const wethLower = wethAddress.toLowerCase();
1542
+ const allPools = poolInfos.map((info, index) => {
1543
+ const poolData = determinePoolVersion(discoveries, index);
1544
+ if (!poolData || !poolData.pair) return null;
1545
+ const isV4 = poolData.version === 4;
1546
+ const liquidity = BigInt(info.liquidity ?? 0);
1547
+ let wethDepthWei;
1548
+ if (isV4) {
1549
+ const wethIsCurrency1 = poolData.v4PoolKey.currency1.toLowerCase() === wethLower;
1550
+ wethDepthWei = deriveV4WethDepthWei(
1551
+ liquidity,
1552
+ BigInt(info.sqrtPriceX96 ?? 0),
1553
+ wethIsCurrency1
1554
+ );
1555
+ } else {
1556
+ wethDepthWei = deriveV2V3WethDepthWei(info, wethLower);
1557
+ }
1558
+ return {
1559
+ tokenAddress: poolData.pair.tokenAddress,
1560
+ baseTokenAddress: poolData.pair.baseTokenAddress || wethAddress,
1561
+ poolAddress: isV4 ? null : info.poolAddress,
1562
+ price: calculatePoolPrice(info, poolData.pair, poolData.version),
1563
+ baseTokenBalance: String(info.baseTokenBalance || 0),
1564
+ token0: info.token0,
1565
+ token1: info.token1,
1566
+ token0Balance: String(info.token0Balance || 0),
1567
+ token1Balance: String(info.token1Balance || 0),
1568
+ fee: poolData.fee,
1569
+ poolKey: constructPoolKey(
1570
+ info,
1571
+ poolData.pair,
1572
+ poolData.version,
1573
+ poolData.v4PoolKey
1574
+ ),
1575
+ liquidity: String(liquidity),
1576
+ wethDepthWei
1577
+ };
1578
+ }).filter((p) => p !== null);
1579
+ const bestPools = selectBestPoolPerPair(allPools);
1580
+ return bestPools.map(
1581
+ (pool) => ({
1582
+ tokenAddress: pool.tokenAddress,
1583
+ baseTokenAddress: pool.baseTokenAddress,
1584
+ poolAddress: pool.poolAddress,
1585
+ price: pool.price,
1586
+ fee: pool.fee,
1587
+ poolKey: pool.poolKey,
1588
+ liquidity: pool.liquidity,
1589
+ balances: {
1590
+ baseTokenBalance: pool.baseTokenBalance,
1591
+ token0Balance: pool.token0Balance,
1592
+ token1Balance: pool.token1Balance
1593
+ }
1594
+ })
1595
+ );
1596
+ }
1597
+
1598
+ // src/hooks/useDiscoverPools.ts
1599
+ function useDiscoverPools({
1600
+ chainId,
1601
+ pairs,
1602
+ enabled = true
1603
+ }) {
1604
+ const publicClient = wagmi.usePublicClient({ chainId });
1605
+ const canRun = enabled && !!publicClient && pairs.length > 0;
1606
+ const keyPairs = pairs.map((p) => [
1607
+ p.tokenAddress.toLowerCase(),
1608
+ p.baseTokenAddress?.toLowerCase()
1609
+ ]);
1610
+ const { data, isLoading, error, refetch } = reactQuery.useQuery({
1611
+ queryKey: ["discoverPools", chainId, keyPairs],
1612
+ queryFn: canRun ? () => discoverPools({ publicClient, pairs, chainId }) : reactQuery.skipToken
1613
+ });
1614
+ return {
1615
+ pools: data ?? [],
1616
+ isLoading,
1617
+ error,
1618
+ refetch
1619
+ };
1620
+ }
878
1621
 
879
1622
  exports.getScoreKey = getScoreKey;
880
1623
  exports.getStorageScoreKey = getStorageScoreKey;
881
1624
  exports.getTokenScoreKey = getTokenScoreKey;
1625
+ exports.useDiscoverPools = useDiscoverPools;
882
1626
  exports.useTokenUpvotes = useTokenUpvotes;
883
1627
  exports.useUpvotePrice = useUpvotePrice;
884
1628
  exports.useUpvoteUser = useUpvoteUser;