@secondlayer/subgraphs 3.21.0 → 3.23.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/dist/src/index.d.ts +238 -17
- package/dist/src/index.js +389 -52
- package/dist/src/index.js.map +12 -11
- package/dist/src/runtime/block-processor.d.ts +111 -10
- package/dist/src/runtime/block-processor.js +263 -31
- package/dist/src/runtime/block-processor.js.map +9 -8
- package/dist/src/runtime/catchup.d.ts +111 -10
- package/dist/src/runtime/catchup.js +263 -31
- package/dist/src/runtime/catchup.js.map +9 -8
- package/dist/src/runtime/context.d.ts +111 -11
- package/dist/src/runtime/context.js +166 -12
- package/dist/src/runtime/context.js.map +7 -6
- package/dist/src/runtime/processor.js +272 -35
- package/dist/src/runtime/processor.js.map +10 -9
- package/dist/src/runtime/reindex.d.ts +111 -10
- package/dist/src/runtime/reindex.js +263 -31
- package/dist/src/runtime/reindex.js.map +9 -8
- package/dist/src/runtime/reorg.d.ts +111 -10
- package/dist/src/runtime/reorg.js +268 -31
- package/dist/src/runtime/reorg.js.map +10 -9
- package/dist/src/runtime/replay.d.ts +3 -3
- package/dist/src/runtime/replay.js +30 -6
- package/dist/src/runtime/replay.js.map +4 -4
- package/dist/src/runtime/runner.d.ts +191 -17
- package/dist/src/runtime/source-matcher.d.ts +68 -8
- package/dist/src/runtime/source-matcher.js +30 -6
- package/dist/src/runtime/source-matcher.js.map +3 -3
- package/dist/src/runtime/stats.d.ts +3 -3
- package/dist/src/schema/index.d.ts +114 -13
- package/dist/src/schema/index.js +133 -20
- package/dist/src/schema/index.js.map +5 -5
- package/dist/src/service.js +272 -35
- package/dist/src/service.js.map +10 -9
- package/dist/src/testing/index.d.ts +667 -0
- package/dist/src/testing/index.js +1463 -0
- package/dist/src/testing/index.js.map +17 -0
- package/dist/src/triggers/index.d.ts +57 -6
- package/dist/src/types.d.ts +112 -11
- package/dist/src/validate.d.ts +129 -11
- package/dist/src/validate.js +117 -18
- package/dist/src/validate.js.map +3 -3
- package/package.json +9 -3
|
@@ -1,4 +1,73 @@
|
|
|
1
|
-
import { AbiContract } from "@secondlayer/stacks/clarity";
|
|
1
|
+
import { AbiContract as AbiContract2 } from "@secondlayer/stacks/clarity";
|
|
2
|
+
import { UnwrapResponse } from "@secondlayer/stacks/actions";
|
|
3
|
+
import { AbiContract, ExtractFunctionArgs, ExtractFunctionOutput, ExtractReadOnlyFunctions, ToCamelCase } from "@secondlayer/stacks/clarity";
|
|
4
|
+
/**
|
|
5
|
+
* How long a read stays valid.
|
|
6
|
+
*
|
|
7
|
+
* - `per-block` (default): pinned to the block being processed, cached under
|
|
8
|
+
* its `index_block_hash`. Sound for any read-only function — reindexing the
|
|
9
|
+
* same block always returns the same value, and a reorg-replaced block has a
|
|
10
|
+
* different id so it can't inherit the orphaned answer.
|
|
11
|
+
* - `contract-constant`: the caller asserts the value cannot change for this
|
|
12
|
+
* contract and these args (SIP-010 `get-decimals`/`get-symbol`), so it is
|
|
13
|
+
* resolved ONCE instead of once per block. This is the mode that makes a
|
|
14
|
+
* backfill affordable — under `per-block` every block is a distinct key and
|
|
15
|
+
* therefore a distinct RPC. Declaring it on a value that does change pins the
|
|
16
|
+
* first answer forever, so it is opt-in and never inferred.
|
|
17
|
+
*/
|
|
18
|
+
type ChainReadCacheMode = "per-block" | "contract-constant";
|
|
19
|
+
type ChainReadOptions = {
|
|
20
|
+
cache?: ChainReadCacheMode
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* The chain-read surface, fully typed by an ABI.
|
|
24
|
+
*
|
|
25
|
+
* Shaped like `getContract` from `@secondlayer/stacks` — the same ABI in, the
|
|
26
|
+
* same camelCased read methods out — minus `call`/`buildCall`: a handler
|
|
27
|
+
* indexes the chain, it never writes to it.
|
|
28
|
+
*
|
|
29
|
+
* Reached through {@link readContractAt} rather than off `ctx.client`
|
|
30
|
+
* directly: an ABI-generic method living on the context interface makes every
|
|
31
|
+
* structural comparison of a subgraph definition instantiate it, which exceeds
|
|
32
|
+
* TS's depth budget. At a call site the ABI is already concrete and it costs
|
|
33
|
+
* nothing.
|
|
34
|
+
*/
|
|
35
|
+
interface ChainReadClient {
|
|
36
|
+
/**
|
|
37
|
+
* Read-only methods of a contract, evaluated against the exact block being
|
|
38
|
+
* processed.
|
|
39
|
+
*
|
|
40
|
+
* Every call is pinned to the block's `index_block_hash`, so a handler is a
|
|
41
|
+
* pure function of its block: reindexing a range produces byte-identical
|
|
42
|
+
* rows. Reading at the node's tip instead would make the same reindex
|
|
43
|
+
* return different values — that is the corruption this refuses to allow,
|
|
44
|
+
* so a block whose id was never persisted throws rather than falling back.
|
|
45
|
+
*
|
|
46
|
+
* Results are memoized in Postgres, so a value is fetched once per block
|
|
47
|
+
* rather than once per event (or once per contract with
|
|
48
|
+
* `cache: "contract-constant"`).
|
|
49
|
+
*/
|
|
50
|
+
contract<const C extends AbiContract>(contractId: string, abi: C, options?: ChainReadOptions): {
|
|
51
|
+
read: ChainReadMethods<C>
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The camelCased read methods of an ABI. Spelled out rather than reused from
|
|
56
|
+
* `ContractInstance<C>["read"]`: that type is a conditional over the codegen
|
|
57
|
+
* brand, and evaluating it wherever a subgraph definition is structurally
|
|
58
|
+
* compared pushes TS past its instantiation depth.
|
|
59
|
+
*/
|
|
60
|
+
type ChainReadMethods<C extends AbiContract> = { [N in ExtractReadOnlyFunctions<C> as ToCamelCase<N>] : (args: ExtractFunctionArgs<C, N>) => Promise<UnwrapResponse<ExtractFunctionOutput<C, N>>> };
|
|
61
|
+
/**
|
|
62
|
+
* `ChainReadClient` with the ABI generic erased. Structurally cheap, so the
|
|
63
|
+
* base `SubgraphContext` can carry it; `TypedSubgraphContext` uses the real
|
|
64
|
+
* one and handlers get full inference.
|
|
65
|
+
*/
|
|
66
|
+
interface ErasedChainReadClient {
|
|
67
|
+
contract(contractId: string, abi: unknown, options?: ChainReadOptions): {
|
|
68
|
+
read: Record<string, (args?: Record<string, unknown>) => Promise<unknown>>
|
|
69
|
+
};
|
|
70
|
+
}
|
|
2
71
|
/** Supported column types for subgraph schemas */
|
|
3
72
|
type ColumnType = "text" | "uint" | "int" | "principal" | "boolean" | "timestamp" | "jsonb";
|
|
4
73
|
/** Column definition in a subgraph table */
|
|
@@ -25,15 +94,24 @@ interface SubgraphRelation {
|
|
|
25
94
|
/** Target column(s) the fields point at (a uniqueKeys entry on the target). */
|
|
26
95
|
referencedColumns: string[];
|
|
27
96
|
}
|
|
28
|
-
/**
|
|
97
|
+
/**
|
|
98
|
+
* Table definition within a subgraph schema.
|
|
99
|
+
*
|
|
100
|
+
* The array fields are `readonly` so a schema literal can be hoisted out of
|
|
101
|
+
* the `defineSubgraph()` call with `as const` (or {@link defineSchema}) and
|
|
102
|
+
* still typecheck — `as const` produces `readonly [readonly ["holder"]]`,
|
|
103
|
+
* which a mutable `string[][]` rejects. Without that, extracting a helper
|
|
104
|
+
* that takes `ctx` meant losing the entire typed surface, which is why the
|
|
105
|
+
* shipped starter reached for `ctx: any`.
|
|
106
|
+
*/
|
|
29
107
|
interface SubgraphTable {
|
|
30
108
|
columns: Record<string, SubgraphColumn>;
|
|
31
109
|
/** Composite indexes (each entry is an array of column names) */
|
|
32
|
-
indexes?: string[][];
|
|
110
|
+
indexes?: readonly (readonly string[])[];
|
|
33
111
|
/** Unique key constraints (each entry is an array of column names). Required for upsert. */
|
|
34
|
-
uniqueKeys?: string[][];
|
|
112
|
+
uniqueKeys?: readonly (readonly string[])[];
|
|
35
113
|
/** Foreign-key relations to other tables (for typed ORM joins). */
|
|
36
|
-
relations?: SubgraphRelation[];
|
|
114
|
+
relations?: readonly SubgraphRelation[];
|
|
37
115
|
}
|
|
38
116
|
/** Subgraph schema — maps table names to table definitions */
|
|
39
117
|
type SubgraphSchema = Record<string, SubgraphTable>;
|
|
@@ -68,6 +146,45 @@ interface StxLockFilter {
|
|
|
68
146
|
type TraitScope = {
|
|
69
147
|
trait?: string
|
|
70
148
|
};
|
|
149
|
+
/**
|
|
150
|
+
* Scope a source to a DYNAMIC address set discovered from another source's
|
|
151
|
+
* events, instead of a fixed contract list.
|
|
152
|
+
*
|
|
153
|
+
* The standard shape it unlocks: a router plus pools created after you
|
|
154
|
+
* deploy. `contractId` arrays cover a set you know at authoring time; a
|
|
155
|
+
* factory covers the set that grows. It is deliberately NOT tied to contract
|
|
156
|
+
* deployment — the address can come from any field of any event, so it also
|
|
157
|
+
* covers launchpad-minted tokens, DAO extension allowlists, and registry
|
|
158
|
+
* entries.
|
|
159
|
+
*
|
|
160
|
+
* ```ts
|
|
161
|
+
* sources: {
|
|
162
|
+
* registry: { type: "print_event", contractId: REGISTRY, topic: "pool-created" },
|
|
163
|
+
* swaps: {
|
|
164
|
+
* type: "print_event",
|
|
165
|
+
* topic: "swap",
|
|
166
|
+
* // every pool the registry has ever announced, including new ones
|
|
167
|
+
* factory: { from: "registry", field: "data.pool" },
|
|
168
|
+
* },
|
|
169
|
+
* }
|
|
170
|
+
* ```
|
|
171
|
+
*
|
|
172
|
+
* Two guarantees the runtime provides:
|
|
173
|
+
* - **Same-block ordering** — a contract discovered in block N receives its
|
|
174
|
+
* own block-N events. Discovery runs before matching, within the block.
|
|
175
|
+
* - **Reorg-scoped** — the discovered set is stamped with the block that
|
|
176
|
+
* revealed it and rolled back with everything else on a fork, so an address
|
|
177
|
+
* announced on an orphaned chain does not linger forever.
|
|
178
|
+
*/
|
|
179
|
+
interface FactoryScope {
|
|
180
|
+
factory?: {
|
|
181
|
+
/** Source name whose events reveal the addresses. */
|
|
182
|
+
from: string
|
|
183
|
+
/** Dotted path to the address on that source's payload
|
|
184
|
+
* (e.g. `"data.pool"`, `"contractId"`, `"data.token.id"`). */
|
|
185
|
+
field: string
|
|
186
|
+
};
|
|
187
|
+
}
|
|
71
188
|
/** FT event filters */
|
|
72
189
|
interface FtTransferFilter extends TraitScope {
|
|
73
190
|
type: "ft_transfer";
|
|
@@ -106,9 +223,19 @@ interface NftBurnFilter extends TraitScope {
|
|
|
106
223
|
sender?: string;
|
|
107
224
|
}
|
|
108
225
|
/** Contract event filters */
|
|
109
|
-
interface ContractCallFilter extends TraitScope {
|
|
226
|
+
interface ContractCallFilter extends TraitScope, FactoryScope {
|
|
110
227
|
type: "contract_call";
|
|
111
|
-
|
|
228
|
+
/**
|
|
229
|
+
* One contract id, or a SET of them (max 20) — the standard DeFi shape is
|
|
230
|
+
* a router plus N pools, which used to cost N near-identical sources and
|
|
231
|
+
* handlers, or the `"*"` catch-all that erases per-source typing. Payload
|
|
232
|
+
* types are identical per `type`, so one handler serves the set and
|
|
233
|
+
* `event.contractId` disambiguates.
|
|
234
|
+
*
|
|
235
|
+
* Supports `*` wildcards (e.g. `"SP1ABC….pool-*"`). Mutually exclusive
|
|
236
|
+
* with `trait`, which resolves its own set per block.
|
|
237
|
+
*/
|
|
238
|
+
contractId?: string | readonly string[];
|
|
112
239
|
functionName?: string;
|
|
113
240
|
caller?: string;
|
|
114
241
|
/**
|
|
@@ -117,7 +244,7 @@ interface ContractCallFilter extends TraitScope {
|
|
|
117
244
|
* definition and used at runtime to decode args by name. Omit to keep
|
|
118
245
|
* `event.args` as a positional `unknown[]`.
|
|
119
246
|
*/
|
|
120
|
-
abi?:
|
|
247
|
+
abi?: AbiContract2;
|
|
121
248
|
}
|
|
122
249
|
interface ContractDeployFilter {
|
|
123
250
|
type: "contract_deploy";
|
|
@@ -146,9 +273,11 @@ type PrintField = ColumnType | {
|
|
|
146
273
|
} | {
|
|
147
274
|
list: PrintField
|
|
148
275
|
};
|
|
149
|
-
interface PrintEventFilter extends TraitScope {
|
|
276
|
+
interface PrintEventFilter extends TraitScope, FactoryScope {
|
|
150
277
|
type: "print_event";
|
|
151
|
-
|
|
278
|
+
/** One contract id, or a set of them (max 20). Supports `*` wildcards.
|
|
279
|
+
* Mutually exclusive with `trait`. */
|
|
280
|
+
contractId?: string | readonly string[];
|
|
152
281
|
topic?: string;
|
|
153
282
|
/**
|
|
154
283
|
* Per-topic field schema. When declared, the handler's `event` is a
|
|
@@ -186,8 +315,21 @@ interface SubgraphContext {
|
|
|
186
315
|
hash: string
|
|
187
316
|
timestamp: number
|
|
188
317
|
burnBlockHeight: number
|
|
318
|
+
/** Nakamoto StacksBlockId, when the block row carries one. */
|
|
319
|
+
indexBlockHash?: string | null
|
|
189
320
|
};
|
|
190
321
|
tx: TxMeta;
|
|
322
|
+
/**
|
|
323
|
+
* Read-only contract calls pinned to this block. Cached, so the same
|
|
324
|
+
* (contract, function, args) is fetched once per block rather than once
|
|
325
|
+
* per event.
|
|
326
|
+
*
|
|
327
|
+
* Erased here on purpose: this interface is compared structurally wherever
|
|
328
|
+
* a subgraph definition is passed around, and carrying an ABI-generic
|
|
329
|
+
* method pushes those comparisons past TS's instantiation depth. Handlers
|
|
330
|
+
* receive `TypedSubgraphContext`, which exposes the fully typed version.
|
|
331
|
+
*/
|
|
332
|
+
client: ErasedChainReadClient;
|
|
191
333
|
insert(table: string, row: Record<string, unknown>): void;
|
|
192
334
|
update(table: string, where: Record<string, unknown>, set: Record<string, unknown>): void;
|
|
193
335
|
upsert(table: string, key: Record<string, unknown>, row: Record<string, unknown>): void;
|
|
@@ -240,9 +382,16 @@ interface SubgraphDefinition {
|
|
|
240
382
|
/** Handler functions — keys must match source names (or "*" for catch-all) */
|
|
241
383
|
handlers: Record<string, SubgraphHandler>;
|
|
242
384
|
}
|
|
243
|
-
import { Database } from "@secondlayer/shared/db";
|
|
244
|
-
import { Kysely, Transaction } from "kysely";
|
|
245
|
-
type AnyDb =
|
|
385
|
+
import { Database as Database2 } from "@secondlayer/shared/db";
|
|
386
|
+
import { Kysely as Kysely2, Transaction } from "kysely";
|
|
387
|
+
type AnyDb = Kysely2<Database2> | Transaction<Database2>;
|
|
388
|
+
interface WriteOp {
|
|
389
|
+
kind: "insert" | "update" | "delete" | "increment";
|
|
390
|
+
table: string;
|
|
391
|
+
data: Record<string, unknown>;
|
|
392
|
+
/** For update: SET clause. For increment: column → signed delta. */
|
|
393
|
+
set?: Record<string, unknown>;
|
|
394
|
+
}
|
|
246
395
|
interface FlushWrite {
|
|
247
396
|
op: "insert" | "update" | "delete";
|
|
248
397
|
table: string;
|
|
@@ -264,6 +413,13 @@ interface BlockMeta {
|
|
|
264
413
|
hash: string;
|
|
265
414
|
timestamp: number;
|
|
266
415
|
burnBlockHeight: number;
|
|
416
|
+
/**
|
|
417
|
+
* Nakamoto StacksBlockId — the identifier `?tip=` accepts, and the only way
|
|
418
|
+
* to pin a read-only call to THIS block. Null on rows ingested before it
|
|
419
|
+
* was persisted; `ctx.client.readOnly` refuses to run rather than silently
|
|
420
|
+
* reading at the node's tip.
|
|
421
|
+
*/
|
|
422
|
+
indexBlockHash?: string | null;
|
|
267
423
|
}
|
|
268
424
|
interface TxMeta2 {
|
|
269
425
|
txId: string;
|
|
@@ -289,7 +445,10 @@ declare class SubgraphContext2 {
|
|
|
289
445
|
private readonly db;
|
|
290
446
|
private readonly pgSchemaName;
|
|
291
447
|
private readonly subgraphSchema;
|
|
292
|
-
|
|
448
|
+
/** Pending writes for this block. `protected` so the in-memory test
|
|
449
|
+
* context (`@secondlayer/subgraphs/testing`) can materialize them through
|
|
450
|
+
* the SAME overlay this class uses — never a second implementation. */
|
|
451
|
+
protected readonly ops: WriteOp[];
|
|
293
452
|
/**
|
|
294
453
|
* BYO data plane: handler writes land in a user-owned DB whose flush can't
|
|
295
454
|
* share the managed block transaction, so a crash replays the block. When
|
|
@@ -305,7 +464,15 @@ declare class SubgraphContext2 {
|
|
|
305
464
|
* journal would just be churn the pruner deletes.
|
|
306
465
|
*/
|
|
307
466
|
private readonly journal;
|
|
467
|
+
/** Memoized per block; see the `client` getter. */
|
|
468
|
+
protected _client?: ChainReadClient;
|
|
308
469
|
constructor(db: AnyDb, pgSchemaName: string, subgraphSchema: SubgraphSchema, block: BlockMeta, tx: TxMeta2, byo?: boolean, journal?: boolean);
|
|
470
|
+
/**
|
|
471
|
+
* Chain reads pinned to THIS block. Lazily built so a handler that never
|
|
472
|
+
* reads never touches the node config, and so the in-memory test context
|
|
473
|
+
* can override it without a node.
|
|
474
|
+
*/
|
|
475
|
+
get client(): ChainReadClient;
|
|
309
476
|
get tx(): TxMeta2;
|
|
310
477
|
/** Update the current transaction context (used by runner between events) */
|
|
311
478
|
setTx(tx: TxMeta2): void;
|
|
@@ -340,6 +507,13 @@ declare class SubgraphContext2 {
|
|
|
340
507
|
patchOrInsert(table: string, key: Record<string, unknown>, row: Record<string, ComputedValue>): Promise<void>;
|
|
341
508
|
/** Format a bigint amount with decimal places */
|
|
342
509
|
formatUnits(value: bigint, decimals: number): string;
|
|
510
|
+
/**
|
|
511
|
+
* Committed rows matching `where`, BEFORE the pending-ops overlay. The one
|
|
512
|
+
* seam a different backing store replaces (the in-memory test context);
|
|
513
|
+
* everything above it — validation, overlay, op queueing, flush semantics
|
|
514
|
+
* — stays here so the two can't diverge.
|
|
515
|
+
*/
|
|
516
|
+
protected readRows(table: string, where: Record<string, unknown>, limit?: number): Promise<Record<string, unknown>[]>;
|
|
343
517
|
findOne(table: string, where: Record<string, unknown>): Promise<Record<string, unknown> | null>;
|
|
344
518
|
findMany(table: string, where: Record<string, unknown>): Promise<Record<string, unknown>[]>;
|
|
345
519
|
/**
|
|
@@ -349,10 +523,10 @@ declare class SubgraphContext2 {
|
|
|
349
523
|
* apply by where-match. Overlaid rows synthesized from pending inserts
|
|
350
524
|
* lack DB-generated columns (_id, _created_at).
|
|
351
525
|
*/
|
|
352
|
-
|
|
353
|
-
|
|
526
|
+
protected overlayOne(table: string, where: Record<string, unknown>, dbRow: Record<string, unknown> | null): Record<string, unknown> | null;
|
|
527
|
+
protected overlayMany(table: string, where: Record<string, unknown>, dbRows: Record<string, unknown>[]): Record<string, unknown>[];
|
|
354
528
|
/** Apply one pending op to a candidate row state (null = row absent). */
|
|
355
|
-
|
|
529
|
+
protected applyOpToRow(op: WriteOp, row: Record<string, unknown> | null, where: Record<string, unknown>): Record<string, unknown> | null;
|
|
356
530
|
count(table: string, where?: Record<string, unknown>): Promise<number>;
|
|
357
531
|
sum(table: string, column: string, where?: Record<string, unknown>): Promise<bigint>;
|
|
358
532
|
min(table: string, column: string, where?: Record<string, unknown>): Promise<bigint | null>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AbiContract } from "@secondlayer/stacks/clarity";
|
|
1
|
+
import { AbiContract as AbiContract2 } from "@secondlayer/stacks/clarity";
|
|
2
2
|
/** Supported column types for subgraph schemas */
|
|
3
3
|
type ColumnType = "text" | "uint" | "int" | "principal" | "boolean" | "timestamp" | "jsonb";
|
|
4
4
|
/** STX event filters */
|
|
@@ -32,6 +32,45 @@ interface StxLockFilter {
|
|
|
32
32
|
type TraitScope = {
|
|
33
33
|
trait?: string
|
|
34
34
|
};
|
|
35
|
+
/**
|
|
36
|
+
* Scope a source to a DYNAMIC address set discovered from another source's
|
|
37
|
+
* events, instead of a fixed contract list.
|
|
38
|
+
*
|
|
39
|
+
* The standard shape it unlocks: a router plus pools created after you
|
|
40
|
+
* deploy. `contractId` arrays cover a set you know at authoring time; a
|
|
41
|
+
* factory covers the set that grows. It is deliberately NOT tied to contract
|
|
42
|
+
* deployment — the address can come from any field of any event, so it also
|
|
43
|
+
* covers launchpad-minted tokens, DAO extension allowlists, and registry
|
|
44
|
+
* entries.
|
|
45
|
+
*
|
|
46
|
+
* ```ts
|
|
47
|
+
* sources: {
|
|
48
|
+
* registry: { type: "print_event", contractId: REGISTRY, topic: "pool-created" },
|
|
49
|
+
* swaps: {
|
|
50
|
+
* type: "print_event",
|
|
51
|
+
* topic: "swap",
|
|
52
|
+
* // every pool the registry has ever announced, including new ones
|
|
53
|
+
* factory: { from: "registry", field: "data.pool" },
|
|
54
|
+
* },
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* Two guarantees the runtime provides:
|
|
59
|
+
* - **Same-block ordering** — a contract discovered in block N receives its
|
|
60
|
+
* own block-N events. Discovery runs before matching, within the block.
|
|
61
|
+
* - **Reorg-scoped** — the discovered set is stamped with the block that
|
|
62
|
+
* revealed it and rolled back with everything else on a fork, so an address
|
|
63
|
+
* announced on an orphaned chain does not linger forever.
|
|
64
|
+
*/
|
|
65
|
+
interface FactoryScope {
|
|
66
|
+
factory?: {
|
|
67
|
+
/** Source name whose events reveal the addresses. */
|
|
68
|
+
from: string
|
|
69
|
+
/** Dotted path to the address on that source's payload
|
|
70
|
+
* (e.g. `"data.pool"`, `"contractId"`, `"data.token.id"`). */
|
|
71
|
+
field: string
|
|
72
|
+
};
|
|
73
|
+
}
|
|
35
74
|
/** FT event filters */
|
|
36
75
|
interface FtTransferFilter extends TraitScope {
|
|
37
76
|
type: "ft_transfer";
|
|
@@ -70,9 +109,19 @@ interface NftBurnFilter extends TraitScope {
|
|
|
70
109
|
sender?: string;
|
|
71
110
|
}
|
|
72
111
|
/** Contract event filters */
|
|
73
|
-
interface ContractCallFilter extends TraitScope {
|
|
112
|
+
interface ContractCallFilter extends TraitScope, FactoryScope {
|
|
74
113
|
type: "contract_call";
|
|
75
|
-
|
|
114
|
+
/**
|
|
115
|
+
* One contract id, or a SET of them (max 20) — the standard DeFi shape is
|
|
116
|
+
* a router plus N pools, which used to cost N near-identical sources and
|
|
117
|
+
* handlers, or the `"*"` catch-all that erases per-source typing. Payload
|
|
118
|
+
* types are identical per `type`, so one handler serves the set and
|
|
119
|
+
* `event.contractId` disambiguates.
|
|
120
|
+
*
|
|
121
|
+
* Supports `*` wildcards (e.g. `"SP1ABC….pool-*"`). Mutually exclusive
|
|
122
|
+
* with `trait`, which resolves its own set per block.
|
|
123
|
+
*/
|
|
124
|
+
contractId?: string | readonly string[];
|
|
76
125
|
functionName?: string;
|
|
77
126
|
caller?: string;
|
|
78
127
|
/**
|
|
@@ -81,7 +130,7 @@ interface ContractCallFilter extends TraitScope {
|
|
|
81
130
|
* definition and used at runtime to decode args by name. Omit to keep
|
|
82
131
|
* `event.args` as a positional `unknown[]`.
|
|
83
132
|
*/
|
|
84
|
-
abi?:
|
|
133
|
+
abi?: AbiContract2;
|
|
85
134
|
}
|
|
86
135
|
interface ContractDeployFilter {
|
|
87
136
|
type: "contract_deploy";
|
|
@@ -110,9 +159,11 @@ type PrintField = ColumnType | {
|
|
|
110
159
|
} | {
|
|
111
160
|
list: PrintField
|
|
112
161
|
};
|
|
113
|
-
interface PrintEventFilter extends TraitScope {
|
|
162
|
+
interface PrintEventFilter extends TraitScope, FactoryScope {
|
|
114
163
|
type: "print_event";
|
|
115
|
-
|
|
164
|
+
/** One contract id, or a set of them (max 20). Supports `*` wildcards.
|
|
165
|
+
* Mutually exclusive with `trait`. */
|
|
166
|
+
contractId?: string | readonly string[];
|
|
116
167
|
topic?: string;
|
|
117
168
|
/**
|
|
118
169
|
* Per-topic field schema. When declared, the handler's `event` is a
|
|
@@ -157,8 +208,17 @@ type EventRecord = {
|
|
|
157
208
|
};
|
|
158
209
|
type TraitContracts = Map<string, ReadonlySet<string>>;
|
|
159
210
|
/**
|
|
211
|
+
* True when a filter's optional `trait` admits this contract: no trait → always
|
|
212
|
+
* allowed; trait set → the contract must be in the resolved conforming set.
|
|
213
|
+
*/
|
|
214
|
+
/** Addresses discovered per source name (factory sets), resolved by the
|
|
215
|
+
* caller as-of the block being processed. */
|
|
216
|
+
type FactoryContracts = ReadonlyMap<string, ReadonlySet<string>>;
|
|
217
|
+
/** Read a dotted path off a decoded payload (`"data.pool"`). */
|
|
218
|
+
declare function readPath(payload: unknown, path: string): unknown;
|
|
219
|
+
/**
|
|
160
220
|
* Match named filters against a block's transactions and events.
|
|
161
221
|
* Returns matches with sourceName = the object key from sources.
|
|
162
222
|
*/
|
|
163
|
-
declare function matchSources(sources: Record<string, SubgraphFilter>, transactions: TxRecord[], events: EventRecord[], traitContracts?: TraitContracts): MatchedTx[];
|
|
164
|
-
export { matchSources, TraitContracts, MatchedTx };
|
|
223
|
+
declare function matchSources(sources: Record<string, SubgraphFilter>, transactions: TxRecord[], events: EventRecord[], traitContracts?: TraitContracts, factoryContracts?: FactoryContracts): MatchedTx[];
|
|
224
|
+
export { readPath, matchSources, TxRecord, TraitContracts, MatchedTx, FactoryContracts, EventRecord };
|
|
@@ -3,6 +3,9 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
3
3
|
|
|
4
4
|
// src/runtime/source-matcher.ts
|
|
5
5
|
var patternCache = new Map;
|
|
6
|
+
function matchAny(value, pattern) {
|
|
7
|
+
return Array.isArray(pattern) ? pattern.some((p) => matchPattern(value, p)) : matchPattern(value, pattern);
|
|
8
|
+
}
|
|
6
9
|
function matchPattern(value, pattern) {
|
|
7
10
|
if (!pattern.includes("*"))
|
|
8
11
|
return value === pattern;
|
|
@@ -15,6 +18,22 @@ function matchPattern(value, pattern) {
|
|
|
15
18
|
return re.test(value);
|
|
16
19
|
}
|
|
17
20
|
var EMPTY_SET = new Set;
|
|
21
|
+
function factoryAllows(filter, contractId, factoryContracts) {
|
|
22
|
+
if (!filter.factory)
|
|
23
|
+
return true;
|
|
24
|
+
if (!contractId)
|
|
25
|
+
return false;
|
|
26
|
+
return (factoryContracts.get(filter.factory.from) ?? EMPTY_SET).has(contractId);
|
|
27
|
+
}
|
|
28
|
+
function readPath(payload, path) {
|
|
29
|
+
let cur = payload;
|
|
30
|
+
for (const key of path.split(".")) {
|
|
31
|
+
if (cur === null || typeof cur !== "object")
|
|
32
|
+
return;
|
|
33
|
+
cur = cur[key];
|
|
34
|
+
}
|
|
35
|
+
return cur;
|
|
36
|
+
}
|
|
18
37
|
function traitAllows(filter, contractId, traitContracts) {
|
|
19
38
|
const trait = filter.trait;
|
|
20
39
|
if (!trait)
|
|
@@ -26,7 +45,7 @@ function traitAllows(filter, contractId, traitContracts) {
|
|
|
26
45
|
function assetContract(assetId) {
|
|
27
46
|
return assetId?.split("::")[0];
|
|
28
47
|
}
|
|
29
|
-
function matchFilter(filter, transactions, eventsByTx, traitContracts) {
|
|
48
|
+
function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts = new Map) {
|
|
30
49
|
const results = [];
|
|
31
50
|
switch (filter.type) {
|
|
32
51
|
case "stx_transfer":
|
|
@@ -153,9 +172,11 @@ function matchFilter(filter, transactions, eventsByTx, traitContracts) {
|
|
|
153
172
|
if (tx.type !== "contract_call")
|
|
154
173
|
continue;
|
|
155
174
|
if (filter.contractId) {
|
|
156
|
-
if (!tx.contract_id || !
|
|
175
|
+
if (!tx.contract_id || !matchAny(tx.contract_id, filter.contractId))
|
|
157
176
|
continue;
|
|
158
177
|
}
|
|
178
|
+
if (!factoryAllows(filter, tx.contract_id, factoryContracts))
|
|
179
|
+
continue;
|
|
159
180
|
if (filter.functionName) {
|
|
160
181
|
if (!tx.function_name || !matchPattern(tx.function_name, filter.functionName))
|
|
161
182
|
continue;
|
|
@@ -202,11 +223,13 @@ function matchFilter(filter, transactions, eventsByTx, traitContracts) {
|
|
|
202
223
|
return false;
|
|
203
224
|
const printContractId = data.contract_identifier ?? data.contract_id;
|
|
204
225
|
if (filter.contractId) {
|
|
205
|
-
if (!printContractId || !
|
|
226
|
+
if (!printContractId || !matchAny(printContractId, filter.contractId))
|
|
206
227
|
return false;
|
|
207
228
|
}
|
|
208
229
|
if (!traitAllows(filter, printContractId, traitContracts))
|
|
209
230
|
return false;
|
|
231
|
+
if (!factoryAllows(filter, printContractId, factoryContracts))
|
|
232
|
+
return false;
|
|
210
233
|
return true;
|
|
211
234
|
});
|
|
212
235
|
if (matched.length > 0) {
|
|
@@ -218,7 +241,7 @@ function matchFilter(filter, transactions, eventsByTx, traitContracts) {
|
|
|
218
241
|
}
|
|
219
242
|
return results;
|
|
220
243
|
}
|
|
221
|
-
function matchSources(sources, transactions, events, traitContracts = new Map) {
|
|
244
|
+
function matchSources(sources, transactions, events, traitContracts = new Map, factoryContracts = new Map) {
|
|
222
245
|
const eventsByTx = new Map;
|
|
223
246
|
for (const event of events) {
|
|
224
247
|
const list = eventsByTx.get(event.tx_id) ?? [];
|
|
@@ -228,7 +251,7 @@ function matchSources(sources, transactions, events, traitContracts = new Map) {
|
|
|
228
251
|
const seen = new Set;
|
|
229
252
|
const results = [];
|
|
230
253
|
for (const [sourceName, filter] of Object.entries(sources)) {
|
|
231
|
-
const matches = matchFilter(filter, transactions, eventsByTx, traitContracts);
|
|
254
|
+
const matches = matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts);
|
|
232
255
|
for (const match of matches) {
|
|
233
256
|
const dedupeKey = `${match.tx.tx_id}:${sourceName}`;
|
|
234
257
|
if (!seen.has(dedupeKey)) {
|
|
@@ -240,8 +263,9 @@ function matchSources(sources, transactions, events, traitContracts = new Map) {
|
|
|
240
263
|
return results;
|
|
241
264
|
}
|
|
242
265
|
export {
|
|
266
|
+
readPath,
|
|
243
267
|
matchSources
|
|
244
268
|
};
|
|
245
269
|
|
|
246
|
-
//# debugId=
|
|
270
|
+
//# debugId=3A7F11E035AAF9A264756E2164756E21
|
|
247
271
|
//# sourceMappingURL=source-matcher.js.map
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/runtime/source-matcher.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import type { SubgraphFilter } from \"../types.ts\";\n\nexport interface MatchedTx {\n\ttx: TxRecord;\n\tevents: EventRecord[];\n\t/** Source object key — used for handler dispatch */\n\tsourceName: string;\n}\n\ntype TxRecord = {\n\ttx_id: string;\n\ttype: string;\n\tsender: string;\n\tstatus: string;\n\t/** Position within the block — the runner sorts dispatch into chain order. */\n\ttx_index?: number;\n\tcontract_id?: string | null;\n\tfunction_name?: string | null;\n\tfunction_args?: unknown | null;\n\traw_result?: string | null;\n};\n\ntype EventRecord = {\n\tid: string;\n\ttx_id: string;\n\ttype: string;\n\tevent_index: number;\n\tdata: unknown;\n};\n\n// ── Wildcard matching (shared with v1) ──────────────────────────────\n\nconst patternCache = new Map<string, RegExp>();\n\nfunction matchPattern(value: string, pattern: string): boolean {\n\tif (!pattern.includes(\"*\")) return value === pattern;\n\tlet re = patternCache.get(pattern);\n\tif (!re) {\n\t\tconst regex = pattern\n\t\t\t.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\t\t\t.replace(/\\*/g, \".*\");\n\t\tre = new RegExp(`^${regex}$`);\n\t\tpatternCache.set(pattern, re);\n\t}\n\treturn re.test(value);\n}\n\n// Trait → set of conforming contract IDs, resolved per block by the caller\n// (block-processor) from the contract registry. Kept as injected data so this\n// module stays pure/sync/DB-less.\nexport type TraitContracts = Map<string, ReadonlySet<string>>;\nconst EMPTY_SET: ReadonlySet<string> = new Set();\n\n/**\n * True when a filter's optional `trait` admits this contract: no trait → always\n * allowed; trait set → the contract must be in the resolved conforming set.\n */\nfunction traitAllows(\n\tfilter: SubgraphFilter,\n\tcontractId: string | undefined | null,\n\ttraitContracts: TraitContracts,\n): boolean {\n\tconst trait = (filter as { trait?: string }).trait;\n\tif (!trait) return true;\n\tif (!contractId) return false;\n\treturn (traitContracts.get(trait) ?? EMPTY_SET).has(contractId);\n}\n\n/** Extract the contract id from an asset identifier (`<contract>::<token>`). */\nfunction assetContract(assetId: string | undefined): string | undefined {\n\treturn assetId?.split(\"::\")[0];\n}\n\n// ── Per-filter-type matchers ────────────────────────────────────────\n\nfunction matchFilter(\n\tfilter: SubgraphFilter,\n\ttransactions: TxRecord[],\n\teventsByTx: Map<string, EventRecord[]>,\n\ttraitContracts: TraitContracts,\n): { tx: TxRecord; events: EventRecord[] }[] {\n\tconst results: { tx: TxRecord; events: EventRecord[] }[] = [];\n\n\tswitch (filter.type) {\n\t\t// ── STX events ──\n\t\tcase \"stx_transfer\":\n\t\tcase \"stx_mint\":\n\t\tcase \"stx_burn\":\n\t\tcase \"stx_lock\": {\n\t\t\tconst eventType = `${filter.type}_event`;\n\t\t\tfor (const tx of transactions) {\n\t\t\t\tconst txEvents = eventsByTx.get(tx.tx_id) ?? [];\n\t\t\t\tconst matched = txEvents.filter((e) => e.type === eventType);\n\t\t\t\tif (matched.length === 0) continue;\n\n\t\t\t\t// Apply address filters\n\t\t\t\tconst filtered = matched.filter((e) => {\n\t\t\t\t\tconst data = e.data as Record<string, unknown> | null;\n\t\t\t\t\tif (!data) return false;\n\t\t\t\t\tif (\"sender\" in filter && filter.sender) {\n\t\t\t\t\t\tif (!matchPattern(data.sender as string, filter.sender))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (\"recipient\" in filter && filter.recipient) {\n\t\t\t\t\t\tif (!matchPattern(data.recipient as string, filter.recipient))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (\"lockedAddress\" in filter && filter.lockedAddress) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t!matchPattern(data.locked_address as string, filter.lockedAddress)\n\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\t// Amount filters\n\t\t\t\t\tif (\"minAmount\" in filter && filter.minAmount !== undefined) {\n\t\t\t\t\t\tconst amount = BigInt(\n\t\t\t\t\t\t\t(data.amount ?? data.locked_amount ?? \"0\") as string,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (amount < filter.minAmount) return false;\n\t\t\t\t\t}\n\t\t\t\t\tif (\n\t\t\t\t\t\t\"maxAmount\" in filter &&\n\t\t\t\t\t\t(filter as { maxAmount?: bigint }).maxAmount !== undefined\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst amount = BigInt((data.amount ?? \"0\") as string);\n\t\t\t\t\t\tif (amount > (filter as { maxAmount: bigint }).maxAmount)\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\n\t\t\t\tif (filtered.length > 0) {\n\t\t\t\t\tresults.push({ tx, events: filtered });\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t// ── FT events ──\n\t\tcase \"ft_transfer\":\n\t\tcase \"ft_mint\":\n\t\tcase \"ft_burn\": {\n\t\t\tconst eventType = `${filter.type}_event`;\n\t\t\tfor (const tx of transactions) {\n\t\t\t\tconst txEvents = eventsByTx.get(tx.tx_id) ?? [];\n\t\t\t\tconst matched = txEvents.filter((e) => {\n\t\t\t\t\tif (e.type !== eventType) return false;\n\t\t\t\t\tconst data = e.data as Record<string, unknown> | null;\n\t\t\t\t\tif (!data) return false;\n\n\t\t\t\t\t// Asset identifier filter\n\t\t\t\t\tif (filter.assetIdentifier) {\n\t\t\t\t\t\tconst assetId = data.asset_identifier as string | undefined;\n\t\t\t\t\t\tif (!assetId || !matchPattern(assetId, filter.assetIdentifier))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\t// Trait scope — the asset's contract must conform.\n\t\t\t\t\tif (\n\t\t\t\t\t\t!traitAllows(\n\t\t\t\t\t\t\tfilter,\n\t\t\t\t\t\t\tassetContract(data.asset_identifier as string | undefined),\n\t\t\t\t\t\t\ttraitContracts,\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t// Address filters\n\t\t\t\t\tif (\"sender\" in filter && filter.sender) {\n\t\t\t\t\t\tif (!matchPattern(data.sender as string, filter.sender))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (\"recipient\" in filter && filter.recipient) {\n\t\t\t\t\t\tif (!matchPattern(data.recipient as string, filter.recipient))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\t// Amount filter\n\t\t\t\t\tif (filter.minAmount !== undefined) {\n\t\t\t\t\t\tconst amount = BigInt((data.amount ?? \"0\") as string);\n\t\t\t\t\t\tif (amount < filter.minAmount) return false;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\n\t\t\t\tif (matched.length > 0) {\n\t\t\t\t\tresults.push({ tx, events: matched });\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t// ── NFT events ──\n\t\tcase \"nft_transfer\":\n\t\tcase \"nft_mint\":\n\t\tcase \"nft_burn\": {\n\t\t\tconst eventType = `${filter.type}_event`;\n\t\t\tfor (const tx of transactions) {\n\t\t\t\tconst txEvents = eventsByTx.get(tx.tx_id) ?? [];\n\t\t\t\tconst matched = txEvents.filter((e) => {\n\t\t\t\t\tif (e.type !== eventType) return false;\n\t\t\t\t\tconst data = e.data as Record<string, unknown> | null;\n\t\t\t\t\tif (!data) return false;\n\n\t\t\t\t\tif (filter.assetIdentifier) {\n\t\t\t\t\t\tconst assetId = data.asset_identifier as string | undefined;\n\t\t\t\t\t\tif (!assetId || !matchPattern(assetId, filter.assetIdentifier))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (\n\t\t\t\t\t\t!traitAllows(\n\t\t\t\t\t\t\tfilter,\n\t\t\t\t\t\t\tassetContract(data.asset_identifier as string | undefined),\n\t\t\t\t\t\t\ttraitContracts,\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t\t\treturn false;\n\t\t\t\t\tif (\"sender\" in filter && filter.sender) {\n\t\t\t\t\t\tif (!matchPattern(data.sender as string, filter.sender))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (\"recipient\" in filter && filter.recipient) {\n\t\t\t\t\t\tif (!matchPattern(data.recipient as string, filter.recipient))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\n\t\t\t\tif (matched.length > 0) {\n\t\t\t\t\tresults.push({ tx, events: matched });\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t// ── Contract call ──\n\t\tcase \"contract_call\": {\n\t\t\tfor (const tx of transactions) {\n\t\t\t\tif (tx.type !== \"contract_call\") continue;\n\n\t\t\t\t// Contract filter\n\t\t\t\tif (filter.contractId) {\n\t\t\t\t\tif (\n\t\t\t\t\t\t!tx.contract_id ||\n\t\t\t\t\t\t!matchPattern(tx.contract_id, filter.contractId)\n\t\t\t\t\t)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// Function filter\n\t\t\t\tif (filter.functionName) {\n\t\t\t\t\tif (\n\t\t\t\t\t\t!tx.function_name ||\n\t\t\t\t\t\t!matchPattern(tx.function_name, filter.functionName)\n\t\t\t\t\t)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// Caller filter\n\t\t\t\tif (filter.caller) {\n\t\t\t\t\tif (!matchPattern(tx.sender, filter.caller)) continue;\n\t\t\t\t}\n\t\t\t\t// Trait scope — the called contract must conform.\n\t\t\t\tif (!traitAllows(filter, tx.contract_id, traitContracts)) continue;\n\n\t\t\t\tconst txEvents = eventsByTx.get(tx.tx_id) ?? [];\n\t\t\t\tresults.push({ tx, events: txEvents });\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t// ── Contract deploy ──\n\t\tcase \"contract_deploy\": {\n\t\t\tfor (const tx of transactions) {\n\t\t\t\tif (tx.type !== \"smart_contract\") continue;\n\n\t\t\t\tif (filter.deployer) {\n\t\t\t\t\tif (!matchPattern(tx.sender, filter.deployer)) continue;\n\t\t\t\t}\n\t\t\t\tif (filter.contractName) {\n\t\t\t\t\tconst name = tx.contract_id?.split(\".\")[1] ?? \"\";\n\t\t\t\t\tif (!matchPattern(name, filter.contractName)) continue;\n\t\t\t\t}\n\n\t\t\t\tconst txEvents = eventsByTx.get(tx.tx_id) ?? [];\n\t\t\t\tresults.push({ tx, events: txEvents });\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t// ── Print event ──\n\t\tcase \"print_event\": {\n\t\t\tfor (const tx of transactions) {\n\t\t\t\tconst txEvents = eventsByTx.get(tx.tx_id) ?? [];\n\t\t\t\tconst matched = txEvents.filter((e) => {\n\t\t\t\t\tif (e.type !== \"smart_contract_event\" && e.type !== \"contract_event\")\n\t\t\t\t\t\treturn false;\n\t\t\t\t\tconst data = e.data as Record<string, unknown> | null;\n\t\t\t\t\tif (!data) return false;\n\t\t\t\t\tif (data.topic !== \"print\") return false;\n\n\t\t\t\t\t// Contract filter — events store the contract under either\n\t\t\t\t\t// `contract_identifier` (legacy smart_contract_event payload)\n\t\t\t\t\t// or `contract_id` (current contract_event payload). Mirror\n\t\t\t\t\t// the streams query which checks both shapes.\n\t\t\t\t\tconst printContractId =\n\t\t\t\t\t\t(data.contract_identifier as string | undefined) ??\n\t\t\t\t\t\t(data.contract_id as string | undefined);\n\t\t\t\t\tif (filter.contractId) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t!printContractId ||\n\t\t\t\t\t\t\t!matchPattern(printContractId, filter.contractId)\n\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (!traitAllows(filter, printContractId, traitContracts))\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t// Topic filter — check the decoded Clarity value's topic field\n\t\t\t\t\t// At this stage data.value is still raw hex; topic filtering happens\n\t\t\t\t\t// after decode in the runner. For now, skip topic filtering here.\n\t\t\t\t\t// The runner will filter by topic after decoding.\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\n\t\t\t\tif (matched.length > 0) {\n\t\t\t\t\tresults.push({ tx, events: matched });\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn results;\n}\n\n/**\n * Match named filters against a block's transactions and events.\n * Returns matches with sourceName = the object key from sources.\n */\nexport function matchSources(\n\tsources: Record<string, SubgraphFilter>,\n\ttransactions: TxRecord[],\n\tevents: EventRecord[],\n\ttraitContracts: TraitContracts = new Map(),\n): MatchedTx[] {\n\t// Index events by txId\n\tconst eventsByTx = new Map<string, EventRecord[]>();\n\tfor (const event of events) {\n\t\tconst list = eventsByTx.get(event.tx_id) ?? [];\n\t\tlist.push(event);\n\t\teventsByTx.set(event.tx_id, list);\n\t}\n\n\tconst seen = new Set<string>();\n\tconst results: MatchedTx[] = [];\n\n\tfor (const [sourceName, filter] of Object.entries(sources)) {\n\t\tconst matches = matchFilter(\n\t\t\tfilter,\n\t\t\ttransactions,\n\t\t\teventsByTx,\n\t\t\ttraitContracts,\n\t\t);\n\t\tfor (const match of matches) {\n\t\t\tconst dedupeKey = `${match.tx.tx_id}:${sourceName}`;\n\t\t\tif (!seen.has(dedupeKey)) {\n\t\t\t\tseen.add(dedupeKey);\n\t\t\t\tresults.push({ ...match, sourceName });\n\t\t\t}\n\t\t}\n\t}\n\n\treturn results;\n}\n"
|
|
5
|
+
"import type { SubgraphFilter } from \"../types.ts\";\n\nexport interface MatchedTx {\n\ttx: TxRecord;\n\tevents: EventRecord[];\n\t/** Source object key — used for handler dispatch */\n\tsourceName: string;\n}\n\nexport type TxRecord = {\n\ttx_id: string;\n\ttype: string;\n\tsender: string;\n\tstatus: string;\n\t/** Position within the block — the runner sorts dispatch into chain order. */\n\ttx_index?: number;\n\tcontract_id?: string | null;\n\tfunction_name?: string | null;\n\tfunction_args?: unknown | null;\n\traw_result?: string | null;\n};\n\nexport type EventRecord = {\n\tid: string;\n\ttx_id: string;\n\ttype: string;\n\tevent_index: number;\n\tdata: unknown;\n};\n\n// ── Wildcard matching (shared with v1) ──────────────────────────────\n\nconst patternCache = new Map<string, RegExp>();\n\n/** Match a value against one pattern, or ANY pattern in a set. */\nfunction matchAny(value: string, pattern: string | readonly string[]): boolean {\n\treturn Array.isArray(pattern)\n\t\t? pattern.some((p) => matchPattern(value, p))\n\t\t: matchPattern(value, pattern as string);\n}\n\nfunction matchPattern(value: string, pattern: string): boolean {\n\tif (!pattern.includes(\"*\")) return value === pattern;\n\tlet re = patternCache.get(pattern);\n\tif (!re) {\n\t\tconst regex = pattern\n\t\t\t.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\t\t\t.replace(/\\*/g, \".*\");\n\t\tre = new RegExp(`^${regex}$`);\n\t\tpatternCache.set(pattern, re);\n\t}\n\treturn re.test(value);\n}\n\n// Trait → set of conforming contract IDs, resolved per block by the caller\n// (block-processor) from the contract registry. Kept as injected data so this\n// module stays pure/sync/DB-less.\nexport type TraitContracts = Map<string, ReadonlySet<string>>;\nconst EMPTY_SET: ReadonlySet<string> = new Set();\n\n/**\n * True when a filter's optional `trait` admits this contract: no trait → always\n * allowed; trait set → the contract must be in the resolved conforming set.\n */\n/** Addresses discovered per source name (factory sets), resolved by the\n * caller as-of the block being processed. */\nexport type FactoryContracts = ReadonlyMap<string, ReadonlySet<string>>;\n\n/**\n * A factory-scoped source matches only contracts its factory has revealed.\n * Same shape as {@link traitAllows}: the set is injected, so this module\n * stays pure and sync.\n */\nfunction factoryAllows(\n\tfilter: { factory?: { from: string; field: string } },\n\tcontractId: string | undefined | null,\n\tfactoryContracts: FactoryContracts,\n): boolean {\n\tif (!filter.factory) return true;\n\tif (!contractId) return false;\n\treturn (factoryContracts.get(filter.factory.from) ?? EMPTY_SET).has(\n\t\tcontractId,\n\t);\n}\n\n/** Read a dotted path off a decoded payload (`\"data.pool\"`). */\nexport function readPath(payload: unknown, path: string): unknown {\n\tlet cur: unknown = payload;\n\tfor (const key of path.split(\".\")) {\n\t\tif (cur === null || typeof cur !== \"object\") return undefined;\n\t\tcur = (cur as Record<string, unknown>)[key];\n\t}\n\treturn cur;\n}\n\nfunction traitAllows(\n\tfilter: SubgraphFilter,\n\tcontractId: string | undefined | null,\n\ttraitContracts: TraitContracts,\n): boolean {\n\tconst trait = (filter as { trait?: string }).trait;\n\tif (!trait) return true;\n\tif (!contractId) return false;\n\treturn (traitContracts.get(trait) ?? EMPTY_SET).has(contractId);\n}\n\n/** Extract the contract id from an asset identifier (`<contract>::<token>`). */\nfunction assetContract(assetId: string | undefined): string | undefined {\n\treturn assetId?.split(\"::\")[0];\n}\n\n// ── Per-filter-type matchers ────────────────────────────────────────\n\nfunction matchFilter(\n\tfilter: SubgraphFilter,\n\ttransactions: TxRecord[],\n\teventsByTx: Map<string, EventRecord[]>,\n\ttraitContracts: TraitContracts,\n\tfactoryContracts: FactoryContracts = new Map(),\n): { tx: TxRecord; events: EventRecord[] }[] {\n\tconst results: { tx: TxRecord; events: EventRecord[] }[] = [];\n\n\tswitch (filter.type) {\n\t\t// ── STX events ──\n\t\tcase \"stx_transfer\":\n\t\tcase \"stx_mint\":\n\t\tcase \"stx_burn\":\n\t\tcase \"stx_lock\": {\n\t\t\tconst eventType = `${filter.type}_event`;\n\t\t\tfor (const tx of transactions) {\n\t\t\t\tconst txEvents = eventsByTx.get(tx.tx_id) ?? [];\n\t\t\t\tconst matched = txEvents.filter((e) => e.type === eventType);\n\t\t\t\tif (matched.length === 0) continue;\n\n\t\t\t\t// Apply address filters\n\t\t\t\tconst filtered = matched.filter((e) => {\n\t\t\t\t\tconst data = e.data as Record<string, unknown> | null;\n\t\t\t\t\tif (!data) return false;\n\t\t\t\t\tif (\"sender\" in filter && filter.sender) {\n\t\t\t\t\t\tif (!matchPattern(data.sender as string, filter.sender))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (\"recipient\" in filter && filter.recipient) {\n\t\t\t\t\t\tif (!matchPattern(data.recipient as string, filter.recipient))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (\"lockedAddress\" in filter && filter.lockedAddress) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t!matchPattern(data.locked_address as string, filter.lockedAddress)\n\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\t// Amount filters\n\t\t\t\t\tif (\"minAmount\" in filter && filter.minAmount !== undefined) {\n\t\t\t\t\t\tconst amount = BigInt(\n\t\t\t\t\t\t\t(data.amount ?? data.locked_amount ?? \"0\") as string,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (amount < filter.minAmount) return false;\n\t\t\t\t\t}\n\t\t\t\t\tif (\n\t\t\t\t\t\t\"maxAmount\" in filter &&\n\t\t\t\t\t\t(filter as { maxAmount?: bigint }).maxAmount !== undefined\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst amount = BigInt((data.amount ?? \"0\") as string);\n\t\t\t\t\t\tif (amount > (filter as { maxAmount: bigint }).maxAmount)\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\n\t\t\t\tif (filtered.length > 0) {\n\t\t\t\t\tresults.push({ tx, events: filtered });\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t// ── FT events ──\n\t\tcase \"ft_transfer\":\n\t\tcase \"ft_mint\":\n\t\tcase \"ft_burn\": {\n\t\t\tconst eventType = `${filter.type}_event`;\n\t\t\tfor (const tx of transactions) {\n\t\t\t\tconst txEvents = eventsByTx.get(tx.tx_id) ?? [];\n\t\t\t\tconst matched = txEvents.filter((e) => {\n\t\t\t\t\tif (e.type !== eventType) return false;\n\t\t\t\t\tconst data = e.data as Record<string, unknown> | null;\n\t\t\t\t\tif (!data) return false;\n\n\t\t\t\t\t// Asset identifier filter\n\t\t\t\t\tif (filter.assetIdentifier) {\n\t\t\t\t\t\tconst assetId = data.asset_identifier as string | undefined;\n\t\t\t\t\t\tif (!assetId || !matchPattern(assetId, filter.assetIdentifier))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\t// Trait scope — the asset's contract must conform.\n\t\t\t\t\tif (\n\t\t\t\t\t\t!traitAllows(\n\t\t\t\t\t\t\tfilter,\n\t\t\t\t\t\t\tassetContract(data.asset_identifier as string | undefined),\n\t\t\t\t\t\t\ttraitContracts,\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t// Address filters\n\t\t\t\t\tif (\"sender\" in filter && filter.sender) {\n\t\t\t\t\t\tif (!matchPattern(data.sender as string, filter.sender))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (\"recipient\" in filter && filter.recipient) {\n\t\t\t\t\t\tif (!matchPattern(data.recipient as string, filter.recipient))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\t// Amount filter\n\t\t\t\t\tif (filter.minAmount !== undefined) {\n\t\t\t\t\t\tconst amount = BigInt((data.amount ?? \"0\") as string);\n\t\t\t\t\t\tif (amount < filter.minAmount) return false;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\n\t\t\t\tif (matched.length > 0) {\n\t\t\t\t\tresults.push({ tx, events: matched });\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t// ── NFT events ──\n\t\tcase \"nft_transfer\":\n\t\tcase \"nft_mint\":\n\t\tcase \"nft_burn\": {\n\t\t\tconst eventType = `${filter.type}_event`;\n\t\t\tfor (const tx of transactions) {\n\t\t\t\tconst txEvents = eventsByTx.get(tx.tx_id) ?? [];\n\t\t\t\tconst matched = txEvents.filter((e) => {\n\t\t\t\t\tif (e.type !== eventType) return false;\n\t\t\t\t\tconst data = e.data as Record<string, unknown> | null;\n\t\t\t\t\tif (!data) return false;\n\n\t\t\t\t\tif (filter.assetIdentifier) {\n\t\t\t\t\t\tconst assetId = data.asset_identifier as string | undefined;\n\t\t\t\t\t\tif (!assetId || !matchPattern(assetId, filter.assetIdentifier))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (\n\t\t\t\t\t\t!traitAllows(\n\t\t\t\t\t\t\tfilter,\n\t\t\t\t\t\t\tassetContract(data.asset_identifier as string | undefined),\n\t\t\t\t\t\t\ttraitContracts,\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t\t\treturn false;\n\t\t\t\t\tif (\"sender\" in filter && filter.sender) {\n\t\t\t\t\t\tif (!matchPattern(data.sender as string, filter.sender))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (\"recipient\" in filter && filter.recipient) {\n\t\t\t\t\t\tif (!matchPattern(data.recipient as string, filter.recipient))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\n\t\t\t\tif (matched.length > 0) {\n\t\t\t\t\tresults.push({ tx, events: matched });\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t// ── Contract call ──\n\t\tcase \"contract_call\": {\n\t\t\tfor (const tx of transactions) {\n\t\t\t\tif (tx.type !== \"contract_call\") continue;\n\n\t\t\t\t// Contract filter\n\t\t\t\tif (filter.contractId) {\n\t\t\t\t\tif (!tx.contract_id || !matchAny(tx.contract_id, filter.contractId))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// Factory scope: only contracts this source's factory revealed.\n\t\t\t\tif (!factoryAllows(filter, tx.contract_id, factoryContracts)) continue;\n\t\t\t\t// Function filter\n\t\t\t\tif (filter.functionName) {\n\t\t\t\t\tif (\n\t\t\t\t\t\t!tx.function_name ||\n\t\t\t\t\t\t!matchPattern(tx.function_name, filter.functionName)\n\t\t\t\t\t)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// Caller filter\n\t\t\t\tif (filter.caller) {\n\t\t\t\t\tif (!matchPattern(tx.sender, filter.caller)) continue;\n\t\t\t\t}\n\t\t\t\t// Trait scope — the called contract must conform.\n\t\t\t\tif (!traitAllows(filter, tx.contract_id, traitContracts)) continue;\n\n\t\t\t\tconst txEvents = eventsByTx.get(tx.tx_id) ?? [];\n\t\t\t\tresults.push({ tx, events: txEvents });\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t// ── Contract deploy ──\n\t\tcase \"contract_deploy\": {\n\t\t\tfor (const tx of transactions) {\n\t\t\t\tif (tx.type !== \"smart_contract\") continue;\n\n\t\t\t\tif (filter.deployer) {\n\t\t\t\t\tif (!matchPattern(tx.sender, filter.deployer)) continue;\n\t\t\t\t}\n\t\t\t\tif (filter.contractName) {\n\t\t\t\t\tconst name = tx.contract_id?.split(\".\")[1] ?? \"\";\n\t\t\t\t\tif (!matchPattern(name, filter.contractName)) continue;\n\t\t\t\t}\n\n\t\t\t\tconst txEvents = eventsByTx.get(tx.tx_id) ?? [];\n\t\t\t\tresults.push({ tx, events: txEvents });\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t// ── Print event ──\n\t\tcase \"print_event\": {\n\t\t\tfor (const tx of transactions) {\n\t\t\t\tconst txEvents = eventsByTx.get(tx.tx_id) ?? [];\n\t\t\t\tconst matched = txEvents.filter((e) => {\n\t\t\t\t\tif (e.type !== \"smart_contract_event\" && e.type !== \"contract_event\")\n\t\t\t\t\t\treturn false;\n\t\t\t\t\tconst data = e.data as Record<string, unknown> | null;\n\t\t\t\t\tif (!data) return false;\n\t\t\t\t\tif (data.topic !== \"print\") return false;\n\n\t\t\t\t\t// Contract filter — events store the contract under either\n\t\t\t\t\t// `contract_identifier` (legacy smart_contract_event payload)\n\t\t\t\t\t// or `contract_id` (current contract_event payload). Mirror\n\t\t\t\t\t// the streams query which checks both shapes.\n\t\t\t\t\tconst printContractId =\n\t\t\t\t\t\t(data.contract_identifier as string | undefined) ??\n\t\t\t\t\t\t(data.contract_id as string | undefined);\n\t\t\t\t\tif (filter.contractId) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t!printContractId ||\n\t\t\t\t\t\t\t!matchAny(printContractId, filter.contractId)\n\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (!traitAllows(filter, printContractId, traitContracts))\n\t\t\t\t\t\treturn false;\n\t\t\t\t\tif (!factoryAllows(filter, printContractId, factoryContracts))\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t// Topic filter — check the decoded Clarity value's topic field\n\t\t\t\t\t// At this stage data.value is still raw hex; topic filtering happens\n\t\t\t\t\t// after decode in the runner. For now, skip topic filtering here.\n\t\t\t\t\t// The runner will filter by topic after decoding.\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\n\t\t\t\tif (matched.length > 0) {\n\t\t\t\t\tresults.push({ tx, events: matched });\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn results;\n}\n\n/**\n * Match named filters against a block's transactions and events.\n * Returns matches with sourceName = the object key from sources.\n */\nexport function matchSources(\n\tsources: Record<string, SubgraphFilter>,\n\ttransactions: TxRecord[],\n\tevents: EventRecord[],\n\ttraitContracts: TraitContracts = new Map(),\n\tfactoryContracts: FactoryContracts = new Map(),\n): MatchedTx[] {\n\t// Index events by txId\n\tconst eventsByTx = new Map<string, EventRecord[]>();\n\tfor (const event of events) {\n\t\tconst list = eventsByTx.get(event.tx_id) ?? [];\n\t\tlist.push(event);\n\t\teventsByTx.set(event.tx_id, list);\n\t}\n\n\tconst seen = new Set<string>();\n\tconst results: MatchedTx[] = [];\n\n\tfor (const [sourceName, filter] of Object.entries(sources)) {\n\t\tconst matches = matchFilter(\n\t\t\tfilter,\n\t\t\ttransactions,\n\t\t\teventsByTx,\n\t\t\ttraitContracts,\n\t\t\tfactoryContracts,\n\t\t);\n\t\tfor (const match of matches) {\n\t\t\tconst dedupeKey = `${match.tx.tx_id}:${sourceName}`;\n\t\t\tif (!seen.has(dedupeKey)) {\n\t\t\t\tseen.add(dedupeKey);\n\t\t\t\tresults.push({ ...match, sourceName });\n\t\t\t}\n\t\t}\n\t}\n\n\treturn results;\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;;AAgCA,IAAM,eAAe,IAAI;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "3A7F11E035AAF9A264756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -3,8 +3,8 @@ interface ProcessBlockTiming {
|
|
|
3
3
|
handlerMs: number;
|
|
4
4
|
flushMs: number;
|
|
5
5
|
}
|
|
6
|
-
import { Database } from "@secondlayer/shared/db";
|
|
7
|
-
import { Kysely } from "kysely";
|
|
6
|
+
import { Database as Database2 } from "@secondlayer/shared/db";
|
|
7
|
+
import { Kysely as Kysely2 } from "kysely";
|
|
8
8
|
/**
|
|
9
9
|
* Accumulates per-block timing stats and flushes to DB periodically.
|
|
10
10
|
* One instance per subgraph during catchup/reindex/live processing.
|
|
@@ -17,7 +17,7 @@ declare class StatsAccumulator {
|
|
|
17
17
|
constructor(subgraphName: string, isCatchup: boolean);
|
|
18
18
|
record(timing: ProcessBlockTiming, opsCount: number): void;
|
|
19
19
|
shouldFlush(): boolean;
|
|
20
|
-
flush(db:
|
|
20
|
+
flush(db: Kysely2<Database2>): Promise<void>;
|
|
21
21
|
private newEntry;
|
|
22
22
|
}
|
|
23
23
|
export { StatsAccumulator };
|