hood-alerts 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +369 -0
  3. package/dist/bot/index.cjs +1008 -0
  4. package/dist/bot/index.cjs.map +1 -0
  5. package/dist/bot/index.d.cts +241 -0
  6. package/dist/bot/index.d.ts +241 -0
  7. package/dist/bot/index.js +30 -0
  8. package/dist/bot/index.js.map +1 -0
  9. package/dist/chunk-32GS6XVE.js +732 -0
  10. package/dist/chunk-32GS6XVE.js.map +1 -0
  11. package/dist/chunk-3LHCQA4Z.js +76 -0
  12. package/dist/chunk-3LHCQA4Z.js.map +1 -0
  13. package/dist/chunk-3NHLSKZE.js +1 -0
  14. package/dist/chunk-3NHLSKZE.js.map +1 -0
  15. package/dist/chunk-6JQK5G3Z.js +72 -0
  16. package/dist/chunk-6JQK5G3Z.js.map +1 -0
  17. package/dist/chunk-CBNZ6AZT.js +598 -0
  18. package/dist/chunk-CBNZ6AZT.js.map +1 -0
  19. package/dist/chunk-CIURPPOW.js +1 -0
  20. package/dist/chunk-CIURPPOW.js.map +1 -0
  21. package/dist/chunk-IUOEGBF6.js +49 -0
  22. package/dist/chunk-IUOEGBF6.js.map +1 -0
  23. package/dist/chunk-NBMINJ5E.js +230 -0
  24. package/dist/chunk-NBMINJ5E.js.map +1 -0
  25. package/dist/chunk-O2RY2SWN.js +1 -0
  26. package/dist/chunk-O2RY2SWN.js.map +1 -0
  27. package/dist/chunk-PLMVKTQM.js +1 -0
  28. package/dist/chunk-PLMVKTQM.js.map +1 -0
  29. package/dist/chunk-RSTLW7XH.js +130 -0
  30. package/dist/chunk-RSTLW7XH.js.map +1 -0
  31. package/dist/chunk-SOBZ2FPP.js +355 -0
  32. package/dist/chunk-SOBZ2FPP.js.map +1 -0
  33. package/dist/chunk-VC5MKEY2.js +170 -0
  34. package/dist/chunk-VC5MKEY2.js.map +1 -0
  35. package/dist/chunk-W2GUAWUR.js +147 -0
  36. package/dist/chunk-W2GUAWUR.js.map +1 -0
  37. package/dist/chunk-Y2M3USWZ.js +355 -0
  38. package/dist/chunk-Y2M3USWZ.js.map +1 -0
  39. package/dist/chunk-Z4DJPESQ.js +1216 -0
  40. package/dist/chunk-Z4DJPESQ.js.map +1 -0
  41. package/dist/chunk-ZDM5VNWB.js +1 -0
  42. package/dist/chunk-ZDM5VNWB.js.map +1 -0
  43. package/dist/events/index.cjs +820 -0
  44. package/dist/events/index.cjs.map +1 -0
  45. package/dist/events/index.d.cts +58 -0
  46. package/dist/events/index.d.ts +58 -0
  47. package/dist/events/index.js +69 -0
  48. package/dist/events/index.js.map +1 -0
  49. package/dist/format-6RG4OcyE.d.ts +57 -0
  50. package/dist/format-B7d40f65.d.cts +57 -0
  51. package/dist/index.cjs +4122 -0
  52. package/dist/index.cjs.map +1 -0
  53. package/dist/index.d.cts +21 -0
  54. package/dist/index.d.ts +21 -0
  55. package/dist/index.js +274 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/logger-3phvV_fl.d.cts +185 -0
  58. package/dist/logger-CYza_0eU.d.ts +185 -0
  59. package/dist/logs-D7sYBvSy.d.cts +66 -0
  60. package/dist/logs-D7sYBvSy.d.ts +66 -0
  61. package/dist/notifiers/index.cjs +620 -0
  62. package/dist/notifiers/index.cjs.map +1 -0
  63. package/dist/notifiers/index.d.cts +186 -0
  64. package/dist/notifiers/index.d.ts +186 -0
  65. package/dist/notifiers/index.js +70 -0
  66. package/dist/notifiers/index.js.map +1 -0
  67. package/dist/pricing-BumgLpvB.d.cts +79 -0
  68. package/dist/pricing-BumgLpvB.d.ts +79 -0
  69. package/dist/ratelimit-BKVQG2oY.d.cts +42 -0
  70. package/dist/ratelimit-CGLlXNIS.d.ts +42 -0
  71. package/dist/registry-D3We8Lg6.d.ts +50 -0
  72. package/dist/registry-DePA3IyS.d.cts +50 -0
  73. package/dist/rules/index.cjs +614 -0
  74. package/dist/rules/index.cjs.map +1 -0
  75. package/dist/rules/index.d.cts +166 -0
  76. package/dist/rules/index.d.ts +166 -0
  77. package/dist/rules/index.js +60 -0
  78. package/dist/rules/index.js.map +1 -0
  79. package/dist/schema-DuDMPqpq.d.ts +183 -0
  80. package/dist/schema-NnIdKFl2.d.cts +183 -0
  81. package/dist/service/index.cjs +3774 -0
  82. package/dist/service/index.cjs.map +1 -0
  83. package/dist/service/index.d.cts +262 -0
  84. package/dist/service/index.d.ts +262 -0
  85. package/dist/service/index.js +45 -0
  86. package/dist/service/index.js.map +1 -0
  87. package/dist/service/main.cjs +3759 -0
  88. package/dist/service/main.cjs.map +1 -0
  89. package/dist/service/main.d.cts +1 -0
  90. package/dist/service/main.d.ts +1 -0
  91. package/dist/service/main.js +60 -0
  92. package/dist/service/main.js.map +1 -0
  93. package/dist/sources-CAaQR6-N.d.ts +159 -0
  94. package/dist/sources-CpDOUlzx.d.cts +159 -0
  95. package/dist/telegram-CpodYJrP.d.cts +64 -0
  96. package/dist/telegram-pvphmBwn.d.ts +64 -0
  97. package/dist/tiers/index.cjs +471 -0
  98. package/dist/tiers/index.cjs.map +1 -0
  99. package/dist/tiers/index.d.cts +231 -0
  100. package/dist/tiers/index.d.ts +231 -0
  101. package/dist/tiers/index.js +34 -0
  102. package/dist/tiers/index.js.map +1 -0
  103. package/dist/types-C0ETog04.d.ts +109 -0
  104. package/dist/types-CcoKpStA.d.cts +109 -0
  105. package/dist/types-DFPpTMWo.d.cts +186 -0
  106. package/dist/types-DFPpTMWo.d.ts +186 -0
  107. package/package.json +114 -0
