@livo-build/runtime 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +126 -8
- package/dist/chain.d.ts +88 -5
- package/dist/chain.js +208 -21
- package/dist/contracts.d.ts +19 -0
- package/dist/contracts.js +15 -1
- package/dist/index.d.ts +14 -4
- package/dist/index.js +13 -2
- package/dist/indexer.d.ts +35 -0
- package/dist/indexer.js +73 -0
- package/dist/multicall.d.ts +30 -0
- package/dist/multicall.js +65 -0
- package/dist/queue.d.ts +27 -0
- package/dist/queue.js +49 -0
- package/dist/secret.d.ts +15 -0
- package/dist/secret.js +40 -0
- package/dist/store.d.ts +11 -0
- package/dist/store.js +24 -0
- package/dist/telegram.d.ts +63 -0
- package/dist/telegram.js +115 -0
- package/dist/watch.d.ts +44 -0
- package/dist/watch.js +46 -0
- package/package.json +1 -1
package/dist/watch.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Chain } from "./chain.js";
|
|
2
|
+
import type { Store } from "./store.js";
|
|
3
|
+
import type { AbiEvent, DecodedLog } from "./events.js";
|
|
4
|
+
import type { Hex } from "./hex.js";
|
|
5
|
+
export interface WatchLogsOptions {
|
|
6
|
+
/** Event to decode — an AbiEvent or a human-readable signature. */
|
|
7
|
+
event: AbiEvent | string;
|
|
8
|
+
/** Contract address(es) to filter (omit to match any). */
|
|
9
|
+
address?: Hex | Hex[];
|
|
10
|
+
/** Indexed-arg filter, positional or by name (see Chain.getLogs). */
|
|
11
|
+
args?: Record<string, unknown> | unknown[];
|
|
12
|
+
/** Store key holding this watcher's cursor. Namespace it per watcher. */
|
|
13
|
+
cursorKey: string;
|
|
14
|
+
/** First block to scan when no cursor exists yet. Default: current head (new logs only). */
|
|
15
|
+
fromBlock?: bigint;
|
|
16
|
+
/** Stay this many blocks behind head for reorg safety. Default 0. */
|
|
17
|
+
confirmations?: bigint;
|
|
18
|
+
/** Max blocks per eth_getLogs window (RPC range cap). Default 2000n. */
|
|
19
|
+
chunkSize?: bigint;
|
|
20
|
+
/** Invoked per non-empty chunk with that chunk's decoded logs. Must be idempotent. */
|
|
21
|
+
onLogs: (logs: DecodedLog[], range: {
|
|
22
|
+
fromBlock: bigint;
|
|
23
|
+
toBlock: bigint;
|
|
24
|
+
}) => Promise<void> | void;
|
|
25
|
+
}
|
|
26
|
+
export interface WatchLogsResult {
|
|
27
|
+
/** First block scanned this run. */
|
|
28
|
+
fromBlock: bigint;
|
|
29
|
+
/** New cursor — the last block scanned (head - confirmations). */
|
|
30
|
+
toBlock: bigint;
|
|
31
|
+
/** Total logs delivered to onLogs this run. */
|
|
32
|
+
logCount: number;
|
|
33
|
+
/** Number of eth_getLogs windows scanned. */
|
|
34
|
+
chunks: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Catch up on event logs since the last run. Reads the cursor (or `fromBlock`),
|
|
38
|
+
* scans `[cursor, head - confirmations]` in `chunkSize` windows, calls `onLogs`
|
|
39
|
+
* per non-empty chunk, and persists the cursor AFTER each chunk — so a Worker
|
|
40
|
+
* killed mid-catch-up resumes from the last completed window. Delivery is
|
|
41
|
+
* at-least-once: if `onLogs` throws, the cursor is not advanced past that chunk
|
|
42
|
+
* and the next tick retries it, so `onLogs` must be idempotent.
|
|
43
|
+
*/
|
|
44
|
+
export declare function watchLogs(chain: Chain, store: Store, opts: WatchLogsOptions): Promise<WatchLogsResult>;
|
package/dist/watch.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// watchLogs — resumable event catch-up for keepers. A Worker has no long-lived
|
|
2
|
+
// process, so this is NOT a websocket subscription: each scheduled tick reads new
|
|
3
|
+
// logs since a Store-persisted block cursor, up to the (confirmed) head, in
|
|
4
|
+
// chunked eth_getLogs windows. Layered as a free function over Chain + Store so
|
|
5
|
+
// Chain (Layer 1, may hold keys) never imports Store (Layer 3).
|
|
6
|
+
/**
|
|
7
|
+
* Catch up on event logs since the last run. Reads the cursor (or `fromBlock`),
|
|
8
|
+
* scans `[cursor, head - confirmations]` in `chunkSize` windows, calls `onLogs`
|
|
9
|
+
* per non-empty chunk, and persists the cursor AFTER each chunk — so a Worker
|
|
10
|
+
* killed mid-catch-up resumes from the last completed window. Delivery is
|
|
11
|
+
* at-least-once: if `onLogs` throws, the cursor is not advanced past that chunk
|
|
12
|
+
* and the next tick retries it, so `onLogs` must be idempotent.
|
|
13
|
+
*/
|
|
14
|
+
export async function watchLogs(chain, store, opts) {
|
|
15
|
+
const confirmations = opts.confirmations ?? 0n;
|
|
16
|
+
const chunkSize = opts.chunkSize ?? 2000n;
|
|
17
|
+
const head = await chain.getBlockNumber();
|
|
18
|
+
const safeHead = head - confirmations;
|
|
19
|
+
const stored = await store.getJSON(opts.cursorKey);
|
|
20
|
+
const from = stored ? BigInt(stored.next) : (opts.fromBlock ?? safeHead);
|
|
21
|
+
if (from > safeHead) {
|
|
22
|
+
return { fromBlock: from, toBlock: from - 1n, logCount: 0, chunks: 0 };
|
|
23
|
+
}
|
|
24
|
+
let cur = from;
|
|
25
|
+
let logCount = 0;
|
|
26
|
+
let chunks = 0;
|
|
27
|
+
while (cur <= safeHead) {
|
|
28
|
+
const to = cur + chunkSize - 1n < safeHead ? cur + chunkSize - 1n : safeHead;
|
|
29
|
+
const logs = await chain.getLogs({
|
|
30
|
+
event: opts.event,
|
|
31
|
+
address: opts.address,
|
|
32
|
+
args: opts.args,
|
|
33
|
+
fromBlock: cur,
|
|
34
|
+
toBlock: to,
|
|
35
|
+
});
|
|
36
|
+
chunks++;
|
|
37
|
+
if (logs.length) {
|
|
38
|
+
await opts.onLogs(logs, { fromBlock: cur, toBlock: to });
|
|
39
|
+
logCount += logs.length;
|
|
40
|
+
}
|
|
41
|
+
// Advance + persist only after a successful onLogs, so a throw retries this window.
|
|
42
|
+
await store.setJSON(opts.cursorKey, { next: (to + 1n).toString() });
|
|
43
|
+
cur = to + 1n;
|
|
44
|
+
}
|
|
45
|
+
return { fromBlock: from, toBlock: safeHead, logCount, chunks };
|
|
46
|
+
}
|