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,79 @@
1
+ import { Address } from 'viem';
2
+ import { HoodClient } from 'hoodchain';
3
+
4
+ /**
5
+ * USD valuation for alert events.
6
+ *
7
+ * There is no hardcoded price anywhere in this package. Every dollar figure
8
+ * comes from live chain state:
9
+ *
10
+ * - **USDG legs are the unit of account.** USDG is Paxos' Global Dollar, a
11
+ * fully reserved 1:1 USD stablecoin with 6 decimals. A USDG leg of a trade
12
+ * is its own USD value, no oracle needed.
13
+ * - **ETH legs are priced through the chain's own Uniswap v3 liquidity.** The
14
+ * oracle asks `hoodchain`'s `quoteSwap` for the real output of selling 1
15
+ * WETH into USDG across every fee tier and two-hop route, and takes the best
16
+ * route's output as the ETH/USD rate. That is the price a trader would
17
+ * actually get, sourced from the same pools the alerts watch.
18
+ * - **Anything else is `null`.** A memecoin/memecoin pool with no USDG or WETH
19
+ * leg has no honest USD value from on-chain data alone, so the event carries
20
+ * `usdValue: null` and min-USD rules simply do not match it.
21
+ *
22
+ * Rates are cached for a short TTL so a burst of events in one block does not
23
+ * re-quote the router once per event.
24
+ */
25
+ /** A source of USD rates for the two priceable legs. */
26
+ interface PriceOracle {
27
+ /**
28
+ * USD per 1 ETH, or `null` when no WETH/USDG route has liquidity right now.
29
+ * Never throws: a quote failure degrades to `null`, which downgrades USD
30
+ * filters rather than dropping the alert pipeline.
31
+ */
32
+ ethUsd(): Promise<number | null>;
33
+ /** Convert a raw wei amount to USD, or `null` when the rate is unavailable. */
34
+ weiToUsd(wei: bigint): Promise<number | null>;
35
+ /** Convert a raw USDG amount (6 decimals) to USD. Always available. */
36
+ usdgToUsd(atomic: bigint): number;
37
+ }
38
+ /** Options for {@link createPriceOracle}. */
39
+ interface PriceOracleOptions {
40
+ /**
41
+ * How long a quoted ETH/USD rate stays fresh, in milliseconds.
42
+ * @defaultValue `30_000`
43
+ */
44
+ ttlMs?: number;
45
+ /**
46
+ * Probe size used to quote WETH into USDG. Larger sizes are less sensitive
47
+ * to a thin tick but need deeper liquidity to fill.
48
+ * @defaultValue `1` ETH
49
+ */
50
+ probeWei?: bigint;
51
+ /** Clock injection point for tests. @defaultValue `Date.now` */
52
+ now?: () => number;
53
+ }
54
+ /** WETH and USDG for the client's network. */
55
+ declare function quoteTokens(client: HoodClient): {
56
+ weth: Address;
57
+ usdg: Address;
58
+ };
59
+ /**
60
+ * Build a caching ETH/USD oracle backed by real Uniswap v3 quotes.
61
+ *
62
+ * @example
63
+ * ```ts
64
+ * import { createHoodClient } from 'hoodchain'
65
+ * import { createPriceOracle } from 'hood-alerts/events'
66
+ *
67
+ * const hood = createHoodClient()
68
+ * const oracle = createPriceOracle(hood)
69
+ * console.log('ETH/USD from chain liquidity:', await oracle.ethUsd())
70
+ * ```
71
+ */
72
+ declare function createPriceOracle(client: HoodClient, options?: PriceOracleOptions): PriceOracle;
73
+ /**
74
+ * A fixed-rate oracle. Useful when an operator already has a trusted ETH/USD
75
+ * feed and wants alerts to agree with it, and for deterministic tests.
76
+ */
77
+ declare function createStaticPriceOracle(ethUsdRate: number | null): PriceOracle;
78
+
79
+ export { type PriceOracle as P, type PriceOracleOptions as a, createStaticPriceOracle as b, createPriceOracle as c, quoteTokens as q };
@@ -0,0 +1,42 @@
1
+ import { R as RateLimitConfig } from './schema-NnIdKFl2.cjs';
2
+
3
+ /**
4
+ * Rate limiting is a *storage* question, not an in-memory counter question:
5
+ * a service that resets its limits on every deploy is not rate limited. The
6
+ * engine talks to this interface, the service backs it with the same SQLite
7
+ * file that holds the delivery log, and tests back it with a map.
8
+ */
9
+ interface RateLimitStore {
10
+ /** How many deliveries were recorded for `key` at or after `sinceMs`. */
11
+ countSince(key: string, sinceMs: number): Promise<number>;
12
+ /** When `key` last delivered, or `null` if never. */
13
+ lastAt(key: string): Promise<number | null>;
14
+ /**
15
+ * Record a delivery against both its rule bucket and its subscriber. The
16
+ * subscriber attribution is what the tier throughput cap counts, so the two
17
+ * are written together and can never drift apart.
18
+ */
19
+ record(key: string, subscriberId: string, atMs: number): Promise<void>;
20
+ /** How many alerts a subscriber received since `sinceMs`, for tier caps. */
21
+ countForSubscriberSince(subscriberId: string, sinceMs: number): Promise<number>;
22
+ }
23
+ /** Why a rate limit rejected a delivery, or `null` when it allowed it. */
24
+ interface RateLimitDecision {
25
+ allowed: boolean;
26
+ /** Human-readable reason, safe to log and to show in `/status`. */
27
+ reason: string | null;
28
+ /** When the next delivery would be allowed, ms since epoch, if known. */
29
+ retryAtMs: number | null;
30
+ }
31
+ /**
32
+ * Check a rule's rate limit without consuming it. Consumption happens through
33
+ * {@link RateLimitStore.record} after a delivery attempt, so a rule that
34
+ * matches but fails entitlement checks does not burn quota.
35
+ */
36
+ declare function checkRateLimit(store: RateLimitStore, key: string, config: RateLimitConfig | undefined, nowMs: number): Promise<RateLimitDecision>;
37
+ /** An in-memory {@link RateLimitStore}. Correct, but resets with the process. */
38
+ declare function createMemoryRateLimitStore(): RateLimitStore;
39
+ /** Canonical rate-limit key: one bucket per rule per subscription. */
40
+ declare function rateLimitKey(subscriptionId: string, ruleId: string): string;
41
+
42
+ export { type RateLimitDecision as R, type RateLimitStore as a, createMemoryRateLimitStore as b, checkRateLimit as c, rateLimitKey as r };
@@ -0,0 +1,42 @@
1
+ import { R as RateLimitConfig } from './schema-DuDMPqpq.js';
2
+
3
+ /**
4
+ * Rate limiting is a *storage* question, not an in-memory counter question:
5
+ * a service that resets its limits on every deploy is not rate limited. The
6
+ * engine talks to this interface, the service backs it with the same SQLite
7
+ * file that holds the delivery log, and tests back it with a map.
8
+ */
9
+ interface RateLimitStore {
10
+ /** How many deliveries were recorded for `key` at or after `sinceMs`. */
11
+ countSince(key: string, sinceMs: number): Promise<number>;
12
+ /** When `key` last delivered, or `null` if never. */
13
+ lastAt(key: string): Promise<number | null>;
14
+ /**
15
+ * Record a delivery against both its rule bucket and its subscriber. The
16
+ * subscriber attribution is what the tier throughput cap counts, so the two
17
+ * are written together and can never drift apart.
18
+ */
19
+ record(key: string, subscriberId: string, atMs: number): Promise<void>;
20
+ /** How many alerts a subscriber received since `sinceMs`, for tier caps. */
21
+ countForSubscriberSince(subscriberId: string, sinceMs: number): Promise<number>;
22
+ }
23
+ /** Why a rate limit rejected a delivery, or `null` when it allowed it. */
24
+ interface RateLimitDecision {
25
+ allowed: boolean;
26
+ /** Human-readable reason, safe to log and to show in `/status`. */
27
+ reason: string | null;
28
+ /** When the next delivery would be allowed, ms since epoch, if known. */
29
+ retryAtMs: number | null;
30
+ }
31
+ /**
32
+ * Check a rule's rate limit without consuming it. Consumption happens through
33
+ * {@link RateLimitStore.record} after a delivery attempt, so a rule that
34
+ * matches but fails entitlement checks does not burn quota.
35
+ */
36
+ declare function checkRateLimit(store: RateLimitStore, key: string, config: RateLimitConfig | undefined, nowMs: number): Promise<RateLimitDecision>;
37
+ /** An in-memory {@link RateLimitStore}. Correct, but resets with the process. */
38
+ declare function createMemoryRateLimitStore(): RateLimitStore;
39
+ /** Canonical rate-limit key: one bucket per rule per subscription. */
40
+ declare function rateLimitKey(subscriptionId: string, ruleId: string): string;
41
+
42
+ export { type RateLimitDecision as R, type RateLimitStore as a, createMemoryRateLimitStore as b, checkRateLimit as c, rateLimitKey as r };
@@ -0,0 +1,50 @@
1
+ import { Address } from 'viem';
2
+ import { e as Launchpad } from './types-DFPpTMWo.js';
3
+
4
+ /**
5
+ * A memecoin pool the whale watcher tracks.
6
+ *
7
+ * Why a registry exists at all: chain-wide `Swap` queries are not viable on
8
+ * Robinhood Chain. A topic-only query for the Uniswap v3 `Swap` signature
9
+ * overflows the RPC's 10,000-log result cap in well under 2,000 blocks (the
10
+ * chain produces a block roughly every 100-130ms and is busy). The whale
11
+ * source therefore watches an explicit, address-filtered *active pool set*
12
+ * assembled from launchpad activity: NOXA pools are known at launch, Odyssey
13
+ * pools are known at graduation.
14
+ */
15
+ interface PoolEntry {
16
+ pool: Address;
17
+ /** The memecoin side of the pool. */
18
+ token: Address;
19
+ /** The quote side (WETH or USDG). */
20
+ quoteToken: Address;
21
+ launchpad: Launchpad;
22
+ /** Block the pool became known (launch for NOXA, graduation for Odyssey). */
23
+ createdBlock: bigint;
24
+ }
25
+ /** Storage for known memecoin pools and token origins. */
26
+ interface PoolRegistry {
27
+ /** Record (or update) a pool. Idempotent. */
28
+ record(entry: PoolEntry): Promise<void>;
29
+ /**
30
+ * The pools to watch for whale trades, newest first, capped at `limit`.
31
+ * `pinned` tokens (subscriber watchlists) are always included regardless of
32
+ * age, because a watchlist alert that silently ages out would be a lie.
33
+ */
34
+ active(limit: number, pinned?: readonly Address[]): Promise<PoolEntry[]>;
35
+ /** Which launchpad a token came from, or `null` if never seen. */
36
+ originOf(token: Address): Promise<Launchpad | null>;
37
+ /** Every pool recorded for a token. */
38
+ poolsFor(token: Address): Promise<PoolEntry[]>;
39
+ /** Total pools recorded, for `/metrics`. */
40
+ size(): Promise<number>;
41
+ }
42
+ /**
43
+ * In-memory pool registry. Correct and dependency-free, but it starts empty
44
+ * on every process start, so the service uses the SQLite-backed registry from
45
+ * `hood-alerts/service` instead. This one is the right choice for scripts,
46
+ * examples and tests.
47
+ */
48
+ declare function createMemoryPoolRegistry(entries?: readonly PoolEntry[]): PoolRegistry;
49
+
50
+ export { type PoolEntry as P, type PoolRegistry as a, createMemoryPoolRegistry as c };
@@ -0,0 +1,50 @@
1
+ import { Address } from 'viem';
2
+ import { e as Launchpad } from './types-DFPpTMWo.cjs';
3
+
4
+ /**
5
+ * A memecoin pool the whale watcher tracks.
6
+ *
7
+ * Why a registry exists at all: chain-wide `Swap` queries are not viable on
8
+ * Robinhood Chain. A topic-only query for the Uniswap v3 `Swap` signature
9
+ * overflows the RPC's 10,000-log result cap in well under 2,000 blocks (the
10
+ * chain produces a block roughly every 100-130ms and is busy). The whale
11
+ * source therefore watches an explicit, address-filtered *active pool set*
12
+ * assembled from launchpad activity: NOXA pools are known at launch, Odyssey
13
+ * pools are known at graduation.
14
+ */
15
+ interface PoolEntry {
16
+ pool: Address;
17
+ /** The memecoin side of the pool. */
18
+ token: Address;
19
+ /** The quote side (WETH or USDG). */
20
+ quoteToken: Address;
21
+ launchpad: Launchpad;
22
+ /** Block the pool became known (launch for NOXA, graduation for Odyssey). */
23
+ createdBlock: bigint;
24
+ }
25
+ /** Storage for known memecoin pools and token origins. */
26
+ interface PoolRegistry {
27
+ /** Record (or update) a pool. Idempotent. */
28
+ record(entry: PoolEntry): Promise<void>;
29
+ /**
30
+ * The pools to watch for whale trades, newest first, capped at `limit`.
31
+ * `pinned` tokens (subscriber watchlists) are always included regardless of
32
+ * age, because a watchlist alert that silently ages out would be a lie.
33
+ */
34
+ active(limit: number, pinned?: readonly Address[]): Promise<PoolEntry[]>;
35
+ /** Which launchpad a token came from, or `null` if never seen. */
36
+ originOf(token: Address): Promise<Launchpad | null>;
37
+ /** Every pool recorded for a token. */
38
+ poolsFor(token: Address): Promise<PoolEntry[]>;
39
+ /** Total pools recorded, for `/metrics`. */
40
+ size(): Promise<number>;
41
+ }
42
+ /**
43
+ * In-memory pool registry. Correct and dependency-free, but it starts empty
44
+ * on every process start, so the service uses the SQLite-backed registry from
45
+ * `hood-alerts/service` instead. This one is the right choice for scripts,
46
+ * examples and tests.
47
+ */
48
+ declare function createMemoryPoolRegistry(entries?: readonly PoolEntry[]): PoolRegistry;
49
+
50
+ export { type PoolEntry as P, type PoolRegistry as a, createMemoryPoolRegistry as c };