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,109 @@
1
+ import { P as Platform } from './schema-NnIdKFl2.cjs';
2
+
3
+ /** A Discord embed field. */
4
+ interface DiscordEmbedField {
5
+ name: string;
6
+ value: string;
7
+ inline?: boolean;
8
+ }
9
+ /** The subset of the Discord embed object this package produces. */
10
+ interface DiscordEmbed {
11
+ title: string;
12
+ url?: string;
13
+ description?: string;
14
+ /** Decimal RGB, per Discord's API. */
15
+ color: number;
16
+ fields: DiscordEmbedField[];
17
+ footer?: {
18
+ text: string;
19
+ };
20
+ /** ISO-8601 timestamp. Omitted when the block time was not read. */
21
+ timestamp?: string;
22
+ }
23
+ /**
24
+ * One alert, rendered for every platform up front.
25
+ *
26
+ * Rendering once and letting each notifier pick its representation keeps the
27
+ * escaping rules next to each other (where they can be compared and tested
28
+ * together) and means a fan-out to Telegram and Discord formats the event
29
+ * once, not twice.
30
+ */
31
+ interface RenderedAlert {
32
+ /** Telegram body, escaped for {@link RenderedAlert.parseMode}. */
33
+ text: string;
34
+ parseMode: 'HTML' | 'MarkdownV2';
35
+ /** Discord embed representation of the same alert. */
36
+ embed: DiscordEmbed;
37
+ /** Unstyled one-line summary, for logs and for platforms added later. */
38
+ plain: string;
39
+ }
40
+ /** What happened when a notifier tried to deliver. */
41
+ interface DeliveryResult {
42
+ ok: boolean;
43
+ /** HTTP status of the final attempt, or `null` if the request never completed. */
44
+ status: number | null;
45
+ /** How many requests were made, including the successful one. */
46
+ attempts: number;
47
+ /** Platform message id, when the API returned one. */
48
+ messageId: string | null;
49
+ /** Failure detail. `null` on success. */
50
+ error: string | null;
51
+ /**
52
+ * `true` when the failure is worth retrying later (5xx, network, exhausted
53
+ * rate limit). `false` for a permanent rejection such as a bad chat id, so
54
+ * the service can disable a dead subscription instead of retrying forever.
55
+ */
56
+ retryable: boolean;
57
+ }
58
+ /** The one interface every delivery adapter implements. */
59
+ interface Notifier {
60
+ readonly platform: Platform;
61
+ /** Adapter flavour, for logs and metrics: `bot`, `webhook`. */
62
+ readonly kind: string;
63
+ /**
64
+ * Deliver a rendered alert to a platform-specific target: a Telegram chat
65
+ * id, a Discord channel id, or a Discord webhook URL.
66
+ */
67
+ send(target: string, alert: RenderedAlert): Promise<DeliveryResult>;
68
+ }
69
+ /** The `fetch` shape the adapters use, so tests can substitute a double. */
70
+ type FetchLike = (input: string, init?: {
71
+ method?: string;
72
+ headers?: Record<string, string>;
73
+ body?: string;
74
+ }) => Promise<FetchLikeResponse>;
75
+ /** The response shape the adapters read. */
76
+ interface FetchLikeResponse {
77
+ ok: boolean;
78
+ status: number;
79
+ headers: {
80
+ get(name: string): string | null;
81
+ };
82
+ text(): Promise<string>;
83
+ }
84
+ /** Options common to every HTTP-backed notifier. */
85
+ interface NotifierHttpOptions {
86
+ /** Injected `fetch`. Defaults to the global. */
87
+ fetch?: FetchLike;
88
+ /** Max attempts per delivery, including the first. @defaultValue `4` */
89
+ maxAttempts?: number;
90
+ /** Base backoff in ms for retryable failures, doubled per attempt. @defaultValue `500` */
91
+ backoffMs?: number;
92
+ /**
93
+ * Upper bound on how long a single delivery will wait for a rate limit, in
94
+ * ms. A `retry_after` beyond this gives up and reports a retryable failure
95
+ * rather than blocking the whole dispatch loop.
96
+ * @defaultValue `60_000`
97
+ */
98
+ maxWaitMs?: number;
99
+ /** Sleep injection point for tests. */
100
+ sleep?: (ms: number) => Promise<void>;
101
+ /** Request timeout in ms. @defaultValue `15_000` */
102
+ timeoutMs?: number;
103
+ }
104
+ /** A successful send result. */
105
+ declare function delivered(status: number, attempts: number, messageId: string | null): DeliveryResult;
106
+ /** A failed send result. */
107
+ declare function failed(status: number | null, attempts: number, error: string, retryable: boolean): DeliveryResult;
108
+
109
+ export { type DeliveryResult as D, type FetchLike as F, type Notifier as N, type RenderedAlert as R, type DiscordEmbed as a, type DiscordEmbedField as b, type FetchLikeResponse as c, type NotifierHttpOptions as d, delivered as e, failed as f };
@@ -0,0 +1,186 @@
1
+ import { Address, Hash } from 'viem';
2
+
3
+ /**
4
+ * The normalized alert event taxonomy for Robinhood Chain memecoins.
5
+ *
6
+ * Two launchpads run on mainnet (chain 4663) and they have genuinely
7
+ * different lifecycles. The taxonomy models that difference instead of
8
+ * pretending they are the same product:
9
+ *
10
+ * - **NOXA** (`fun.noxa.fi/robinhood`) is an *instant* launcher. One
11
+ * transaction deploys the ERC-20, creates a Uniswap v3 pool, seeds
12
+ * single-sided liquidity and locks the LP NFT. There is no bonding curve,
13
+ * so NOXA emits `launch` and (through its pool) `whale_trade`, and it can
14
+ * never emit `curve_trade` or `graduation`.
15
+ * - **The Odyssey** (`theodyssey.fun`) is a pump.fun-style native-ETH bonding
16
+ * curve with virtual reserves. It emits `launch` when a curve opens,
17
+ * `curve_trade` for every buy/sell on the curve, and `graduation` when the
18
+ * curve fills and liquidity migrates to a locked Uniswap v3 pool. After
19
+ * graduation its tokens also produce `whale_trade` from the migrated pool.
20
+ *
21
+ * A `graduation` alert therefore covers The Odyssey only. Claiming NOXA
22
+ * "graduations" would be describing an event that does not exist on chain.
23
+ */
24
+ /** Which launchpad an event originated from. */
25
+ type Launchpad = 'noxa' | 'odyssey';
26
+ /** The kinds of alert this package emits. */
27
+ type AlertEventKind = 'launch' | 'curve_trade' | 'graduation' | 'whale_trade';
28
+ /** Buy or sell, from the trader's perspective. */
29
+ type TradeSide = 'buy' | 'sell';
30
+ /** Explorer deep links attached to every event. */
31
+ interface ExplorerLinks {
32
+ /** The transaction that produced the event. */
33
+ tx: string;
34
+ /** The token contract. */
35
+ token: string;
36
+ /** The actor (deployer or trader). */
37
+ actor: string;
38
+ /** The Uniswap v3 pool, when the event has one. */
39
+ pool?: string;
40
+ }
41
+ /** Fields every alert event carries. */
42
+ interface AlertEventBase {
43
+ /**
44
+ * Stable, content-addressed identity for this event: `kind:txHash:logIndex`.
45
+ * Reprocessing the same block range regenerates the identical id, which is
46
+ * what makes the delivery dedupe table work across restarts and reorg
47
+ * re-reads.
48
+ */
49
+ id: string;
50
+ kind: AlertEventKind;
51
+ launchpad: Launchpad;
52
+ /** The memecoin the event is about. */
53
+ token: Address;
54
+ /** Token symbol read from the ERC-20, or `null` when the call reverts. */
55
+ symbol: string | null;
56
+ /** Token name read from the ERC-20, or `null` when the call reverts. */
57
+ name: string | null;
58
+ /** The wallet responsible: the deployer for launches, the trader for trades. */
59
+ actor: Address;
60
+ blockNumber: bigint;
61
+ logIndex: number;
62
+ transactionHash: Hash;
63
+ /**
64
+ * Block timestamp in milliseconds since epoch, or `null` when the event was
65
+ * decoded without a block fetch (the source fills it whenever available).
66
+ */
67
+ timestampMs: number | null;
68
+ /**
69
+ * USD value of the event, or `null` when it cannot be derived from a real
70
+ * price source. Never guessed: launches carry the seeded/initial-buy value,
71
+ * trades carry the traded notional, and `null` means "no USDG or WETH leg to
72
+ * price against", which rules treat as "does not satisfy a min-USD filter".
73
+ */
74
+ usdValue: number | null;
75
+ explorer: ExplorerLinks;
76
+ }
77
+ /**
78
+ * A new token launch.
79
+ *
80
+ * NOXA: decoded from `TokenLaunched` on the launch factory. `pool` is present
81
+ * immediately and `initialBuyAmount` is the deployer's own first buy.
82
+ * Odyssey: decoded from `TokenCreated` on a bonding-curve factory. `pool` is
83
+ * `null` because the curve holds the liquidity until graduation.
84
+ */
85
+ interface LaunchEvent extends AlertEventBase {
86
+ kind: 'launch';
87
+ /** The Uniswap v3 pool, or `null` for an Odyssey token still on its curve. */
88
+ pool: Address | null;
89
+ /** The quote token of the pool (NOXA only, `null` on Odyssey). */
90
+ pairToken: Address | null;
91
+ /** Raw initial buy in the pair token's units (NOXA), else `0n`. */
92
+ initialBuyAmount: bigint;
93
+ /**
94
+ * The Uniswap v3 LP position NFT minted for this launch (NOXA), or `null`
95
+ * when the launchpad did not mint one at launch time (every Odyssey token,
96
+ * whose LP appears at graduation). Reputation rules read its owner to prove
97
+ * the LP is locked.
98
+ */
99
+ positionId: bigint | null;
100
+ /**
101
+ * `true` when this launchpad lists instantly with no bonding curve, so no
102
+ * `graduation` event will ever follow. Always `true` for NOXA. For Odyssey
103
+ * it is `true` only for launches from the instant factory variant.
104
+ */
105
+ instantListing: boolean;
106
+ }
107
+ /**
108
+ * A bonding-curve buy or sell. The Odyssey only: NOXA has no curve.
109
+ * Decoded from `Traded` on an Odyssey factory.
110
+ */
111
+ interface CurveTradeEvent extends AlertEventBase {
112
+ kind: 'curve_trade';
113
+ launchpad: 'odyssey';
114
+ side: TradeSide;
115
+ /** Token amount bought or sold, raw (18 decimals). */
116
+ tokenAmount: bigint;
117
+ /** Native ETH paid or received, in wei. */
118
+ quoteAmountWei: bigint;
119
+ /** Protocol fee taken on this trade, in wei. */
120
+ feeWei: bigint;
121
+ /** Curve virtual quote reserve after the trade, in wei. */
122
+ virtualQuoteWei: bigint;
123
+ /** Curve virtual token reserve after the trade, raw. */
124
+ virtualTokenAmount: bigint;
125
+ /** Price of one token in ETH implied by this fill, or `null` for a zero-size fill. */
126
+ priceEth: number | null;
127
+ }
128
+ /**
129
+ * A curve that filled and migrated to a locked Uniswap v3 pool.
130
+ * The Odyssey only. Decoded from `PoolMigrated`.
131
+ */
132
+ interface GraduationEvent extends AlertEventBase {
133
+ kind: 'graduation';
134
+ launchpad: 'odyssey';
135
+ /** The Uniswap v3 pool liquidity migrated into. */
136
+ pool: Address;
137
+ /** The position manager token id holding the migrated liquidity. */
138
+ positionId: bigint;
139
+ /** Uniswap v3 liquidity units minted at migration. */
140
+ liquidity: bigint;
141
+ /** Token side used to seed the pool, raw. */
142
+ tokenUsed: bigint;
143
+ /** Quote side used to seed the pool, raw (6 decimals when the quote is USDG). */
144
+ quoteUsed: bigint;
145
+ }
146
+ /**
147
+ * A Uniswap v3 swap on a memecoin pool above the configured USD threshold.
148
+ * Both launchpads produce these: NOXA from block one, Odyssey after
149
+ * graduation.
150
+ */
151
+ interface WhaleTradeEvent extends AlertEventBase {
152
+ kind: 'whale_trade';
153
+ pool: Address;
154
+ /** The pool's quote token (the non-memecoin side). */
155
+ quoteToken: Address;
156
+ /** Quote token symbol, e.g. `WETH` or `USDG`. */
157
+ quoteSymbol: string;
158
+ side: TradeSide;
159
+ /** Memecoin amount that moved, in human units. */
160
+ tokenAmount: number;
161
+ /** Quote amount that moved, in human units. */
162
+ quoteAmount: number;
163
+ /** Executed price of the memecoin in quote-token units. */
164
+ price: number;
165
+ /** Uniswap v3 fee tier of the pool, in hundredths of a bip (3000 = 0.3%). */
166
+ feeTier: number;
167
+ }
168
+ /** Any normalized alert event. */
169
+ type AlertEvent = LaunchEvent | CurveTradeEvent | GraduationEvent | WhaleTradeEvent;
170
+ /** Narrow an event to a kind. */
171
+ declare function isKind<K extends AlertEventKind>(event: AlertEvent, kind: K): event is Extract<AlertEvent, {
172
+ kind: K;
173
+ }>;
174
+ /**
175
+ * Human-readable one-line label for an event kind, used by the bot and the
176
+ * notifier formatters.
177
+ */
178
+ declare const EVENT_KIND_LABELS: Record<AlertEventKind, string>;
179
+ /**
180
+ * Which event kinds each launchpad can actually produce. The rule engine uses
181
+ * this to reject impossible rules (for example: Odyssey-only `graduation`
182
+ * combined with a NOXA-only launchpad filter would never fire).
183
+ */
184
+ declare const LAUNCHPAD_EVENT_KINDS: Record<Launchpad, readonly AlertEventKind[]>;
185
+
186
+ export { type AlertEvent as A, type CurveTradeEvent as C, EVENT_KIND_LABELS as E, type GraduationEvent as G, LAUNCHPAD_EVENT_KINDS as L, type TradeSide as T, type WhaleTradeEvent as W, type AlertEventBase as a, type AlertEventKind as b, type ExplorerLinks as c, type LaunchEvent as d, type Launchpad as e, isKind as i };
@@ -0,0 +1,186 @@
1
+ import { Address, Hash } from 'viem';
2
+
3
+ /**
4
+ * The normalized alert event taxonomy for Robinhood Chain memecoins.
5
+ *
6
+ * Two launchpads run on mainnet (chain 4663) and they have genuinely
7
+ * different lifecycles. The taxonomy models that difference instead of
8
+ * pretending they are the same product:
9
+ *
10
+ * - **NOXA** (`fun.noxa.fi/robinhood`) is an *instant* launcher. One
11
+ * transaction deploys the ERC-20, creates a Uniswap v3 pool, seeds
12
+ * single-sided liquidity and locks the LP NFT. There is no bonding curve,
13
+ * so NOXA emits `launch` and (through its pool) `whale_trade`, and it can
14
+ * never emit `curve_trade` or `graduation`.
15
+ * - **The Odyssey** (`theodyssey.fun`) is a pump.fun-style native-ETH bonding
16
+ * curve with virtual reserves. It emits `launch` when a curve opens,
17
+ * `curve_trade` for every buy/sell on the curve, and `graduation` when the
18
+ * curve fills and liquidity migrates to a locked Uniswap v3 pool. After
19
+ * graduation its tokens also produce `whale_trade` from the migrated pool.
20
+ *
21
+ * A `graduation` alert therefore covers The Odyssey only. Claiming NOXA
22
+ * "graduations" would be describing an event that does not exist on chain.
23
+ */
24
+ /** Which launchpad an event originated from. */
25
+ type Launchpad = 'noxa' | 'odyssey';
26
+ /** The kinds of alert this package emits. */
27
+ type AlertEventKind = 'launch' | 'curve_trade' | 'graduation' | 'whale_trade';
28
+ /** Buy or sell, from the trader's perspective. */
29
+ type TradeSide = 'buy' | 'sell';
30
+ /** Explorer deep links attached to every event. */
31
+ interface ExplorerLinks {
32
+ /** The transaction that produced the event. */
33
+ tx: string;
34
+ /** The token contract. */
35
+ token: string;
36
+ /** The actor (deployer or trader). */
37
+ actor: string;
38
+ /** The Uniswap v3 pool, when the event has one. */
39
+ pool?: string;
40
+ }
41
+ /** Fields every alert event carries. */
42
+ interface AlertEventBase {
43
+ /**
44
+ * Stable, content-addressed identity for this event: `kind:txHash:logIndex`.
45
+ * Reprocessing the same block range regenerates the identical id, which is
46
+ * what makes the delivery dedupe table work across restarts and reorg
47
+ * re-reads.
48
+ */
49
+ id: string;
50
+ kind: AlertEventKind;
51
+ launchpad: Launchpad;
52
+ /** The memecoin the event is about. */
53
+ token: Address;
54
+ /** Token symbol read from the ERC-20, or `null` when the call reverts. */
55
+ symbol: string | null;
56
+ /** Token name read from the ERC-20, or `null` when the call reverts. */
57
+ name: string | null;
58
+ /** The wallet responsible: the deployer for launches, the trader for trades. */
59
+ actor: Address;
60
+ blockNumber: bigint;
61
+ logIndex: number;
62
+ transactionHash: Hash;
63
+ /**
64
+ * Block timestamp in milliseconds since epoch, or `null` when the event was
65
+ * decoded without a block fetch (the source fills it whenever available).
66
+ */
67
+ timestampMs: number | null;
68
+ /**
69
+ * USD value of the event, or `null` when it cannot be derived from a real
70
+ * price source. Never guessed: launches carry the seeded/initial-buy value,
71
+ * trades carry the traded notional, and `null` means "no USDG or WETH leg to
72
+ * price against", which rules treat as "does not satisfy a min-USD filter".
73
+ */
74
+ usdValue: number | null;
75
+ explorer: ExplorerLinks;
76
+ }
77
+ /**
78
+ * A new token launch.
79
+ *
80
+ * NOXA: decoded from `TokenLaunched` on the launch factory. `pool` is present
81
+ * immediately and `initialBuyAmount` is the deployer's own first buy.
82
+ * Odyssey: decoded from `TokenCreated` on a bonding-curve factory. `pool` is
83
+ * `null` because the curve holds the liquidity until graduation.
84
+ */
85
+ interface LaunchEvent extends AlertEventBase {
86
+ kind: 'launch';
87
+ /** The Uniswap v3 pool, or `null` for an Odyssey token still on its curve. */
88
+ pool: Address | null;
89
+ /** The quote token of the pool (NOXA only, `null` on Odyssey). */
90
+ pairToken: Address | null;
91
+ /** Raw initial buy in the pair token's units (NOXA), else `0n`. */
92
+ initialBuyAmount: bigint;
93
+ /**
94
+ * The Uniswap v3 LP position NFT minted for this launch (NOXA), or `null`
95
+ * when the launchpad did not mint one at launch time (every Odyssey token,
96
+ * whose LP appears at graduation). Reputation rules read its owner to prove
97
+ * the LP is locked.
98
+ */
99
+ positionId: bigint | null;
100
+ /**
101
+ * `true` when this launchpad lists instantly with no bonding curve, so no
102
+ * `graduation` event will ever follow. Always `true` for NOXA. For Odyssey
103
+ * it is `true` only for launches from the instant factory variant.
104
+ */
105
+ instantListing: boolean;
106
+ }
107
+ /**
108
+ * A bonding-curve buy or sell. The Odyssey only: NOXA has no curve.
109
+ * Decoded from `Traded` on an Odyssey factory.
110
+ */
111
+ interface CurveTradeEvent extends AlertEventBase {
112
+ kind: 'curve_trade';
113
+ launchpad: 'odyssey';
114
+ side: TradeSide;
115
+ /** Token amount bought or sold, raw (18 decimals). */
116
+ tokenAmount: bigint;
117
+ /** Native ETH paid or received, in wei. */
118
+ quoteAmountWei: bigint;
119
+ /** Protocol fee taken on this trade, in wei. */
120
+ feeWei: bigint;
121
+ /** Curve virtual quote reserve after the trade, in wei. */
122
+ virtualQuoteWei: bigint;
123
+ /** Curve virtual token reserve after the trade, raw. */
124
+ virtualTokenAmount: bigint;
125
+ /** Price of one token in ETH implied by this fill, or `null` for a zero-size fill. */
126
+ priceEth: number | null;
127
+ }
128
+ /**
129
+ * A curve that filled and migrated to a locked Uniswap v3 pool.
130
+ * The Odyssey only. Decoded from `PoolMigrated`.
131
+ */
132
+ interface GraduationEvent extends AlertEventBase {
133
+ kind: 'graduation';
134
+ launchpad: 'odyssey';
135
+ /** The Uniswap v3 pool liquidity migrated into. */
136
+ pool: Address;
137
+ /** The position manager token id holding the migrated liquidity. */
138
+ positionId: bigint;
139
+ /** Uniswap v3 liquidity units minted at migration. */
140
+ liquidity: bigint;
141
+ /** Token side used to seed the pool, raw. */
142
+ tokenUsed: bigint;
143
+ /** Quote side used to seed the pool, raw (6 decimals when the quote is USDG). */
144
+ quoteUsed: bigint;
145
+ }
146
+ /**
147
+ * A Uniswap v3 swap on a memecoin pool above the configured USD threshold.
148
+ * Both launchpads produce these: NOXA from block one, Odyssey after
149
+ * graduation.
150
+ */
151
+ interface WhaleTradeEvent extends AlertEventBase {
152
+ kind: 'whale_trade';
153
+ pool: Address;
154
+ /** The pool's quote token (the non-memecoin side). */
155
+ quoteToken: Address;
156
+ /** Quote token symbol, e.g. `WETH` or `USDG`. */
157
+ quoteSymbol: string;
158
+ side: TradeSide;
159
+ /** Memecoin amount that moved, in human units. */
160
+ tokenAmount: number;
161
+ /** Quote amount that moved, in human units. */
162
+ quoteAmount: number;
163
+ /** Executed price of the memecoin in quote-token units. */
164
+ price: number;
165
+ /** Uniswap v3 fee tier of the pool, in hundredths of a bip (3000 = 0.3%). */
166
+ feeTier: number;
167
+ }
168
+ /** Any normalized alert event. */
169
+ type AlertEvent = LaunchEvent | CurveTradeEvent | GraduationEvent | WhaleTradeEvent;
170
+ /** Narrow an event to a kind. */
171
+ declare function isKind<K extends AlertEventKind>(event: AlertEvent, kind: K): event is Extract<AlertEvent, {
172
+ kind: K;
173
+ }>;
174
+ /**
175
+ * Human-readable one-line label for an event kind, used by the bot and the
176
+ * notifier formatters.
177
+ */
178
+ declare const EVENT_KIND_LABELS: Record<AlertEventKind, string>;
179
+ /**
180
+ * Which event kinds each launchpad can actually produce. The rule engine uses
181
+ * this to reject impossible rules (for example: Odyssey-only `graduation`
182
+ * combined with a NOXA-only launchpad filter would never fire).
183
+ */
184
+ declare const LAUNCHPAD_EVENT_KINDS: Record<Launchpad, readonly AlertEventKind[]>;
185
+
186
+ export { type AlertEvent as A, type CurveTradeEvent as C, EVENT_KIND_LABELS as E, type GraduationEvent as G, LAUNCHPAD_EVENT_KINDS as L, type TradeSide as T, type WhaleTradeEvent as W, type AlertEventBase as a, type AlertEventKind as b, type ExplorerLinks as c, type LaunchEvent as d, type Launchpad as e, isKind as i };
package/package.json ADDED
@@ -0,0 +1,114 @@
1
+ {
2
+ "name": "hood-alerts",
3
+ "version": "0.1.0",
4
+ "description": "Telegram + Discord alert bots for Robinhood Chain memecoins (chain ID 4663): normalized launch/curve/graduation/whale events, a schema-validated rule engine, rate-limit-correct notifiers, and a restart-safe hosted service with SQLite state.",
5
+ "keywords": [
6
+ "robinhood",
7
+ "robinhood-chain",
8
+ "memecoin",
9
+ "alerts",
10
+ "telegram-bot",
11
+ "discord-bot",
12
+ "noxa",
13
+ "odyssey",
14
+ "whale-alerts",
15
+ "uniswap",
16
+ "viem"
17
+ ],
18
+ "license": "SEE LICENSE IN LICENSE",
19
+ "author": "nirholas (https://x.com/nichxbt)",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/nirholas/robinhood-chain-alerts.git"
23
+ },
24
+ "homepage": "https://nirholas.github.io/hood-alerts/",
25
+ "bugs": {
26
+ "url": "https://github.com/nirholas/robinhood-chain-alerts/issues"
27
+ },
28
+ "type": "module",
29
+ "engines": {
30
+ "node": ">=20"
31
+ },
32
+ "sideEffects": false,
33
+ "main": "./dist/index.cjs",
34
+ "module": "./dist/index.js",
35
+ "types": "./dist/index.d.ts",
36
+ "bin": {
37
+ "hood-alerts": "./dist/service/main.js"
38
+ },
39
+ "exports": {
40
+ ".": {
41
+ "types": "./dist/index.d.ts",
42
+ "import": "./dist/index.js",
43
+ "require": "./dist/index.cjs"
44
+ },
45
+ "./events": {
46
+ "types": "./dist/events/index.d.ts",
47
+ "import": "./dist/events/index.js",
48
+ "require": "./dist/events/index.cjs"
49
+ },
50
+ "./rules": {
51
+ "types": "./dist/rules/index.d.ts",
52
+ "import": "./dist/rules/index.js",
53
+ "require": "./dist/rules/index.cjs"
54
+ },
55
+ "./notifiers": {
56
+ "types": "./dist/notifiers/index.d.ts",
57
+ "import": "./dist/notifiers/index.js",
58
+ "require": "./dist/notifiers/index.cjs"
59
+ },
60
+ "./bot": {
61
+ "types": "./dist/bot/index.d.ts",
62
+ "import": "./dist/bot/index.js",
63
+ "require": "./dist/bot/index.cjs"
64
+ },
65
+ "./tiers": {
66
+ "types": "./dist/tiers/index.d.ts",
67
+ "import": "./dist/tiers/index.js",
68
+ "require": "./dist/tiers/index.cjs"
69
+ },
70
+ "./service": {
71
+ "types": "./dist/service/index.d.ts",
72
+ "import": "./dist/service/index.js",
73
+ "require": "./dist/service/index.cjs"
74
+ },
75
+ "./package.json": "./package.json"
76
+ },
77
+ "files": [
78
+ "dist",
79
+ "README.md",
80
+ "LICENSE"
81
+ ],
82
+ "scripts": {
83
+ "build": "tsup",
84
+ "typecheck": "tsc --noEmit",
85
+ "test": "vitest run tests",
86
+ "test:watch": "vitest",
87
+ "start": "node dist/service/main.js",
88
+ "dev": "tsx src/service/main.ts",
89
+ "verify:chain": "tsx examples/verify-chain.ts",
90
+ "demo:events": "tsx examples/watch-events.ts",
91
+ "demo:notify": "tsx examples/dry-run-notify.ts",
92
+ "demo:reputation": "tsx examples/deployer-reputation.ts",
93
+ "register:discord": "tsx examples/register-discord-commands.ts",
94
+ "prepublishOnly": "npm run build && npm test"
95
+ },
96
+ "dependencies": {
97
+ "zod": "^4.0.0"
98
+ },
99
+ "peerDependencies": {
100
+ "hoodchain": "^0.1.1",
101
+ "hoodkit": "^0.1.0",
102
+ "viem": "^2.55.0"
103
+ },
104
+ "devDependencies": {
105
+ "@types/node": "^24.0.0",
106
+ "hoodchain": "^0.1.1",
107
+ "hoodkit": "^0.1.0",
108
+ "tsup": "^8.5.0",
109
+ "tsx": "^4.19.2",
110
+ "typescript": "^5.9.0",
111
+ "viem": "^2.55.0",
112
+ "vitest": "^3.2.0"
113
+ }
114
+ }