@@ -0,0 +1,598 @@
1
+ import {
2
+ quoteTokens
3
+ } from "./chunk-6JQK5G3Z.js";
4
+ import {
5
+ batchAddresses,
6
+ fetchLogRange
7
+ } from "./chunk-3LHCQA4Z.js";
8
+
9
+ // src/events/explorer.ts
10
+ var MAINNET_EXPLORER = "https://robinhoodchain.blockscout.com";
11
+ var TESTNET_EXPLORER = "https://explorer.testnet.chain.robinhood.com";
12
+ function explorerBase(network) {
13
+ return network === "testnet" ? TESTNET_EXPLORER : MAINNET_EXPLORER;
14
+ }
15
+ function txUrl(base, hash) {
16
+ return `${base}/tx/${hash}`;
17
+ }
18
+ function addressUrl(base, address) {
19
+ return `${base}/address/${address}`;
20
+ }
21
+ function tokenUrl(base, token) {
22
+ return `${base}/token/${token}`;
23
+ }
24
+ function blockUrl(base, blockNumber) {
25
+ return `${base}/block/${blockNumber}`;
26
+ }
27
+
28
+ // src/events/tokens.ts
29
+ import { getAddress } from "viem";
30
+ import { erc20Abi } from "hoodchain";
31
+ function createTokenMetaReader(client, maxEntries = 5e3) {
32
+ const cache = /* @__PURE__ */ new Map();
33
+ const inflight = /* @__PURE__ */ new Map();
34
+ function remember(meta) {
35
+ if (cache.size >= maxEntries) {
36
+ const oldest = cache.keys().next();
37
+ if (!oldest.done) cache.delete(oldest.value);
38
+ }
39
+ cache.set(meta.address.toLowerCase(), meta);
40
+ }
41
+ async function load(token) {
42
+ const results = await client.public.multicall({
43
+ contracts: [
44
+ { address: token, abi: erc20Abi, functionName: "symbol" },
45
+ { address: token, abi: erc20Abi, functionName: "name" },
46
+ { address: token, abi: erc20Abi, functionName: "decimals" }
47
+ ],
48
+ allowFailure: true
49
+ });
50
+ const [symbolResult, nameResult, decimalsResult] = results;
51
+ const meta = {
52
+ address: getAddress(token),
53
+ symbol: symbolResult && symbolResult.status === "success" ? String(symbolResult.result) : null,
54
+ name: nameResult && nameResult.status === "success" ? String(nameResult.result) : null,
55
+ decimals: decimalsResult && decimalsResult.status === "success" ? Number(decimalsResult.result) : 18
56
+ };
57
+ remember(meta);
58
+ return meta;
59
+ }
60
+ return {
61
+ async get(token) {
62
+ const key = token.toLowerCase();
63
+ const hit = cache.get(key);
64
+ if (hit) return hit;
65
+ const pending = inflight.get(key);
66
+ if (pending) return pending;
67
+ const promise = load(token).catch(() => {
68
+ const fallback = { address: getAddress(token), symbol: null, name: null, decimals: 18 };
69
+ remember(fallback);
70
+ return fallback;
71
+ }).finally(() => {
72
+ inflight.delete(key);
73
+ });
74
+ inflight.set(key, promise);
75
+ return promise;
76
+ },
77
+ prime(meta) {
78
+ remember(meta);
79
+ }
80
+ };
81
+ }
82
+
83
+ // src/events/blocktime.ts
84
+ function createBlockTimeReader(client, maxEntries = 2e3) {
85
+ const cache = /* @__PURE__ */ new Map();
86
+ function remember(block, ts) {
87
+ if (cache.size >= maxEntries) {
88
+ const oldest = cache.keys().next();
89
+ if (!oldest.done) cache.delete(oldest.value);
90
+ }
91
+ cache.set(block.toString(), ts);
92
+ }
93
+ async function get(blockNumber) {
94
+ const hit = cache.get(blockNumber.toString());
95
+ if (hit !== void 0) return hit;
96
+ try {
97
+ const block = await client.public.getBlock({ blockNumber, includeTransactions: false });
98
+ const ms = Number(block.timestamp) * 1e3;
99
+ remember(blockNumber, ms);
100
+ return ms;
101
+ } catch {
102
+ return null;
103
+ }
104
+ }
105
+ return {
106
+ get,
107
+ async getMany(blocks, maxLookups) {
108
+ const unique = [...new Set(blocks.map((b) => b.toString()))].map((b) => BigInt(b));
109
+ const out = /* @__PURE__ */ new Map();
110
+ let budget = maxLookups;
111
+ for (const block of unique) {
112
+ const cached = cache.get(block.toString());
113
+ if (cached !== void 0) {
114
+ out.set(block, cached);
115
+ continue;
116
+ }
117
+ if (budget <= 0) {
118
+ out.set(block, null);
119
+ continue;
120
+ }
121
+ budget -= 1;
122
+ out.set(block, await get(block));
123
+ }
124
+ return out;
125
+ }
126
+ };
127
+ }
128
+
129
+ // src/events/liquidity.ts
130
+ import { formatUnits } from "viem";
131
+ import { USDG_DECIMALS, erc20Abi as erc20Abi2 } from "hoodchain";
132
+ function createLiquidityReader(client, oracle, registry, options = {}) {
133
+ const ttlMs = options.ttlMs ?? 6e4;
134
+ const now = options.now ?? Date.now;
135
+ const cache = /* @__PURE__ */ new Map();
136
+ const { weth, usdg } = quoteTokens(client);
137
+ async function readPool(pool, quoteToken) {
138
+ const quote = quoteToken.toLowerCase();
139
+ if (quote !== weth.toLowerCase() && quote !== usdg.toLowerCase()) return null;
140
+ const balance = await client.public.readContract({
141
+ address: quoteToken,
142
+ abi: erc20Abi2,
143
+ functionName: "balanceOf",
144
+ args: [pool]
145
+ });
146
+ if (quote === usdg.toLowerCase()) {
147
+ return Number(formatUnits(balance, USDG_DECIMALS)) * 2;
148
+ }
149
+ const usd = await oracle.weiToUsd(balance);
150
+ return usd === null ? null : usd * 2;
151
+ }
152
+ async function poolLiquidityUsd(pool, quoteToken) {
153
+ const key = pool.toLowerCase();
154
+ const hit = cache.get(key);
155
+ if (hit && now() - hit.at < ttlMs) return hit.value;
156
+ let value;
157
+ try {
158
+ value = await readPool(pool, quoteToken);
159
+ } catch {
160
+ value = null;
161
+ }
162
+ cache.set(key, { value, at: now() });
163
+ return value;
164
+ }
165
+ return {
166
+ poolLiquidityUsd,
167
+ async tokenLiquidityUsd(token) {
168
+ const pools = await registry.poolsFor(token);
169
+ if (pools.length === 0) return null;
170
+ const values = await Promise.all(
171
+ pools.map((entry) => poolLiquidityUsd(entry.pool, entry.quoteToken))
172
+ );
173
+ const known = values.filter((v) => v !== null);
174
+ return known.length === 0 ? null : Math.max(...known);
175
+ }
176
+ };
177
+ }
178
+
179
+ // src/events/sources.ts
180
+ import { getAddress as getAddress2 } from "viem";
181
+ import {
182
+ NOXA_ADDRESSES,
183
+ ODYSSEY_ADDRESSES,
184
+ noxaTokenLaunchedEvent,
185
+ odysseyTokenCreatedEvent,
186
+ odysseyTradedEvent,
187
+ odysseyPoolMigratedEvent
188
+ } from "hoodchain";
189
+ import { loadPoolInfo, uniswapV3SwapEvent } from "hoodkit";
190
+ var ODYSSEY_FACTORIES = [
191
+ ODYSSEY_ADDRESSES.bondingCurveFactory,
192
+ ODYSSEY_ADDRESSES.reflectionFactory,
193
+ ODYSSEY_ADDRESSES.instantFactory
194
+ ];
195
+ function eventId(kind, transactionHash, logIndex) {
196
+ return `${kind}:${transactionHash.toLowerCase()}:${logIndex}`;
197
+ }
198
+ function links(base, token, actor, hash, pool) {
199
+ return {
200
+ tx: txUrl(base, hash),
201
+ token: tokenUrl(base, token),
202
+ actor: addressUrl(base, actor),
203
+ ...pool ? { pool: addressUrl(base, pool) } : {}
204
+ };
205
+ }
206
+ async function valueQuote(ctx, quoteToken, raw) {
207
+ const { weth, usdg } = quoteTokens(ctx.client);
208
+ const token = quoteToken.toLowerCase();
209
+ if (token === usdg.toLowerCase()) return ctx.oracle.usdgToUsd(raw);
210
+ if (token === weth.toLowerCase()) return ctx.oracle.weiToUsd(raw);
211
+ return null;
212
+ }
213
+ function assertMainnet(client) {
214
+ if (client.network !== "mainnet") {
215
+ throw new Error(
216
+ 'hood-alerts: the NOXA and Odyssey launchpads are deployed on Robinhood Chain mainnet (chain 4663) only. Create the client with { chain: "mainnet" } (the default) to watch them.'
217
+ );
218
+ }
219
+ }
220
+ function createEventSources(ctx) {
221
+ assertMainnet(ctx.client);
222
+ return [
223
+ createNoxaLaunchSource(ctx),
224
+ createOdysseyLaunchSource(ctx),
225
+ createOdysseyCurveTradeSource(ctx),
226
+ createOdysseyGraduationSource(ctx),
227
+ createWhaleTradeSource(ctx)
228
+ ];
229
+ }
230
+ function blockTimes(ctx) {
231
+ ctx.blockTimes ??= createBlockTimeReader(ctx.client);
232
+ return ctx.blockTimes;
233
+ }
234
+ async function stampTimes(ctx, events) {
235
+ if (events.length === 0) return events;
236
+ const reader = blockTimes(ctx);
237
+ const times = await reader.getMany(
238
+ events.map((e) => e.blockNumber),
239
+ ctx.maxBlockTimeLookupsPerPoll ?? 25
240
+ );
241
+ for (const event of events) event.timestampMs = times.get(event.blockNumber) ?? null;
242
+ return events;
243
+ }
244
+ function createNoxaLaunchSource(ctx) {
245
+ const base = explorerBase(ctx.client.network);
246
+ return {
247
+ id: "noxa:launch",
248
+ label: "NOXA launches",
249
+ startBlock: NOXA_ADDRESSES.deployBlock,
250
+ async poll(from, to) {
251
+ const logs = await fetchLogRange(
252
+ (lo, hi) => ctx.client.public.getLogs({
253
+ address: NOXA_ADDRESSES.launchFactory,
254
+ event: noxaTokenLaunchedEvent,
255
+ fromBlock: lo,
256
+ toBlock: hi
257
+ }),
258
+ from,
259
+ to,
260
+ ctx.logOptions ?? {}
261
+ );
262
+ const events = [];
263
+ for (const log of logs) {
264
+ const token = log.args.token;
265
+ const deployer = log.args.deployer;
266
+ if (!token || !deployer) continue;
267
+ const pool = log.args.pool ?? null;
268
+ const pairToken = log.args.pairToken ?? null;
269
+ const initialBuyAmount = log.args.initialBuyAmount ?? 0n;
270
+ if (pool && pairToken) {
271
+ await ctx.registry.record({
272
+ pool: getAddress2(pool),
273
+ token: getAddress2(token),
274
+ quoteToken: getAddress2(pairToken),
275
+ launchpad: "noxa",
276
+ createdBlock: log.blockNumber
277
+ });
278
+ }
279
+ const meta = await ctx.tokens.get(token);
280
+ const usdValue = pairToken && initialBuyAmount > 0n ? await valueQuote(ctx, pairToken, initialBuyAmount) : null;
281
+ events.push({
282
+ id: eventId("launch", log.transactionHash, log.logIndex),
283
+ kind: "launch",
284
+ launchpad: "noxa",
285
+ token: getAddress2(token),
286
+ symbol: meta.symbol,
287
+ name: meta.name,
288
+ actor: getAddress2(deployer),
289
+ blockNumber: log.blockNumber,
290
+ logIndex: log.logIndex,
291
+ transactionHash: log.transactionHash,
292
+ timestampMs: null,
293
+ usdValue,
294
+ explorer: links(base, token, deployer, log.transactionHash, pool ?? void 0),
295
+ pool: pool ? getAddress2(pool) : null,
296
+ pairToken: pairToken ? getAddress2(pairToken) : null,
297
+ initialBuyAmount,
298
+ positionId: log.args.positionId ?? null,
299
+ // NOXA has no bonding curve at all: a launch is already a listing.
300
+ instantListing: true
301
+ });
302
+ }
303
+ return stampTimes(ctx, events);
304
+ }
305
+ };
306
+ }
307
+ function createOdysseyLaunchSource(ctx) {
308
+ const base = explorerBase(ctx.client.network);
309
+ return {
310
+ id: "odyssey:launch",
311
+ label: "Odyssey launches",
312
+ startBlock: NOXA_ADDRESSES.deployBlock,
313
+ async poll(from, to) {
314
+ const logs = await fetchLogRange(
315
+ (lo, hi) => ctx.client.public.getLogs({
316
+ address: ODYSSEY_FACTORIES,
317
+ event: odysseyTokenCreatedEvent,
318
+ fromBlock: lo,
319
+ toBlock: hi
320
+ }),
321
+ from,
322
+ to,
323
+ ctx.logOptions ?? {}
324
+ );
325
+ const events = [];
326
+ for (const log of logs) {
327
+ const token = log.args.token;
328
+ const creator = log.args.creator;
329
+ if (!token || !creator) continue;
330
+ const meta = await ctx.tokens.get(token);
331
+ events.push({
332
+ id: eventId("launch", log.transactionHash, log.logIndex),
333
+ kind: "launch",
334
+ launchpad: "odyssey",
335
+ token: getAddress2(token),
336
+ symbol: meta.symbol,
337
+ name: meta.name,
338
+ actor: getAddress2(creator),
339
+ blockNumber: log.blockNumber,
340
+ logIndex: log.logIndex,
341
+ transactionHash: log.transactionHash,
342
+ timestampMs: null,
343
+ // The curve holds liquidity until graduation, so a new Odyssey token
344
+ // has no pool and no priceable notional at creation time.
345
+ usdValue: null,
346
+ explorer: links(base, token, creator, log.transactionHash),
347
+ pool: null,
348
+ pairToken: null,
349
+ initialBuyAmount: 0n,
350
+ positionId: null,
351
+ instantListing: log.address.toLowerCase() === ODYSSEY_ADDRESSES.instantFactory.toLowerCase()
352
+ });
353
+ }
354
+ return stampTimes(ctx, events);
355
+ }
356
+ };
357
+ }
358
+ function createOdysseyCurveTradeSource(ctx) {
359
+ const base = explorerBase(ctx.client.network);
360
+ return {
361
+ id: "odyssey:curve_trade",
362
+ label: "Odyssey curve trades",
363
+ startBlock: NOXA_ADDRESSES.deployBlock,
364
+ async poll(from, to) {
365
+ const logs = await fetchLogRange(
366
+ (lo, hi) => ctx.client.public.getLogs({
367
+ address: ODYSSEY_FACTORIES,
368
+ event: odysseyTradedEvent,
369
+ fromBlock: lo,
370
+ toBlock: hi
371
+ }),
372
+ from,
373
+ to,
374
+ ctx.logOptions ?? {}
375
+ );
376
+ const events = [];
377
+ for (const log of logs) {
378
+ const token = log.args.token;
379
+ const trader = log.args.trader;
380
+ if (!token || !trader) continue;
381
+ const tokenAmount = log.args.tokenAmount ?? 0n;
382
+ const quoteAmountWei = log.args.quoteAmount ?? 0n;
383
+ const meta = await ctx.tokens.get(token);
384
+ const usdValue = await ctx.oracle.weiToUsd(quoteAmountWei);
385
+ const priceEth = tokenAmount > 0n ? Number(quoteAmountWei) / Number(tokenAmount) : null;
386
+ events.push({
387
+ id: eventId("curve_trade", log.transactionHash, log.logIndex),
388
+ kind: "curve_trade",
389
+ launchpad: "odyssey",
390
+ token: getAddress2(token),
391
+ symbol: meta.symbol,
392
+ name: meta.name,
393
+ actor: getAddress2(trader),
394
+ blockNumber: log.blockNumber,
395
+ logIndex: log.logIndex,
396
+ transactionHash: log.transactionHash,
397
+ timestampMs: null,
398
+ usdValue,
399
+ explorer: links(base, token, trader, log.transactionHash),
400
+ side: log.args.isBuy ? "buy" : "sell",
401
+ tokenAmount,
402
+ quoteAmountWei,
403
+ feeWei: log.args.fee ?? 0n,
404
+ virtualQuoteWei: log.args.virtualQuote ?? 0n,
405
+ virtualTokenAmount: log.args.virtualToken ?? 0n,
406
+ priceEth
407
+ });
408
+ }
409
+ return stampTimes(ctx, events);
410
+ }
411
+ };
412
+ }
413
+ function createOdysseyGraduationSource(ctx) {
414
+ const base = explorerBase(ctx.client.network);
415
+ return {
416
+ id: "odyssey:graduation",
417
+ label: "Odyssey graduations",
418
+ startBlock: NOXA_ADDRESSES.deployBlock,
419
+ async poll(from, to) {
420
+ const logs = await fetchLogRange(
421
+ (lo, hi) => ctx.client.public.getLogs({
422
+ address: ODYSSEY_FACTORIES,
423
+ event: odysseyPoolMigratedEvent,
424
+ fromBlock: lo,
425
+ toBlock: hi
426
+ }),
427
+ from,
428
+ to,
429
+ ctx.logOptions ?? {}
430
+ );
431
+ const events = [];
432
+ for (const log of logs) {
433
+ const token = log.args.token;
434
+ const pool = log.args.pool;
435
+ if (!token || !pool) continue;
436
+ const quoteUsed = log.args.usdcUsed ?? 0n;
437
+ let quoteToken = null;
438
+ try {
439
+ const info = await loadPoolInfo(ctx.client, pool);
440
+ quoteToken = info.token0.toLowerCase() === token.toLowerCase() ? info.token1 : info.token0;
441
+ await ctx.registry.record({
442
+ pool: getAddress2(pool),
443
+ token: getAddress2(token),
444
+ quoteToken,
445
+ launchpad: "odyssey",
446
+ createdBlock: log.blockNumber
447
+ });
448
+ } catch {
449
+ quoteToken = null;
450
+ }
451
+ const meta = await ctx.tokens.get(token);
452
+ const usdValue = quoteToken ? await valueQuote(ctx, quoteToken, quoteUsed) : null;
453
+ events.push({
454
+ id: eventId("graduation", log.transactionHash, log.logIndex),
455
+ kind: "graduation",
456
+ launchpad: "odyssey",
457
+ token: getAddress2(token),
458
+ symbol: meta.symbol,
459
+ name: meta.name,
460
+ // A migration is executed by the factory on behalf of the curve, so
461
+ // the closest thing to an actor is the factory that emitted it.
462
+ actor: getAddress2(log.address),
463
+ blockNumber: log.blockNumber,
464
+ logIndex: log.logIndex,
465
+ transactionHash: log.transactionHash,
466
+ timestampMs: null,
467
+ usdValue,
468
+ explorer: links(base, token, log.address, log.transactionHash, pool),
469
+ pool: getAddress2(pool),
470
+ positionId: log.args.tokenId ?? 0n,
471
+ liquidity: log.args.liquidity ?? 0n,
472
+ tokenUsed: log.args.tokenUsed ?? 0n,
473
+ quoteUsed
474
+ });
475
+ }
476
+ return stampTimes(ctx, events);
477
+ }
478
+ };
479
+ }
480
+ function createWhaleTradeSource(ctx) {
481
+ const base = explorerBase(ctx.client.network);
482
+ const poolInfoCache = /* @__PURE__ */ new Map();
483
+ const minUsd = ctx.whaleMinUsd ?? 1e3;
484
+ async function poolInfo(pool) {
485
+ const key = pool.toLowerCase();
486
+ const hit = poolInfoCache.get(key);
487
+ if (hit) return hit;
488
+ try {
489
+ const info = await loadPoolInfo(ctx.client, pool);
490
+ poolInfoCache.set(key, info);
491
+ return info;
492
+ } catch {
493
+ return null;
494
+ }
495
+ }
496
+ return {
497
+ id: "uniswap:whale_trade",
498
+ label: "Whale trades",
499
+ startBlock: NOXA_ADDRESSES.deployBlock,
500
+ async poll(from, to) {
501
+ const pinned = ctx.pinnedTokens ? await ctx.pinnedTokens() : [];
502
+ const entries = await ctx.registry.active(ctx.whalePoolLimit ?? 400, pinned);
503
+ if (entries.length === 0) return [];
504
+ const byPool = new Map(entries.map((entry) => [entry.pool.toLowerCase(), entry]));
505
+ const batches = batchAddresses(
506
+ entries.map((entry) => entry.pool),
507
+ ctx.whalePoolBatchSize ?? 200
508
+ );
509
+ const logs = (await Promise.all(
510
+ batches.map(
511
+ (batch) => fetchLogRange(
512
+ (lo, hi) => ctx.client.public.getLogs({
513
+ address: batch,
514
+ event: uniswapV3SwapEvent,
515
+ fromBlock: lo,
516
+ toBlock: hi
517
+ }),
518
+ from,
519
+ to,
520
+ ctx.logOptions ?? {}
521
+ )
522
+ )
523
+ )).flat();
524
+ const events = [];
525
+ for (const log of logs) {
526
+ const entry = byPool.get(log.address.toLowerCase());
527
+ if (!entry) continue;
528
+ const info = await poolInfo(entry.pool);
529
+ if (!info) continue;
530
+ const amount0 = log.args.amount0 ?? 0n;
531
+ const amount1 = log.args.amount1 ?? 0n;
532
+ const quoteIsToken0 = info.token0.toLowerCase() === entry.quoteToken.toLowerCase();
533
+ const quoteRaw = quoteIsToken0 ? amount0 : amount1;
534
+ const tokenRaw = quoteIsToken0 ? amount1 : amount0;
535
+ if (quoteRaw === 0n || tokenRaw === 0n) continue;
536
+ const usdValue = await valueQuote(ctx, entry.quoteToken, quoteRaw);
537
+ if (usdValue === null || usdValue < minUsd) continue;
538
+ const quoteDecimals = quoteIsToken0 ? info.decimals0 : info.decimals1;
539
+ const tokenDecimals = quoteIsToken0 ? info.decimals1 : info.decimals0;
540
+ const quoteAmount = Math.abs(Number(quoteRaw)) / 10 ** quoteDecimals;
541
+ const tokenAmount = Math.abs(Number(tokenRaw)) / 10 ** tokenDecimals;
542
+ const [tokenMeta, quoteMeta] = await Promise.all([
543
+ ctx.tokens.get(entry.token),
544
+ ctx.tokens.get(entry.quoteToken)
545
+ ]);
546
+ const trader = log.args.recipient ?? log.args.sender;
547
+ events.push({
548
+ id: eventId("whale_trade", log.transactionHash, log.logIndex),
549
+ kind: "whale_trade",
550
+ launchpad: entry.launchpad,
551
+ token: entry.token,
552
+ symbol: tokenMeta.symbol,
553
+ name: tokenMeta.name,
554
+ actor: getAddress2(trader),
555
+ blockNumber: log.blockNumber,
556
+ logIndex: log.logIndex,
557
+ transactionHash: log.transactionHash,
558
+ timestampMs: null,
559
+ usdValue,
560
+ explorer: links(base, entry.token, trader, log.transactionHash, entry.pool),
561
+ pool: entry.pool,
562
+ quoteToken: entry.quoteToken,
563
+ quoteSymbol: quoteMeta.symbol ?? "quote",
564
+ // The memecoin left the pool (negative delta) means the trader bought it.
565
+ side: tokenRaw < 0n ? "buy" : "sell",
566
+ tokenAmount,
567
+ quoteAmount,
568
+ price: tokenAmount > 0 ? quoteAmount / tokenAmount : 0,
569
+ feeTier: info.fee
570
+ });
571
+ }
572
+ return stampTimes(ctx, events);
573
+ }
574
+ };
575
+ }
576
+ var SUPPORTED_LAUNCHPADS = ["noxa", "odyssey"];
577
+
578
+ export {
579
+ MAINNET_EXPLORER,
580
+ TESTNET_EXPLORER,
581
+ explorerBase,
582
+ txUrl,
583
+ addressUrl,
584
+ tokenUrl,
585
+ blockUrl,
586
+ createTokenMetaReader,
587
+ createBlockTimeReader,
588
+ createLiquidityReader,
589
+ eventId,
590
+ createEventSources,
591
+ createNoxaLaunchSource,
592
+ createOdysseyLaunchSource,
593
+ createOdysseyCurveTradeSource,
594
+ createOdysseyGraduationSource,
595
+ createWhaleTradeSource,
596
+ SUPPORTED_LAUNCHPADS
597
+ };
598
+ //# sourceMappingURL=chunk-CBNZ6AZT.js.map