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.
- package/LICENSE +20 -0
- package/README.md +369 -0
- package/dist/bot/index.cjs +1008 -0
- package/dist/bot/index.cjs.map +1 -0
- package/dist/bot/index.d.cts +241 -0
- package/dist/bot/index.d.ts +241 -0
- package/dist/bot/index.js +30 -0
- package/dist/bot/index.js.map +1 -0
- package/dist/chunk-32GS6XVE.js +732 -0
- package/dist/chunk-32GS6XVE.js.map +1 -0
- package/dist/chunk-3LHCQA4Z.js +76 -0
- package/dist/chunk-3LHCQA4Z.js.map +1 -0
- package/dist/chunk-3NHLSKZE.js +1 -0
- package/dist/chunk-3NHLSKZE.js.map +1 -0
- package/dist/chunk-6JQK5G3Z.js +72 -0
- package/dist/chunk-6JQK5G3Z.js.map +1 -0
- package/dist/chunk-CBNZ6AZT.js +598 -0
- package/dist/chunk-CBNZ6AZT.js.map +1 -0
- package/dist/chunk-CIURPPOW.js +1 -0
- package/dist/chunk-CIURPPOW.js.map +1 -0
- package/dist/chunk-IUOEGBF6.js +49 -0
- package/dist/chunk-IUOEGBF6.js.map +1 -0
- package/dist/chunk-NBMINJ5E.js +230 -0
- package/dist/chunk-NBMINJ5E.js.map +1 -0
- package/dist/chunk-O2RY2SWN.js +1 -0
- package/dist/chunk-O2RY2SWN.js.map +1 -0
- package/dist/chunk-PLMVKTQM.js +1 -0
- package/dist/chunk-PLMVKTQM.js.map +1 -0
- package/dist/chunk-RSTLW7XH.js +130 -0
- package/dist/chunk-RSTLW7XH.js.map +1 -0
- package/dist/chunk-SOBZ2FPP.js +355 -0
- package/dist/chunk-SOBZ2FPP.js.map +1 -0
- package/dist/chunk-VC5MKEY2.js +170 -0
- package/dist/chunk-VC5MKEY2.js.map +1 -0
- package/dist/chunk-W2GUAWUR.js +147 -0
- package/dist/chunk-W2GUAWUR.js.map +1 -0
- package/dist/chunk-Y2M3USWZ.js +355 -0
- package/dist/chunk-Y2M3USWZ.js.map +1 -0
- package/dist/chunk-Z4DJPESQ.js +1216 -0
- package/dist/chunk-Z4DJPESQ.js.map +1 -0
- package/dist/chunk-ZDM5VNWB.js +1 -0
- package/dist/chunk-ZDM5VNWB.js.map +1 -0
- package/dist/events/index.cjs +820 -0
- package/dist/events/index.cjs.map +1 -0
- package/dist/events/index.d.cts +58 -0
- package/dist/events/index.d.ts +58 -0
- package/dist/events/index.js +69 -0
- package/dist/events/index.js.map +1 -0
- package/dist/format-6RG4OcyE.d.ts +57 -0
- package/dist/format-B7d40f65.d.cts +57 -0
- package/dist/index.cjs +4122 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +21 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +274 -0
- package/dist/index.js.map +1 -0
- package/dist/logger-3phvV_fl.d.cts +185 -0
- package/dist/logger-CYza_0eU.d.ts +185 -0
- package/dist/logs-D7sYBvSy.d.cts +66 -0
- package/dist/logs-D7sYBvSy.d.ts +66 -0
- package/dist/notifiers/index.cjs +620 -0
- package/dist/notifiers/index.cjs.map +1 -0
- package/dist/notifiers/index.d.cts +186 -0
- package/dist/notifiers/index.d.ts +186 -0
- package/dist/notifiers/index.js +70 -0
- package/dist/notifiers/index.js.map +1 -0
- package/dist/pricing-BumgLpvB.d.cts +79 -0
- package/dist/pricing-BumgLpvB.d.ts +79 -0
- package/dist/ratelimit-BKVQG2oY.d.cts +42 -0
- package/dist/ratelimit-CGLlXNIS.d.ts +42 -0
- package/dist/registry-D3We8Lg6.d.ts +50 -0
- package/dist/registry-DePA3IyS.d.cts +50 -0
- package/dist/rules/index.cjs +614 -0
- package/dist/rules/index.cjs.map +1 -0
- package/dist/rules/index.d.cts +166 -0
- package/dist/rules/index.d.ts +166 -0
- package/dist/rules/index.js +60 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/schema-DuDMPqpq.d.ts +183 -0
- package/dist/schema-NnIdKFl2.d.cts +183 -0
- package/dist/service/index.cjs +3774 -0
- package/dist/service/index.cjs.map +1 -0
- package/dist/service/index.d.cts +262 -0
- package/dist/service/index.d.ts +262 -0
- package/dist/service/index.js +45 -0
- package/dist/service/index.js.map +1 -0
- package/dist/service/main.cjs +3759 -0
- package/dist/service/main.cjs.map +1 -0
- package/dist/service/main.d.cts +1 -0
- package/dist/service/main.d.ts +1 -0
- package/dist/service/main.js +60 -0
- package/dist/service/main.js.map +1 -0
- package/dist/sources-CAaQR6-N.d.ts +159 -0
- package/dist/sources-CpDOUlzx.d.cts +159 -0
- package/dist/telegram-CpodYJrP.d.cts +64 -0
- package/dist/telegram-pvphmBwn.d.ts +64 -0
- package/dist/tiers/index.cjs +471 -0
- package/dist/tiers/index.cjs.map +1 -0
- package/dist/tiers/index.d.cts +231 -0
- package/dist/tiers/index.d.ts +231 -0
- package/dist/tiers/index.js +34 -0
- package/dist/tiers/index.js.map +1 -0
- package/dist/types-C0ETog04.d.ts +109 -0
- package/dist/types-CcoKpStA.d.cts +109 -0
- package/dist/types-DFPpTMWo.d.cts +186 -0
- package/dist/types-DFPpTMWo.d.ts +186 -0
- 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 };
|