@net-protocol/score 0.1.8 → 0.1.10
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/README.md +30 -0
- package/dist/index.d.mts +107 -28
- package/dist/index.d.ts +107 -28
- package/dist/index.js +474 -36
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +475 -39
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js +3 -1
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +3 -1
- package/dist/react.mjs.map +1 -1
- package/dist/{scoreKeyUtils-BjMpyhyX.d.mts → scoreKeyUtils-BQl5oCSc.d.mts} +1 -1
- package/dist/{scoreKeyUtils-BjMpyhyX.d.ts → scoreKeyUtils-BQl5oCSc.d.ts} +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -2484,6 +2484,44 @@ var user_upvote_default = [
|
|
|
2484
2484
|
{ type: "error", name: "ZeroUpvotes", inputs: [] }
|
|
2485
2485
|
];
|
|
2486
2486
|
|
|
2487
|
+
// src/abis/erc20-bulk-info-helper.json
|
|
2488
|
+
var erc20_bulk_info_helper_default = [
|
|
2489
|
+
{
|
|
2490
|
+
type: "function",
|
|
2491
|
+
name: "getTokenInfo",
|
|
2492
|
+
inputs: [
|
|
2493
|
+
{
|
|
2494
|
+
name: "tokenAddresses",
|
|
2495
|
+
type: "address[]",
|
|
2496
|
+
internalType: "address[]"
|
|
2497
|
+
}
|
|
2498
|
+
],
|
|
2499
|
+
outputs: [
|
|
2500
|
+
{
|
|
2501
|
+
name: "tokenInfos",
|
|
2502
|
+
type: "tuple[]",
|
|
2503
|
+
internalType: "struct ERC20BulkInfoHelper.TokenInfo[]",
|
|
2504
|
+
components: [
|
|
2505
|
+
{ name: "name", type: "string", internalType: "string" },
|
|
2506
|
+
{ name: "symbol", type: "string", internalType: "string" },
|
|
2507
|
+
{ name: "decimals", type: "uint8", internalType: "uint8" },
|
|
2508
|
+
{
|
|
2509
|
+
name: "totalSupply",
|
|
2510
|
+
type: "uint256",
|
|
2511
|
+
internalType: "uint256"
|
|
2512
|
+
},
|
|
2513
|
+
{
|
|
2514
|
+
name: "burnedTokens",
|
|
2515
|
+
type: "uint256",
|
|
2516
|
+
internalType: "uint256"
|
|
2517
|
+
}
|
|
2518
|
+
]
|
|
2519
|
+
}
|
|
2520
|
+
],
|
|
2521
|
+
stateMutability: "view"
|
|
2522
|
+
}
|
|
2523
|
+
];
|
|
2524
|
+
|
|
2487
2525
|
// src/constants.ts
|
|
2488
2526
|
var SCORE_CONTRACT = {
|
|
2489
2527
|
address: "0x0000000fa09b022e5616e5a173b4b67fa2fbcf28",
|
|
@@ -2528,8 +2566,10 @@ var MULTI_VERSION_UNISWAP_POOL_INFO_RETRIEVER = {
|
|
|
2528
2566
|
var WETH_BY_CHAIN = {
|
|
2529
2567
|
8453: "0x4200000000000000000000000000000000000006",
|
|
2530
2568
|
// Base (L2 predeploy)
|
|
2531
|
-
1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
|
|
2569
|
+
1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
2532
2570
|
// Ethereum mainnet
|
|
2571
|
+
4663: "0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73"
|
|
2572
|
+
// Robinhood Chain (non-standard WETH; not the OP predeploy)
|
|
2533
2573
|
};
|
|
2534
2574
|
function getWethAddress(chainId) {
|
|
2535
2575
|
const addr = WETH_BY_CHAIN[chainId];
|
|
@@ -2544,6 +2584,10 @@ var USER_UPVOTE_CONTRACT = {
|
|
|
2544
2584
|
address: "0xa4bc2c63dd0157692fd5f409389e5032e37d8895",
|
|
2545
2585
|
abi: user_upvote_default
|
|
2546
2586
|
};
|
|
2587
|
+
var ERC20_BULK_INFO_HELPER_CONTRACT = {
|
|
2588
|
+
address: "0x00000051809cbfacdf7d08ada813836822b880a2",
|
|
2589
|
+
abi: erc20_bulk_info_helper_default
|
|
2590
|
+
};
|
|
2547
2591
|
var encodeUpvoteKey = (tokenAddress) => {
|
|
2548
2592
|
return `0x${BigInt(`0x${tokenAddress.slice(2)}`).toString(16).padStart(64, "0")}`;
|
|
2549
2593
|
};
|
|
@@ -3209,41 +3253,6 @@ var UserUpvoteClient = class {
|
|
|
3209
3253
|
return hash;
|
|
3210
3254
|
}
|
|
3211
3255
|
};
|
|
3212
|
-
var encodePoolKey = (poolKey) => {
|
|
3213
|
-
if (!poolKey || !isValidPoolKey2(poolKey)) {
|
|
3214
|
-
return "0x";
|
|
3215
|
-
}
|
|
3216
|
-
try {
|
|
3217
|
-
return viem.encodeAbiParameters(
|
|
3218
|
-
[
|
|
3219
|
-
{
|
|
3220
|
-
type: "tuple",
|
|
3221
|
-
components: [
|
|
3222
|
-
{ name: "currency0", type: "address" },
|
|
3223
|
-
{ name: "currency1", type: "address" },
|
|
3224
|
-
{ name: "fee", type: "uint24" },
|
|
3225
|
-
{ name: "tickSpacing", type: "int24" },
|
|
3226
|
-
{ name: "hooks", type: "address" }
|
|
3227
|
-
]
|
|
3228
|
-
}
|
|
3229
|
-
],
|
|
3230
|
-
[
|
|
3231
|
-
{
|
|
3232
|
-
currency0: poolKey.currency0,
|
|
3233
|
-
currency1: poolKey.currency1,
|
|
3234
|
-
fee: poolKey.fee,
|
|
3235
|
-
tickSpacing: poolKey.tickSpacing,
|
|
3236
|
-
hooks: poolKey.hooks
|
|
3237
|
-
}
|
|
3238
|
-
]
|
|
3239
|
-
);
|
|
3240
|
-
} catch {
|
|
3241
|
-
return "0x";
|
|
3242
|
-
}
|
|
3243
|
-
};
|
|
3244
|
-
function isValidPoolKey2(poolKey) {
|
|
3245
|
-
return poolKey.fee !== void 0 || poolKey.tickSpacing !== void 0 || poolKey.currency0 !== void 0;
|
|
3246
|
-
}
|
|
3247
3256
|
var V3_V4_FEE_TIERS = [500, 3e3, 1e4, 12e3, 8388608];
|
|
3248
3257
|
var V4_TICK_SPACINGS = [200];
|
|
3249
3258
|
var V4_HOOKS = [
|
|
@@ -3597,8 +3606,436 @@ async function discoverTokenPool({
|
|
|
3597
3606
|
return results[0] ?? null;
|
|
3598
3607
|
}
|
|
3599
3608
|
|
|
3609
|
+
// src/ranking/getTokenRankings.ts
|
|
3610
|
+
var DEFAULT_MESSAGE_SCAN_WINDOW = 150;
|
|
3611
|
+
var DEFAULT_MAX_TOKENS = 50;
|
|
3612
|
+
var DEFAULT_MIN_UPVOTES = 500;
|
|
3613
|
+
var DEFAULT_MIN_MARKET_CAP = 4e4;
|
|
3614
|
+
var DEFAULT_RECENCY_HOURS = 48;
|
|
3615
|
+
var USDC_ADDRESS_BY_CHAIN = {
|
|
3616
|
+
8453: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
|
|
3617
|
+
};
|
|
3618
|
+
var ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
3619
|
+
async function getTokenRankings(options) {
|
|
3620
|
+
const {
|
|
3621
|
+
chainId,
|
|
3622
|
+
sort = "trending",
|
|
3623
|
+
maxTokens = DEFAULT_MAX_TOKENS,
|
|
3624
|
+
messageScanWindow = DEFAULT_MESSAGE_SCAN_WINDOW,
|
|
3625
|
+
rpcUrl
|
|
3626
|
+
} = options;
|
|
3627
|
+
validateChainSupported(chainId);
|
|
3628
|
+
const minUpvotes = options.thresholds?.minUpvotes ?? DEFAULT_MIN_UPVOTES;
|
|
3629
|
+
const minMarketCap = options.thresholds?.minMarketCap ?? DEFAULT_MIN_MARKET_CAP;
|
|
3630
|
+
const recencyHours = options.thresholds?.recencyHours ?? DEFAULT_RECENCY_HOURS;
|
|
3631
|
+
const nowSec = Date.now() / 1e3;
|
|
3632
|
+
const rpcOverride = rpcUrl ? { rpcUrls: Array.isArray(rpcUrl) ? rpcUrl : [rpcUrl] } : void 0;
|
|
3633
|
+
const publicClient = core.getPublicClient({ chainId, rpcUrl });
|
|
3634
|
+
const netClient = new core.NetClient({ chainId, overrides: rpcOverride });
|
|
3635
|
+
const messageGroups = await fetchUpvoteMessages({
|
|
3636
|
+
netClient,
|
|
3637
|
+
messageScanWindow
|
|
3638
|
+
});
|
|
3639
|
+
const storageBlobByKey = await fetchStrategyStorage({
|
|
3640
|
+
publicClient,
|
|
3641
|
+
messageGroups
|
|
3642
|
+
});
|
|
3643
|
+
const wethAddress = getWethAddress(chainId);
|
|
3644
|
+
const { tokenAddresses, latestUpvoteTimestamps } = aggregateAndRank({
|
|
3645
|
+
messageGroups,
|
|
3646
|
+
storageBlobByKey,
|
|
3647
|
+
sort,
|
|
3648
|
+
maxTokens,
|
|
3649
|
+
excludeAddresses: [wethAddress.toLowerCase()],
|
|
3650
|
+
nowSec
|
|
3651
|
+
});
|
|
3652
|
+
if (tokenAddresses.length === 0) return [];
|
|
3653
|
+
const usdcAddress = USDC_ADDRESS_BY_CHAIN[chainId];
|
|
3654
|
+
const pairs = [
|
|
3655
|
+
...tokenAddresses.map((tokenAddress) => ({
|
|
3656
|
+
tokenAddress,
|
|
3657
|
+
baseTokenAddress: wethAddress
|
|
3658
|
+
})),
|
|
3659
|
+
...usdcAddress ? [{ tokenAddress: wethAddress, baseTokenAddress: usdcAddress }] : []
|
|
3660
|
+
];
|
|
3661
|
+
const [tokenInfos, pools, upvoteCounts] = await Promise.all([
|
|
3662
|
+
fetchBulkErc20Info({ publicClient, addresses: tokenAddresses }),
|
|
3663
|
+
discoverPools({ publicClient, pairs, chainId }),
|
|
3664
|
+
fetchAggregateUpvotes({ publicClient, tokenAddresses })
|
|
3665
|
+
]);
|
|
3666
|
+
if (tokenInfos.length !== tokenAddresses.length) {
|
|
3667
|
+
throw new Error(
|
|
3668
|
+
`ERC20 bulk info returned ${tokenInfos.length} entries for ${tokenAddresses.length} addresses`
|
|
3669
|
+
);
|
|
3670
|
+
}
|
|
3671
|
+
if (upvoteCounts.length !== tokenAddresses.length) {
|
|
3672
|
+
throw new Error(
|
|
3673
|
+
`getUpvotesWithLegacy returned ${upvoteCounts.length} entries for ${tokenAddresses.length} addresses`
|
|
3674
|
+
);
|
|
3675
|
+
}
|
|
3676
|
+
return composeAndFilter({
|
|
3677
|
+
tokenAddresses,
|
|
3678
|
+
tokenInfos,
|
|
3679
|
+
pools,
|
|
3680
|
+
upvoteCounts,
|
|
3681
|
+
latestUpvoteTimestamps,
|
|
3682
|
+
wethAddress,
|
|
3683
|
+
usdcAddress,
|
|
3684
|
+
sort,
|
|
3685
|
+
maxTokens,
|
|
3686
|
+
minUpvotes,
|
|
3687
|
+
minMarketCap,
|
|
3688
|
+
recencyHours,
|
|
3689
|
+
nowSec
|
|
3690
|
+
});
|
|
3691
|
+
}
|
|
3692
|
+
function validateChainSupported(chainId) {
|
|
3693
|
+
if (!SUPPORTED_SCORE_CHAINS.includes(chainId)) {
|
|
3694
|
+
throw new Error(
|
|
3695
|
+
`getTokenRankings: chainId ${chainId} is not supported. Supported chains: ${SUPPORTED_SCORE_CHAINS.join(", ")}`
|
|
3696
|
+
);
|
|
3697
|
+
}
|
|
3698
|
+
}
|
|
3699
|
+
async function fetchUpvoteMessages({
|
|
3700
|
+
netClient,
|
|
3701
|
+
messageScanWindow
|
|
3702
|
+
}) {
|
|
3703
|
+
const legacyFilter = {
|
|
3704
|
+
appAddress: LEGACY_UPVOTE_V1_ADDRESS,
|
|
3705
|
+
topic: "t"
|
|
3706
|
+
};
|
|
3707
|
+
const strategyFilters = [
|
|
3708
|
+
UNIV234_POOLS_STRATEGY.address,
|
|
3709
|
+
PURE_ALPHA_STRATEGY.address,
|
|
3710
|
+
DYNAMIC_SPLIT_STRATEGY.address
|
|
3711
|
+
].map((stratAddr) => ({
|
|
3712
|
+
appAddress: SCORE_CONTRACT.address,
|
|
3713
|
+
topic: `t${stratAddr.toLowerCase()}`
|
|
3714
|
+
}));
|
|
3715
|
+
const [legacyCount, ...strategyCounts] = await Promise.all([
|
|
3716
|
+
netClient.getMessageCount({ filter: legacyFilter }),
|
|
3717
|
+
...strategyFilters.map((filter) => netClient.getMessageCount({ filter }))
|
|
3718
|
+
]);
|
|
3719
|
+
const sliceRange = (count) => ({
|
|
3720
|
+
startIndex: Math.max(0, count - messageScanWindow),
|
|
3721
|
+
endIndex: count
|
|
3722
|
+
});
|
|
3723
|
+
const [legacy, strategy1, strategy2, strategy3] = await Promise.all([
|
|
3724
|
+
legacyCount > 0 ? netClient.getMessagesBatch({
|
|
3725
|
+
filter: legacyFilter,
|
|
3726
|
+
...sliceRange(legacyCount)
|
|
3727
|
+
}) : Promise.resolve([]),
|
|
3728
|
+
...strategyFilters.map(
|
|
3729
|
+
(filter, i) => strategyCounts[i] > 0 ? netClient.getMessagesBatch({
|
|
3730
|
+
filter,
|
|
3731
|
+
...sliceRange(strategyCounts[i])
|
|
3732
|
+
}) : Promise.resolve([])
|
|
3733
|
+
)
|
|
3734
|
+
]);
|
|
3735
|
+
return { legacy, strategy1, strategy2, strategy3 };
|
|
3736
|
+
}
|
|
3737
|
+
async function fetchStrategyStorage({
|
|
3738
|
+
publicClient,
|
|
3739
|
+
messageGroups
|
|
3740
|
+
}) {
|
|
3741
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3742
|
+
const keys = [];
|
|
3743
|
+
for (const msg of [
|
|
3744
|
+
...messageGroups.strategy1,
|
|
3745
|
+
...messageGroups.strategy2,
|
|
3746
|
+
...messageGroups.strategy3
|
|
3747
|
+
]) {
|
|
3748
|
+
if (!msg.data || !msg.data.startsWith("0x")) continue;
|
|
3749
|
+
const key = msg.data;
|
|
3750
|
+
if (seen.has(key)) continue;
|
|
3751
|
+
seen.add(key);
|
|
3752
|
+
keys.push({ key, operator: SCORE_CONTRACT.address });
|
|
3753
|
+
}
|
|
3754
|
+
if (keys.length === 0) return /* @__PURE__ */ new Map();
|
|
3755
|
+
const results = await actions.readContract(publicClient, {
|
|
3756
|
+
address: storage.STORAGE_CONTRACT.address,
|
|
3757
|
+
abi: storage.STORAGE_CONTRACT.abi,
|
|
3758
|
+
functionName: "bulkGet",
|
|
3759
|
+
args: [keys]
|
|
3760
|
+
});
|
|
3761
|
+
return buildStorageMapFromBulkGetResults(results, keys);
|
|
3762
|
+
}
|
|
3763
|
+
function buildStorageMapFromBulkGetResults(results, keys) {
|
|
3764
|
+
if (results.length !== keys.length) {
|
|
3765
|
+
throw new Error(
|
|
3766
|
+
`bulkGet returned ${results.length} entries for ${keys.length} keys`
|
|
3767
|
+
);
|
|
3768
|
+
}
|
|
3769
|
+
const map = /* @__PURE__ */ new Map();
|
|
3770
|
+
results.forEach((item, idx) => {
|
|
3771
|
+
const value = item?.value;
|
|
3772
|
+
if (value && value !== "0x") {
|
|
3773
|
+
map.set(keys[idx].key, value);
|
|
3774
|
+
}
|
|
3775
|
+
});
|
|
3776
|
+
return map;
|
|
3777
|
+
}
|
|
3778
|
+
function aggregateAndRank({
|
|
3779
|
+
messageGroups,
|
|
3780
|
+
storageBlobByKey,
|
|
3781
|
+
sort,
|
|
3782
|
+
maxTokens,
|
|
3783
|
+
excludeAddresses = [],
|
|
3784
|
+
nowSec
|
|
3785
|
+
}) {
|
|
3786
|
+
const tokenUpvoteEvents = /* @__PURE__ */ new Map();
|
|
3787
|
+
const excludeSet = new Set(excludeAddresses.map((a) => a.toLowerCase()));
|
|
3788
|
+
const addEvent = (tokenAddress, timestamp, count) => {
|
|
3789
|
+
if (!ADDRESS_RE.test(tokenAddress)) return;
|
|
3790
|
+
const lower = tokenAddress.toLowerCase();
|
|
3791
|
+
if (lower === NULL_ADDRESS || excludeSet.has(lower)) return;
|
|
3792
|
+
if (!Number.isFinite(timestamp) || !Number.isFinite(count)) return;
|
|
3793
|
+
const events = tokenUpvoteEvents.get(lower) ?? [];
|
|
3794
|
+
events.push({ timestamp, count });
|
|
3795
|
+
tokenUpvoteEvents.set(lower, events);
|
|
3796
|
+
};
|
|
3797
|
+
for (const msg of messageGroups.legacy) {
|
|
3798
|
+
if (msg.topic !== "t" || !msg.text) continue;
|
|
3799
|
+
let count = 1;
|
|
3800
|
+
if (msg.data && msg.data.startsWith("0x")) {
|
|
3801
|
+
try {
|
|
3802
|
+
const [votes] = viem.decodeAbiParameters(
|
|
3803
|
+
[{ type: "uint256" }],
|
|
3804
|
+
msg.data
|
|
3805
|
+
);
|
|
3806
|
+
count = Number(votes);
|
|
3807
|
+
} catch {
|
|
3808
|
+
count = 1;
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3811
|
+
addEvent(msg.text, Number(msg.timestamp), count);
|
|
3812
|
+
}
|
|
3813
|
+
for (const msg of [
|
|
3814
|
+
...messageGroups.strategy1,
|
|
3815
|
+
...messageGroups.strategy2,
|
|
3816
|
+
...messageGroups.strategy3
|
|
3817
|
+
]) {
|
|
3818
|
+
if (!msg.topic.startsWith("t") || !msg.data || !msg.data.startsWith("0x")) {
|
|
3819
|
+
continue;
|
|
3820
|
+
}
|
|
3821
|
+
const blob = storageBlobByKey.get(msg.data);
|
|
3822
|
+
if (!blob) continue;
|
|
3823
|
+
const decoded = decodeUpvoteStorageBlob(blob);
|
|
3824
|
+
if (!decoded || !decoded.scoreKey) continue;
|
|
3825
|
+
const tokenAddress = extractTokenAddressFromScoreKey(decoded.scoreKey);
|
|
3826
|
+
if (!tokenAddress) continue;
|
|
3827
|
+
addEvent(tokenAddress, Number(msg.timestamp), decoded.scoreDelta);
|
|
3828
|
+
}
|
|
3829
|
+
const timeWeight = (ts) => Math.exp(-0.1 * ((nowSec - ts) / 3600));
|
|
3830
|
+
const latestUpvoteTimestamps = /* @__PURE__ */ new Map();
|
|
3831
|
+
for (const [addr, events] of tokenUpvoteEvents) {
|
|
3832
|
+
let latest = events[0].timestamp;
|
|
3833
|
+
for (const e of events) if (e.timestamp > latest) latest = e.timestamp;
|
|
3834
|
+
latestUpvoteTimestamps.set(addr, latest);
|
|
3835
|
+
}
|
|
3836
|
+
let ordered;
|
|
3837
|
+
if (sort === "top") {
|
|
3838
|
+
ordered = Array.from(tokenUpvoteEvents.keys());
|
|
3839
|
+
} else {
|
|
3840
|
+
const scores = /* @__PURE__ */ new Map();
|
|
3841
|
+
for (const [addr, events] of tokenUpvoteEvents) {
|
|
3842
|
+
if (sort === "recent") {
|
|
3843
|
+
scores.set(addr, latestUpvoteTimestamps.get(addr) ?? 0);
|
|
3844
|
+
} else {
|
|
3845
|
+
const score = events.reduce(
|
|
3846
|
+
(total, e) => total + e.count * timeWeight(e.timestamp),
|
|
3847
|
+
0
|
|
3848
|
+
);
|
|
3849
|
+
scores.set(addr, score);
|
|
3850
|
+
}
|
|
3851
|
+
}
|
|
3852
|
+
ordered = Array.from(scores.entries()).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([address]) => address);
|
|
3853
|
+
}
|
|
3854
|
+
const fetchLimit = Math.max(maxTokens * 3, 30);
|
|
3855
|
+
return {
|
|
3856
|
+
tokenAddresses: ordered.slice(0, fetchLimit),
|
|
3857
|
+
latestUpvoteTimestamps
|
|
3858
|
+
};
|
|
3859
|
+
}
|
|
3860
|
+
async function fetchBulkErc20Info({
|
|
3861
|
+
publicClient,
|
|
3862
|
+
addresses
|
|
3863
|
+
}) {
|
|
3864
|
+
if (addresses.length === 0) return [];
|
|
3865
|
+
try {
|
|
3866
|
+
const data = await actions.readContract(publicClient, {
|
|
3867
|
+
address: ERC20_BULK_INFO_HELPER_CONTRACT.address,
|
|
3868
|
+
abi: ERC20_BULK_INFO_HELPER_CONTRACT.abi,
|
|
3869
|
+
functionName: "getTokenInfo",
|
|
3870
|
+
args: [addresses]
|
|
3871
|
+
});
|
|
3872
|
+
if (Array.isArray(data) && data.length === addresses.length) return data;
|
|
3873
|
+
} catch {
|
|
3874
|
+
}
|
|
3875
|
+
const results = await Promise.all(
|
|
3876
|
+
addresses.map(async (addr) => {
|
|
3877
|
+
try {
|
|
3878
|
+
const data = await actions.readContract(publicClient, {
|
|
3879
|
+
address: ERC20_BULK_INFO_HELPER_CONTRACT.address,
|
|
3880
|
+
abi: ERC20_BULK_INFO_HELPER_CONTRACT.abi,
|
|
3881
|
+
functionName: "getTokenInfo",
|
|
3882
|
+
args: [[addr]]
|
|
3883
|
+
});
|
|
3884
|
+
if (Array.isArray(data) && data[0]) return data[0];
|
|
3885
|
+
} catch {
|
|
3886
|
+
}
|
|
3887
|
+
return {
|
|
3888
|
+
name: "",
|
|
3889
|
+
symbol: "",
|
|
3890
|
+
decimals: 0,
|
|
3891
|
+
totalSupply: 0n,
|
|
3892
|
+
burnedTokens: 0n
|
|
3893
|
+
};
|
|
3894
|
+
})
|
|
3895
|
+
);
|
|
3896
|
+
return results;
|
|
3897
|
+
}
|
|
3898
|
+
async function fetchAggregateUpvotes({
|
|
3899
|
+
publicClient,
|
|
3900
|
+
tokenAddresses
|
|
3901
|
+
}) {
|
|
3902
|
+
if (tokenAddresses.length === 0) return [];
|
|
3903
|
+
const data = await actions.readContract(publicClient, {
|
|
3904
|
+
address: UPVOTE_APP.address,
|
|
3905
|
+
abi: UPVOTE_APP.abi,
|
|
3906
|
+
functionName: "getUpvotesWithLegacy",
|
|
3907
|
+
args: [
|
|
3908
|
+
tokenAddresses.map((addr) => encodeUpvoteKey(addr)),
|
|
3909
|
+
ALL_STRATEGY_ADDRESSES
|
|
3910
|
+
]
|
|
3911
|
+
});
|
|
3912
|
+
if (!Array.isArray(data)) {
|
|
3913
|
+
throw new Error(
|
|
3914
|
+
"getUpvotesWithLegacy returned non-array; contract or RPC misbehaving"
|
|
3915
|
+
);
|
|
3916
|
+
}
|
|
3917
|
+
return data.map(Number);
|
|
3918
|
+
}
|
|
3919
|
+
function composeAndFilter({
|
|
3920
|
+
tokenAddresses,
|
|
3921
|
+
tokenInfos,
|
|
3922
|
+
pools,
|
|
3923
|
+
upvoteCounts,
|
|
3924
|
+
latestUpvoteTimestamps,
|
|
3925
|
+
wethAddress,
|
|
3926
|
+
usdcAddress,
|
|
3927
|
+
sort,
|
|
3928
|
+
maxTokens,
|
|
3929
|
+
minUpvotes,
|
|
3930
|
+
minMarketCap,
|
|
3931
|
+
recencyHours,
|
|
3932
|
+
nowSec
|
|
3933
|
+
}) {
|
|
3934
|
+
const wethLower = wethAddress.toLowerCase();
|
|
3935
|
+
const usdcLower = usdcAddress?.toLowerCase();
|
|
3936
|
+
let ethPriceInUsdc;
|
|
3937
|
+
if (usdcLower) {
|
|
3938
|
+
const wethUsdcPool = pools.find(
|
|
3939
|
+
(p) => p.tokenAddress.toLowerCase() === wethLower && p.baseTokenAddress.toLowerCase() === usdcLower
|
|
3940
|
+
);
|
|
3941
|
+
const usdcWethPool = pools.find(
|
|
3942
|
+
(p) => p.tokenAddress.toLowerCase() === usdcLower && p.baseTokenAddress.toLowerCase() === wethLower
|
|
3943
|
+
);
|
|
3944
|
+
if (wethUsdcPool?.price) {
|
|
3945
|
+
ethPriceInUsdc = wethUsdcPool.price;
|
|
3946
|
+
} else if (usdcWethPool?.price) {
|
|
3947
|
+
ethPriceInUsdc = 1 / usdcWethPool.price;
|
|
3948
|
+
}
|
|
3949
|
+
}
|
|
3950
|
+
const tokenAddressToPool = /* @__PURE__ */ new Map();
|
|
3951
|
+
for (const pool of pools) {
|
|
3952
|
+
if (pool.baseTokenAddress.toLowerCase() === wethLower) {
|
|
3953
|
+
tokenAddressToPool.set(pool.tokenAddress.toLowerCase(), pool);
|
|
3954
|
+
}
|
|
3955
|
+
}
|
|
3956
|
+
const composed = tokenAddresses.map((address, i) => {
|
|
3957
|
+
const info = tokenInfos[i];
|
|
3958
|
+
const pool = tokenAddressToPool.get(address.toLowerCase());
|
|
3959
|
+
const priceInUsdc = pool?.price && ethPriceInUsdc ? pool.price * ethPriceInUsdc : void 0;
|
|
3960
|
+
let circulating;
|
|
3961
|
+
if (info?.totalSupply) {
|
|
3962
|
+
const burned = info.burnedTokens ?? 0n;
|
|
3963
|
+
const c = info.totalSupply - burned;
|
|
3964
|
+
circulating = c > 0n ? c : 0n;
|
|
3965
|
+
}
|
|
3966
|
+
const decimals = info?.decimals || void 0;
|
|
3967
|
+
const fdv = circulating != null && decimals != null && priceInUsdc != null ? Number(circulating) / 10 ** decimals * priceInUsdc : void 0;
|
|
3968
|
+
return {
|
|
3969
|
+
address,
|
|
3970
|
+
name: info?.name || void 0,
|
|
3971
|
+
symbol: info?.symbol || void 0,
|
|
3972
|
+
decimals,
|
|
3973
|
+
fdv,
|
|
3974
|
+
priceInUsdc,
|
|
3975
|
+
upvotes: upvoteCounts[i] ?? 0,
|
|
3976
|
+
latestUpvoteTimestamp: latestUpvoteTimestamps.get(address.toLowerCase()) ?? 0
|
|
3977
|
+
};
|
|
3978
|
+
});
|
|
3979
|
+
const valid = composed.filter((t) => {
|
|
3980
|
+
if (!t.name?.trim() || !t.symbol?.trim()) return false;
|
|
3981
|
+
const belowFloor = t.fdv == null || t.fdv < minMarketCap;
|
|
3982
|
+
if (belowFloor) {
|
|
3983
|
+
const hoursSince = (nowSec - t.latestUpvoteTimestamp) / 3600;
|
|
3984
|
+
if (hoursSince > recencyHours) return false;
|
|
3985
|
+
}
|
|
3986
|
+
return true;
|
|
3987
|
+
});
|
|
3988
|
+
let sorted;
|
|
3989
|
+
if (sort === "top") {
|
|
3990
|
+
sorted = [...valid].sort((a, b) => b.upvotes - a.upvotes);
|
|
3991
|
+
} else {
|
|
3992
|
+
const byAddr = new Map(valid.map((t) => [t.address.toLowerCase(), t]));
|
|
3993
|
+
sorted = tokenAddresses.map((addr) => byAddr.get(addr.toLowerCase())).filter((t) => t != null);
|
|
3994
|
+
}
|
|
3995
|
+
const qualifies = (t) => t.upvotes >= minUpvotes || t.fdv != null && t.fdv >= minMarketCap;
|
|
3996
|
+
const top = sorted.filter(qualifies);
|
|
3997
|
+
const rest = sorted.filter((t) => !qualifies(t));
|
|
3998
|
+
return [...top, ...rest].slice(0, maxTokens);
|
|
3999
|
+
}
|
|
4000
|
+
var encodePoolKey = (poolKey) => {
|
|
4001
|
+
if (!poolKey || !isValidPoolKey2(poolKey)) {
|
|
4002
|
+
return "0x";
|
|
4003
|
+
}
|
|
4004
|
+
try {
|
|
4005
|
+
return viem.encodeAbiParameters(
|
|
4006
|
+
[
|
|
4007
|
+
{
|
|
4008
|
+
type: "tuple",
|
|
4009
|
+
components: [
|
|
4010
|
+
{ name: "currency0", type: "address" },
|
|
4011
|
+
{ name: "currency1", type: "address" },
|
|
4012
|
+
{ name: "fee", type: "uint24" },
|
|
4013
|
+
{ name: "tickSpacing", type: "int24" },
|
|
4014
|
+
{ name: "hooks", type: "address" }
|
|
4015
|
+
]
|
|
4016
|
+
}
|
|
4017
|
+
],
|
|
4018
|
+
[
|
|
4019
|
+
{
|
|
4020
|
+
currency0: poolKey.currency0,
|
|
4021
|
+
currency1: poolKey.currency1,
|
|
4022
|
+
fee: poolKey.fee,
|
|
4023
|
+
tickSpacing: poolKey.tickSpacing,
|
|
4024
|
+
hooks: poolKey.hooks
|
|
4025
|
+
}
|
|
4026
|
+
]
|
|
4027
|
+
);
|
|
4028
|
+
} catch {
|
|
4029
|
+
return "0x";
|
|
4030
|
+
}
|
|
4031
|
+
};
|
|
4032
|
+
function isValidPoolKey2(poolKey) {
|
|
4033
|
+
return poolKey.fee !== void 0 || poolKey.tickSpacing !== void 0 || poolKey.currency0 !== void 0;
|
|
4034
|
+
}
|
|
4035
|
+
|
|
3600
4036
|
exports.ALL_STRATEGY_ADDRESSES = ALL_STRATEGY_ADDRESSES;
|
|
3601
4037
|
exports.DYNAMIC_SPLIT_STRATEGY = DYNAMIC_SPLIT_STRATEGY;
|
|
4038
|
+
exports.ERC20_BULK_INFO_HELPER_CONTRACT = ERC20_BULK_INFO_HELPER_CONTRACT;
|
|
3602
4039
|
exports.LEGACY_UPVOTE_V1_ADDRESS = LEGACY_UPVOTE_V1_ADDRESS;
|
|
3603
4040
|
exports.LEGACY_UPVOTE_V2_ADDRESS = LEGACY_UPVOTE_V2_ADDRESS;
|
|
3604
4041
|
exports.MULTI_VERSION_UNISWAP_BULK_POOL_FINDER = MULTI_VERSION_UNISWAP_BULK_POOL_FINDER;
|
|
@@ -3634,6 +4071,7 @@ exports.getFeedContentKey = getFeedContentKey;
|
|
|
3634
4071
|
exports.getScoreKey = getScoreKey;
|
|
3635
4072
|
exports.getStorageScoreKey = getStorageScoreKey;
|
|
3636
4073
|
exports.getStorageUpvoteContext = getStorageUpvoteContext;
|
|
4074
|
+
exports.getTokenRankings = getTokenRankings;
|
|
3637
4075
|
exports.getTokenScoreKey = getTokenScoreKey;
|
|
3638
4076
|
exports.getWethAddress = getWethAddress;
|
|
3639
4077
|
exports.isDynamicSplitStrategy = isDynamicSplitStrategy;
|