@secondlayer/subgraphs 4.4.1 → 5.0.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/README.md +13 -21
- package/dist/{src/index.d.ts → index.d.ts} +59 -189
- package/dist/index.js +2151 -0
- package/dist/index.js.map +24 -0
- package/dist/{src/runtime → runtime}/emitter.d.ts +1 -1
- package/dist/{src/runtime → runtime}/emitter.js +8 -12
- package/dist/{src/runtime → runtime}/emitter.js.map +3 -3
- package/dist/{src/runtime → runtime}/replay.d.ts +1 -1
- package/dist/{src/runtime → runtime}/replay.js +342 -683
- package/dist/{src/runtime → runtime}/replay.js.map +5 -5
- package/dist/{src/schema → schema}/index.d.ts +10 -69
- package/dist/{src/schema → schema}/index.js +188 -281
- package/dist/schema/index.js.map +13 -0
- package/dist/{src/testing → testing}/index.d.ts +65 -115
- package/dist/{src/testing → testing}/index.js +554 -968
- package/dist/testing/index.js.map +21 -0
- package/dist/{src/types.d.ts → types.d.ts} +10 -66
- package/dist/{src/validate.d.ts → validate.d.ts} +11 -69
- package/dist/{src/validate.js → validate.js} +18 -98
- package/dist/validate.js.map +10 -0
- package/package.json +18 -26
- package/dist/src/index.js +0 -6081
- package/dist/src/index.js.map +0 -44
- package/dist/src/runtime/block-processor.d.ts +0 -559
- package/dist/src/runtime/block-processor.js +0 -3810
- package/dist/src/runtime/block-processor.js.map +0 -31
- package/dist/src/runtime/catchup.d.ts +0 -473
- package/dist/src/runtime/catchup.js +0 -4228
- package/dist/src/runtime/catchup.js.map +0 -35
- package/dist/src/runtime/clarity.d.ts +0 -15
- package/dist/src/runtime/clarity.js +0 -41
- package/dist/src/runtime/clarity.js.map +0 -10
- package/dist/src/runtime/context.d.ts +0 -325
- package/dist/src/runtime/context.js +0 -936
- package/dist/src/runtime/context.js.map +0 -13
- package/dist/src/runtime/processor.d.ts +0 -11
- package/dist/src/runtime/processor.js +0 -5372
- package/dist/src/runtime/processor.js.map +0 -40
- package/dist/src/runtime/reindex.d.ts +0 -539
- package/dist/src/runtime/reindex.js +0 -4802
- package/dist/src/runtime/reindex.js.map +0 -37
- package/dist/src/runtime/reorg.d.ts +0 -432
- package/dist/src/runtime/reorg.js +0 -4355
- package/dist/src/runtime/reorg.js.map +0 -36
- package/dist/src/runtime/runner.d.ts +0 -719
- package/dist/src/runtime/runner.js +0 -1332
- package/dist/src/runtime/runner.js.map +0 -16
- package/dist/src/runtime/source-matcher.d.ts +0 -304
- package/dist/src/runtime/source-matcher.js +0 -335
- package/dist/src/runtime/source-matcher.js.map +0 -10
- package/dist/src/runtime/stats.d.ts +0 -23
- package/dist/src/runtime/stats.js +0 -71
- package/dist/src/runtime/stats.js.map +0 -10
- package/dist/src/schema/index.js.map +0 -13
- package/dist/src/service.d.ts +0 -0
- package/dist/src/service.js +0 -5398
- package/dist/src/service.js.map +0 -41
- package/dist/src/testing/index.js.map +0 -22
- package/dist/src/triggers/index.d.ts +0 -381
- package/dist/src/triggers/index.js +0 -31
- package/dist/src/triggers/index.js.map +0 -10
- package/dist/src/validate.js.map +0 -10
- /package/dist/{src/types.js → types.js} +0 -0
- /package/dist/{src/types.js.map → types.js.map} +0 -0
|
@@ -1,559 +0,0 @@
|
|
|
1
|
-
import { AbiContract as AbiContract2 } from "@secondlayer/stacks/clarity";
|
|
2
|
-
import { AssetIdentifier } from "@secondlayer/stacks/filters";
|
|
3
|
-
/**
|
|
4
|
-
* How long a read stays valid.
|
|
5
|
-
*
|
|
6
|
-
* - `per-block` (default): pinned to the block being processed, cached under
|
|
7
|
-
* its `index_block_hash`. Sound for any read-only function — reindexing the
|
|
8
|
-
* same block always returns the same value, and a reorg-replaced block has a
|
|
9
|
-
* different id so it can't inherit the orphaned answer.
|
|
10
|
-
* - `contract-constant`: the caller asserts the value cannot change for this
|
|
11
|
-
* contract and these args (SIP-010 `get-decimals`/`get-symbol`), so it is
|
|
12
|
-
* resolved ONCE instead of once per block. This is the mode that makes a
|
|
13
|
-
* backfill affordable — under `per-block` every block is a distinct key and
|
|
14
|
-
* therefore a distinct RPC. Declaring it on a value that does change pins the
|
|
15
|
-
* first answer forever, so it is opt-in and never inferred.
|
|
16
|
-
*/
|
|
17
|
-
type ChainReadCacheMode = "per-block" | "contract-constant";
|
|
18
|
-
type ChainReadOptions = {
|
|
19
|
-
cache?: ChainReadCacheMode
|
|
20
|
-
};
|
|
21
|
-
/**
|
|
22
|
-
* `ChainReadClient` with the ABI generic erased. Structurally cheap, so the
|
|
23
|
-
* base `SubgraphContext` can carry it; `TypedSubgraphContext` uses the real
|
|
24
|
-
* one and handlers get full inference.
|
|
25
|
-
*/
|
|
26
|
-
interface ErasedChainReadClient {
|
|
27
|
-
contract(contractId: string, abi: unknown, options?: ChainReadOptions): {
|
|
28
|
-
read: Record<string, (args?: Record<string, unknown>) => Promise<unknown>>
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
/** Supported column types for subgraph schemas */
|
|
32
|
-
type ColumnType = "text" | "uint" | "int" | "principal" | "boolean" | "timestamp" | "jsonb";
|
|
33
|
-
/** Column definition in a subgraph table */
|
|
34
|
-
interface SubgraphColumn {
|
|
35
|
-
type: ColumnType;
|
|
36
|
-
nullable?: boolean;
|
|
37
|
-
indexed?: boolean;
|
|
38
|
-
search?: boolean;
|
|
39
|
-
default?: string | number | boolean;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* A foreign-key relation to another table in the same subgraph. Drives DDL FK
|
|
43
|
-
* constraints and ORM codegen (`@relation` in Prisma, `relations()` in Drizzle)
|
|
44
|
-
* so generated clients get typed joins. The referenced columns must form a
|
|
45
|
-
* `uniqueKeys` entry on the target table.
|
|
46
|
-
*/
|
|
47
|
-
interface SubgraphRelation {
|
|
48
|
-
/** Relation field name on this table's generated model (e.g. "pool"). */
|
|
49
|
-
name: string;
|
|
50
|
-
/** Target table name in this subgraph. */
|
|
51
|
-
references: string;
|
|
52
|
-
/** Local column(s) holding the foreign key. */
|
|
53
|
-
fields: string[];
|
|
54
|
-
/** Target column(s) the fields point at (a uniqueKeys entry on the target). */
|
|
55
|
-
referencedColumns: string[];
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Table definition within a subgraph schema.
|
|
59
|
-
*
|
|
60
|
-
* The array fields are `readonly` so a schema literal can be hoisted out of
|
|
61
|
-
* the `defineSubgraph()` call with `as const` (or {@link defineSchema}) and
|
|
62
|
-
* still typecheck — `as const` produces `readonly [readonly ["holder"]]`,
|
|
63
|
-
* which a mutable `string[][]` rejects. Without that, extracting a helper
|
|
64
|
-
* that takes `ctx` meant losing the entire typed surface, which is why the
|
|
65
|
-
* shipped starter reached for `ctx: any`.
|
|
66
|
-
*/
|
|
67
|
-
interface SubgraphTable {
|
|
68
|
-
columns: Record<string, SubgraphColumn>;
|
|
69
|
-
/** Composite indexes (each entry is an array of column names) */
|
|
70
|
-
indexes?: readonly (readonly string[])[];
|
|
71
|
-
/** Unique key constraints (each entry is an array of column names). Required for upsert. */
|
|
72
|
-
uniqueKeys?: readonly (readonly string[])[];
|
|
73
|
-
/** Foreign-key relations to other tables (for typed ORM joins). */
|
|
74
|
-
relations?: readonly SubgraphRelation[];
|
|
75
|
-
}
|
|
76
|
-
/** Subgraph schema — maps table names to table definitions */
|
|
77
|
-
type SubgraphSchema = Record<string, SubgraphTable>;
|
|
78
|
-
/** STX event filters */
|
|
79
|
-
interface StxTransferFilter {
|
|
80
|
-
type: "stx_transfer";
|
|
81
|
-
sender?: string;
|
|
82
|
-
recipient?: string;
|
|
83
|
-
minAmount?: bigint;
|
|
84
|
-
maxAmount?: bigint;
|
|
85
|
-
}
|
|
86
|
-
interface StxMintFilter {
|
|
87
|
-
type: "stx_mint";
|
|
88
|
-
recipient?: string;
|
|
89
|
-
minAmount?: bigint;
|
|
90
|
-
}
|
|
91
|
-
interface StxBurnFilter {
|
|
92
|
-
type: "stx_burn";
|
|
93
|
-
sender?: string;
|
|
94
|
-
minAmount?: bigint;
|
|
95
|
-
}
|
|
96
|
-
interface StxLockFilter {
|
|
97
|
-
type: "stx_lock";
|
|
98
|
-
lockedAddress?: string;
|
|
99
|
-
minAmount?: bigint;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Restrict a source to contracts conforming to a trait/standard (e.g. "sip-010")
|
|
103
|
-
* instead of a fixed contract — resolved from the contract registry at match time,
|
|
104
|
-
* as-of each processed block. Lets a source index "all SIP-010 tokens" etc.
|
|
105
|
-
*/
|
|
106
|
-
type TraitScope = {
|
|
107
|
-
trait?: string
|
|
108
|
-
};
|
|
109
|
-
/**
|
|
110
|
-
* Scope a source to a DYNAMIC address set discovered from another source's
|
|
111
|
-
* events, instead of a fixed contract list.
|
|
112
|
-
*
|
|
113
|
-
* The standard shape it unlocks: a router plus pools created after you
|
|
114
|
-
* deploy. `contractId` arrays cover a set you know at authoring time; a
|
|
115
|
-
* factory covers the set that grows. It is deliberately NOT tied to contract
|
|
116
|
-
* deployment — the address can come from any field of any event, so it also
|
|
117
|
-
* covers launchpad-minted tokens, DAO extension allowlists, and registry
|
|
118
|
-
* entries.
|
|
119
|
-
*
|
|
120
|
-
* ```ts
|
|
121
|
-
* sources: {
|
|
122
|
-
* registry: {
|
|
123
|
-
* type: "print_event",
|
|
124
|
-
* contractId: REGISTRY,
|
|
125
|
-
* topic: "pool-created",
|
|
126
|
-
* prints: { "pool-created": { pool: "principal" } },
|
|
127
|
-
* },
|
|
128
|
-
* swaps: {
|
|
129
|
-
* type: "print_event",
|
|
130
|
-
* topic: "swap",
|
|
131
|
-
* // every pool the registry has ever announced, including new ones
|
|
132
|
-
* factory: { from: "registry", field: "data.pool" },
|
|
133
|
-
* },
|
|
134
|
-
* }
|
|
135
|
-
* ```
|
|
136
|
-
*
|
|
137
|
-
* Two guarantees the runtime provides:
|
|
138
|
-
* - **Same-block ordering** — a contract discovered in block N receives its
|
|
139
|
-
* own block-N events. Discovery runs before matching, within the block.
|
|
140
|
-
* - **Reorg-scoped** — the discovered set is stamped with the block that
|
|
141
|
-
* revealed it and rolled back with everything else on a fork, so an address
|
|
142
|
-
* announced on an orphaned chain does not linger forever.
|
|
143
|
-
*/
|
|
144
|
-
interface FactoryScope {
|
|
145
|
-
factory?: {
|
|
146
|
-
/** Source name whose events reveal the addresses. */
|
|
147
|
-
from: string
|
|
148
|
-
/** Dotted path to the address on that source's payload
|
|
149
|
-
* (e.g. `"data.pool"`, `"contractId"`, `"data.token.id"`). */
|
|
150
|
-
field: string
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
/** FT event filters */
|
|
154
|
-
interface FtTransferFilter extends TraitScope {
|
|
155
|
-
type: "ft_transfer";
|
|
156
|
-
assetIdentifier?: AssetIdentifier;
|
|
157
|
-
sender?: string;
|
|
158
|
-
recipient?: string;
|
|
159
|
-
minAmount?: bigint;
|
|
160
|
-
}
|
|
161
|
-
interface FtMintFilter extends TraitScope {
|
|
162
|
-
type: "ft_mint";
|
|
163
|
-
assetIdentifier?: AssetIdentifier;
|
|
164
|
-
recipient?: string;
|
|
165
|
-
minAmount?: bigint;
|
|
166
|
-
}
|
|
167
|
-
interface FtBurnFilter extends TraitScope {
|
|
168
|
-
type: "ft_burn";
|
|
169
|
-
assetIdentifier?: AssetIdentifier;
|
|
170
|
-
sender?: string;
|
|
171
|
-
minAmount?: bigint;
|
|
172
|
-
}
|
|
173
|
-
/** NFT event filters */
|
|
174
|
-
interface NftTransferFilter extends TraitScope {
|
|
175
|
-
type: "nft_transfer";
|
|
176
|
-
assetIdentifier?: AssetIdentifier;
|
|
177
|
-
sender?: string;
|
|
178
|
-
recipient?: string;
|
|
179
|
-
}
|
|
180
|
-
interface NftMintFilter extends TraitScope {
|
|
181
|
-
type: "nft_mint";
|
|
182
|
-
assetIdentifier?: AssetIdentifier;
|
|
183
|
-
recipient?: string;
|
|
184
|
-
}
|
|
185
|
-
interface NftBurnFilter extends TraitScope {
|
|
186
|
-
type: "nft_burn";
|
|
187
|
-
assetIdentifier?: AssetIdentifier;
|
|
188
|
-
sender?: string;
|
|
189
|
-
}
|
|
190
|
-
/** Contract event filters */
|
|
191
|
-
interface ContractCallFilter extends TraitScope, FactoryScope {
|
|
192
|
-
type: "contract_call";
|
|
193
|
-
/**
|
|
194
|
-
* One contract id, or a SET of them (max 20) — the standard DeFi shape is
|
|
195
|
-
* a router plus N pools, which used to cost N near-identical sources and
|
|
196
|
-
* handlers, or the `"*"` catch-all that erases per-source typing. Payload
|
|
197
|
-
* types are identical per `type`, so one handler serves the set and
|
|
198
|
-
* `event.contractId` disambiguates.
|
|
199
|
-
*
|
|
200
|
-
* Supports `*` wildcards (e.g. `"SP1ABC….pool-*"`). Mutually exclusive
|
|
201
|
-
* with `trait`, which resolves its own set per block.
|
|
202
|
-
*/
|
|
203
|
-
contractId?: string | readonly string[];
|
|
204
|
-
functionName?: string;
|
|
205
|
-
caller?: string;
|
|
206
|
-
/**
|
|
207
|
-
* Contract ABI (pass it `as const`) used to type `event.input` — the named,
|
|
208
|
-
* decoded function arguments. Dev-provided; serialized into the deployed
|
|
209
|
-
* definition and used at runtime to decode args by name. Omit to keep
|
|
210
|
-
* `event.args` as a positional `unknown[]`.
|
|
211
|
-
*
|
|
212
|
-
* Deploy validation requires `abi` whenever `functionName` is set
|
|
213
|
-
* (`normalizeAbi()` from `@secondlayer/stacks/clarity`).
|
|
214
|
-
*/
|
|
215
|
-
abi?: AbiContract2;
|
|
216
|
-
}
|
|
217
|
-
interface ContractDeployFilter {
|
|
218
|
-
type: "contract_deploy";
|
|
219
|
-
deployer?: string;
|
|
220
|
-
contractName?: string;
|
|
221
|
-
}
|
|
222
|
-
interface NestedContractCallFilter extends TraitScope, FactoryScope {
|
|
223
|
-
type: "nested_contract_call";
|
|
224
|
-
contractId?: string | readonly string[];
|
|
225
|
-
functionName?: string;
|
|
226
|
-
caller?: string;
|
|
227
|
-
sender?: string;
|
|
228
|
-
}
|
|
229
|
-
interface VarSetFilter extends TraitScope, FactoryScope {
|
|
230
|
-
type: "var_set";
|
|
231
|
-
contractId?: string | readonly string[];
|
|
232
|
-
varName?: string;
|
|
233
|
-
}
|
|
234
|
-
interface MapWriteFilter extends TraitScope, FactoryScope {
|
|
235
|
-
type: "map_set" | "map_insert" | "map_delete";
|
|
236
|
-
contractId?: string | readonly string[];
|
|
237
|
-
map?: string;
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* One declared field of a print payload.
|
|
241
|
-
*
|
|
242
|
-
* A bare {@link ColumnType} is a required scalar (`amount: "uint"`). The
|
|
243
|
-
* composite forms exist because real print payloads are not flat: BNS-V2
|
|
244
|
-
* emits `name` as a NESTED tuple, and a `prints` vocabulary that could only
|
|
245
|
-
* say `"jsonb"` is why a handler reading flat `data.name` type-checked,
|
|
246
|
-
* deployed, and then decoded to null on every event.
|
|
247
|
-
*
|
|
248
|
-
* - `{ type: F, optional: true }` — the KEY becomes optional on `event.data`
|
|
249
|
-
* (mirrors `always_present: false` in the inferred schema).
|
|
250
|
-
* - `{ tuple: { … } }` — a nested tuple, typed all the way down.
|
|
251
|
-
* - `{ list: F }` — a list of `F`.
|
|
252
|
-
*/
|
|
253
|
-
type PrintField = ColumnType | {
|
|
254
|
-
type: PrintField
|
|
255
|
-
optional: true
|
|
256
|
-
} | {
|
|
257
|
-
tuple: Record<string, PrintField>
|
|
258
|
-
} | {
|
|
259
|
-
list: PrintField
|
|
260
|
-
};
|
|
261
|
-
/**
|
|
262
|
-
* Per-topic field schema for a print_event source. When declared, the handler's
|
|
263
|
-
* `event` is a discriminated union keyed by `topic` and `event.data` is typed
|
|
264
|
-
* per topic. Nested tuples, lists, and optional fields — see {@link PrintField}.
|
|
265
|
-
*
|
|
266
|
-
* Also opts the source into RUNTIME validation: a payload that does not match
|
|
267
|
-
* is skipped and logged rather than written as nulls.
|
|
268
|
-
*/
|
|
269
|
-
type PrintEventPrints = Record<string, Record<string, PrintField>>;
|
|
270
|
-
/**
|
|
271
|
-
* One column in a static identity map. Literals only — the bundler never
|
|
272
|
-
* executes user code, so `{ from: "tokenX" }` is valid and `from("tokenX")`
|
|
273
|
-
* is not.
|
|
274
|
-
*/
|
|
275
|
-
type MaterializeColumn = {
|
|
276
|
-
from: string
|
|
277
|
-
} | {
|
|
278
|
-
fromTx: "sender" | "txId"
|
|
279
|
-
} | {
|
|
280
|
-
fromBlock: "height" | "hash" | "timestamp"
|
|
281
|
-
};
|
|
282
|
-
/** Static insert from a source event — replaces a 1:1 handler. */
|
|
283
|
-
interface MaterializeSpec {
|
|
284
|
-
table: string;
|
|
285
|
-
/** Keys = schema column names (snake). */
|
|
286
|
-
columns: Record<string, MaterializeColumn>;
|
|
287
|
-
}
|
|
288
|
-
type PrintEventFields = FactoryScope & {
|
|
289
|
-
type: "print_event"
|
|
290
|
-
topic?: string
|
|
291
|
-
/** Identity map: insert without a handler function. XOR with handlers[name]. */
|
|
292
|
-
materialize?: MaterializeSpec
|
|
293
|
-
};
|
|
294
|
-
/**
|
|
295
|
-
* Pinned to one or more contract ids with no trait — `prints` is required so
|
|
296
|
-
* `event.data` is typed and empty-table handlers cannot typecheck.
|
|
297
|
-
*/
|
|
298
|
-
type PinnedPrintEventFilter = PrintEventFields & {
|
|
299
|
-
contractId: string | readonly string[]
|
|
300
|
-
trait?: undefined
|
|
301
|
-
prints: PrintEventPrints
|
|
302
|
-
};
|
|
303
|
-
/**
|
|
304
|
-
* Trait-scoped print source (optionally narrowed by contractId). No single
|
|
305
|
-
* schema — `prints` stays optional.
|
|
306
|
-
*/
|
|
307
|
-
type TraitPrintEventFilter = PrintEventFields & {
|
|
308
|
-
contractId?: string | readonly string[]
|
|
309
|
-
trait: string
|
|
310
|
-
prints?: PrintEventPrints
|
|
311
|
-
};
|
|
312
|
-
/**
|
|
313
|
-
* Unpinned / factory-only print source (no contractId). `prints` optional.
|
|
314
|
-
*/
|
|
315
|
-
type UnpinnedPrintEventFilter = PrintEventFields & {
|
|
316
|
-
contractId?: undefined
|
|
317
|
-
trait?: string
|
|
318
|
-
prints?: PrintEventPrints
|
|
319
|
-
};
|
|
320
|
-
/**
|
|
321
|
-
* Print-event filter. Pinned sources (contractId set, no trait) require
|
|
322
|
-
* `prints`; trait and unpinned sources may omit it.
|
|
323
|
-
*/
|
|
324
|
-
type PrintEventFilter = PinnedPrintEventFilter | TraitPrintEventFilter | UnpinnedPrintEventFilter;
|
|
325
|
-
/** All subgraph filter types — discriminated on `type` */
|
|
326
|
-
type SubgraphFilter = StxTransferFilter | StxMintFilter | StxBurnFilter | StxLockFilter | FtTransferFilter | FtMintFilter | FtBurnFilter | NftTransferFilter | NftMintFilter | NftBurnFilter | ContractCallFilter | ContractDeployFilter | PrintEventFilter | NestedContractCallFilter | VarSetFilter | MapWriteFilter;
|
|
327
|
-
/** Transaction metadata available in handlers */
|
|
328
|
-
interface TxMeta {
|
|
329
|
-
txId: string;
|
|
330
|
-
sender: string;
|
|
331
|
-
type: string;
|
|
332
|
-
status: string;
|
|
333
|
-
contractId?: string | null;
|
|
334
|
-
functionName?: string | null;
|
|
335
|
-
}
|
|
336
|
-
/** Scalar types that can appear as row values */
|
|
337
|
-
type RowValue = string | number | bigint | boolean | null | undefined | Record<string, unknown> | unknown[];
|
|
338
|
-
/** Value or computed function that receives existing row */
|
|
339
|
-
type ComputedValue = RowValue | ((existing: Record<string, unknown> | null) => unknown);
|
|
340
|
-
/** Context passed to subgraph handlers during event processing */
|
|
341
|
-
interface SubgraphContext {
|
|
342
|
-
block: {
|
|
343
|
-
height: number
|
|
344
|
-
hash: string
|
|
345
|
-
timestamp: number
|
|
346
|
-
burnBlockHeight: number
|
|
347
|
-
/** Nakamoto StacksBlockId, when the block row carries one. */
|
|
348
|
-
indexBlockHash?: string | null
|
|
349
|
-
};
|
|
350
|
-
tx: TxMeta;
|
|
351
|
-
/**
|
|
352
|
-
* Read-only contract calls pinned to this block. Cached, so the same
|
|
353
|
-
* (contract, function, args) is fetched once per block rather than once
|
|
354
|
-
* per event.
|
|
355
|
-
*
|
|
356
|
-
* Erased here on purpose: this interface is compared structurally wherever
|
|
357
|
-
* a subgraph definition is passed around, and carrying an ABI-generic
|
|
358
|
-
* method pushes those comparisons past TS's instantiation depth. Handlers
|
|
359
|
-
* receive `TypedSubgraphContext`, which exposes the fully typed version.
|
|
360
|
-
*/
|
|
361
|
-
client: ErasedChainReadClient;
|
|
362
|
-
insert(table: string, row: Record<string, unknown>): void;
|
|
363
|
-
update(table: string, where: Record<string, unknown>, set: Record<string, unknown>): void;
|
|
364
|
-
upsert(table: string, key: Record<string, unknown>, row: Record<string, unknown>): void;
|
|
365
|
-
delete(table: string, where: Record<string, unknown>): void;
|
|
366
|
-
/**
|
|
367
|
-
* Atomic counter update — the blessed accumulator primitive. Applies
|
|
368
|
-
* `col = COALESCE(col, 0) + delta` per column (insert-or-add); deltas may
|
|
369
|
-
* be negative. Requires a uniqueKeys constraint matching `key`. Prefer
|
|
370
|
-
* this over patchOrInsert with functional updaters for running totals.
|
|
371
|
-
*/
|
|
372
|
-
increment(table: string, key: Record<string, unknown>, deltas: Record<string, bigint | number>): void;
|
|
373
|
-
/** Partial update — sets only specified fields, preserves others */
|
|
374
|
-
patch(table: string, where: Record<string, unknown>, set: Record<string, unknown>): void;
|
|
375
|
-
/** Find-then-merge-or-insert. Values can be functions: (existing) => newValue */
|
|
376
|
-
patchOrInsert(table: string, key: Record<string, unknown>, row: Record<string, ComputedValue>): Promise<void>;
|
|
377
|
-
findOne(table: string, where: Record<string, unknown>): Promise<Record<string, unknown> | null>;
|
|
378
|
-
findMany(table: string, where: Record<string, unknown>): Promise<Record<string, unknown>[]>;
|
|
379
|
-
/** Format a bigint amount with decimal places */
|
|
380
|
-
formatUnits(value: bigint, decimals: number): string;
|
|
381
|
-
/** Count rows matching filter */
|
|
382
|
-
count(table: string, where?: Record<string, unknown>): Promise<number>;
|
|
383
|
-
/** Sum a numeric column */
|
|
384
|
-
sum(table: string, column: string, where?: Record<string, unknown>): Promise<bigint>;
|
|
385
|
-
/** Min of a numeric column */
|
|
386
|
-
min(table: string, column: string, where?: Record<string, unknown>): Promise<bigint | null>;
|
|
387
|
-
/** Max of a numeric column */
|
|
388
|
-
max(table: string, column: string, where?: Record<string, unknown>): Promise<bigint | null>;
|
|
389
|
-
/** Count distinct values in a column */
|
|
390
|
-
countDistinct(table: string, column: string, where?: Record<string, unknown>): Promise<number>;
|
|
391
|
-
}
|
|
392
|
-
/** Handler function that processes events and writes to the subgraph */
|
|
393
|
-
type SubgraphHandler = (event: Record<string, unknown>, ctx: SubgraphContext) => Promise<void> | void;
|
|
394
|
-
/** Complete subgraph definition */
|
|
395
|
-
interface SubgraphDefinition {
|
|
396
|
-
/** Unique subgraph name (lowercase, alphanumeric + hyphens) */
|
|
397
|
-
name: string;
|
|
398
|
-
/** Semantic version */
|
|
399
|
-
version?: string;
|
|
400
|
-
/** Human description */
|
|
401
|
-
description?: string;
|
|
402
|
-
/** Block height to start indexing from (default: 1) */
|
|
403
|
-
startBlock?: number;
|
|
404
|
-
/** 'concurrent' = tip-first: live at tip now, history backfills behind.
|
|
405
|
-
* Requires order-tolerant handlers. Default 'blocking'. */
|
|
406
|
-
backfillMode?: "blocking" | "concurrent";
|
|
407
|
-
/** Named source filters — keys become handler keys */
|
|
408
|
-
sources: Record<string, SubgraphFilter>;
|
|
409
|
-
/** Tables in this subgraph */
|
|
410
|
-
schema: SubgraphSchema;
|
|
411
|
-
/** Handler functions — keys must match source names (or "*" for catch-all) */
|
|
412
|
-
handlers: Record<string, SubgraphHandler>;
|
|
413
|
-
}
|
|
414
|
-
type TxRecord = {
|
|
415
|
-
tx_id: string
|
|
416
|
-
type: string
|
|
417
|
-
sender: string
|
|
418
|
-
status: string
|
|
419
|
-
/** Position within the block — the runner sorts dispatch into chain order. */
|
|
420
|
-
tx_index?: number
|
|
421
|
-
contract_id?: string | null
|
|
422
|
-
function_name?: string | null
|
|
423
|
-
function_args?: unknown | null
|
|
424
|
-
raw_result?: string | null
|
|
425
|
-
};
|
|
426
|
-
type EventRecord = {
|
|
427
|
-
id: string
|
|
428
|
-
tx_id: string
|
|
429
|
-
type: string
|
|
430
|
-
/** Classic `event_index`, or `ordinal` when `clock === "vm"`. */
|
|
431
|
-
event_index: number
|
|
432
|
-
data: unknown
|
|
433
|
-
/** Set on opt-in `vm_events` rows. Two clocks never sort or dedupe together. */
|
|
434
|
-
clock?: "vm"
|
|
435
|
-
};
|
|
436
|
-
import { Database as Database2 } from "@secondlayer/shared/db";
|
|
437
|
-
import { Kysely as Kysely2, Transaction } from "kysely";
|
|
438
|
-
/** Drop a subgraph's cached route — call on redeploy/delete (conn may
|
|
439
|
-
* change). Also evicts any warm sandbox subprocess for this subgraph
|
|
440
|
-
* (f071 Stage A `host.ts`) on the same edge: a redeploy can change
|
|
441
|
-
* `handler_code`/`version` together, and a stale warm
|
|
442
|
-
* subprocess must not survive past this invalidation any more than the
|
|
443
|
-
* cached route does. A no-op today since nothing sets `sandbox_workers`,
|
|
444
|
-
* but keeps the pool's documented invalidation contract honest for when it
|
|
445
|
-
* is. */
|
|
446
|
-
declare function invalidateSubgraphRoute(subgraphName: string): void;
|
|
447
|
-
/**
|
|
448
|
-
* Resolve each distinct trait used by a subgraph's sources to its conforming
|
|
449
|
-
* contract-id set, as of `blockHeight`, from the contract registry. Empty map
|
|
450
|
-
* when no source is trait-scoped (the common case → no DB work).
|
|
451
|
-
*/
|
|
452
|
-
/**
|
|
453
|
-
* Resolve each factory-scoped source's address set for this block.
|
|
454
|
-
*
|
|
455
|
-
* Two parts, and the ORDER is the guarantee:
|
|
456
|
-
* 1. addresses persisted by earlier blocks (`block_height <= blockHeight`,
|
|
457
|
-
* so a reindex sees exactly what the live walk saw), then
|
|
458
|
-
* 2. addresses revealed by THIS block's events — computed before matching,
|
|
459
|
-
* so a contract discovered in block N receives its own block-N events.
|
|
460
|
-
* Without this pass the first event from every discovered contract is
|
|
461
|
-
* silently lost.
|
|
462
|
-
*/
|
|
463
|
-
/**
|
|
464
|
-
* Exported as a test seam. Both guarantees this function carries — a contract
|
|
465
|
-
* discovered in block N receiving its OWN block-N events, and the persisted set
|
|
466
|
-
* being height-stamped so a reorg can roll it back — were asserted only by
|
|
467
|
-
* comment until now.
|
|
468
|
-
*/
|
|
469
|
-
declare function resolveFactoryContracts(subgraph: SubgraphDefinition, blockHeight: number, schemaName: string, db: Kysely2<Database2>, txs: TxRecord[], evts: EventRecord[], vmEvts?: EventRecord[]): Promise<{
|
|
470
|
-
resolved: Map<string, ReadonlySet<string>>
|
|
471
|
-
discovered: Array<{
|
|
472
|
-
sourceName: string
|
|
473
|
-
address: string
|
|
474
|
-
}>
|
|
475
|
-
}>;
|
|
476
|
-
interface ProcessBlockTiming {
|
|
477
|
-
totalMs: number;
|
|
478
|
-
handlerMs: number;
|
|
479
|
-
flushMs: number;
|
|
480
|
-
}
|
|
481
|
-
interface ProcessBlockResult {
|
|
482
|
-
blockHeight: number;
|
|
483
|
-
matched: number;
|
|
484
|
-
processed: number;
|
|
485
|
-
errors: number;
|
|
486
|
-
skipped: boolean;
|
|
487
|
-
timing?: ProcessBlockTiming;
|
|
488
|
-
}
|
|
489
|
-
/**
|
|
490
|
-
* Process a single block through a single subgraph's pipeline.
|
|
491
|
-
*
|
|
492
|
-
* Flow:
|
|
493
|
-
* 1. Load block + txs + events from DB
|
|
494
|
-
* 2. Run source matcher
|
|
495
|
-
* 3. Run handlers with SubgraphContext
|
|
496
|
-
* 4. Flush context (commit writes atomically)
|
|
497
|
-
* 5. Update subgraph.last_processed_block
|
|
498
|
-
*/
|
|
499
|
-
interface PreloadedBlockData {
|
|
500
|
-
block: import("@secondlayer/shared/db").Block;
|
|
501
|
-
txs: import("@secondlayer/shared/db").Transaction[];
|
|
502
|
-
events: import("@secondlayer/shared/db").Event[];
|
|
503
|
-
/** Opt-in `vm_events` rows on the vm clock; see `BlockData.vmEvents`. */
|
|
504
|
-
vmEvents?: import("./batch-loader.ts").RuntimeEvent[];
|
|
505
|
-
}
|
|
506
|
-
interface ProcessBlockOptions {
|
|
507
|
-
/** Skip updating last_processed_block in DB (reindex batches this externally). */
|
|
508
|
-
skipProgressUpdate?: boolean;
|
|
509
|
-
/** Pre-loaded block data — skips DB reads when provided (used by batch catch-up). */
|
|
510
|
-
preloaded?: PreloadedBlockData;
|
|
511
|
-
/**
|
|
512
|
-
* Crash-safe sequential processing. Two checkpoint scopes:
|
|
513
|
-
* - `{ status }` (reindex): a written block commits
|
|
514
|
-
* `subgraphs.last_processed_block = blockHeight` in the SAME transaction
|
|
515
|
-
* as its writes; replays skip at/below the cursor. Only for strictly
|
|
516
|
-
* ascending walks over the subgraph's own cursor.
|
|
517
|
-
* - `{ operationId }` (backfill): same guarantee against the OPERATION's
|
|
518
|
-
* own `cursor_block` — backfills legitimately revisit heights below the
|
|
519
|
-
* live cursor, so they must never checkpoint (or read) the subgraph
|
|
520
|
-
* cursor. The advance is a CONDITIONAL monotonic UPDATE: concurrent
|
|
521
|
-
* writers serialize on it, and the loser's whole block tx rolls back
|
|
522
|
-
* (surfaced as `skipped`, never an error/gap).
|
|
523
|
-
* Either way: a crash can never leave committed deltas ahead of the
|
|
524
|
-
* relevant checkpoint.
|
|
525
|
-
*/
|
|
526
|
-
atomicProgress?: {
|
|
527
|
-
status: string
|
|
528
|
-
} | {
|
|
529
|
-
operationId: string
|
|
530
|
-
};
|
|
531
|
-
/**
|
|
532
|
-
* Reorg rewind (f069): set exclusively by `reorg.ts`'s fork-block
|
|
533
|
-
* reprocess. Bypasses the live-path replay guard (a fork block's height
|
|
534
|
-
* is, by construction, at or below the current cursor — the guard would
|
|
535
|
-
* otherwise skip it) and writes the checkpoint via
|
|
536
|
-
* `rewindLiveProgress` (unconditional — a rewind's whole point is
|
|
537
|
-
* moving the cursor *backward*, which the live path's conditional
|
|
538
|
-
* advance would refuse). Safe only because the reorg path holds a
|
|
539
|
-
* transaction-scoped advisory lock across its whole
|
|
540
|
-
* delete+reprocess+rewind sequence, so nothing else can interleave a
|
|
541
|
-
* forward write. Never set this outside `reorg.ts`.
|
|
542
|
-
*/
|
|
543
|
-
reorgRewind?: true;
|
|
544
|
-
/** The subgraph row's uuid. Required to record a gap row when a block
|
|
545
|
-
* processes with handler errors — `subgraph_gaps.subgraph_id` is NOT NULL.
|
|
546
|
-
* Absent on paths that pass `skipProgressUpdate` (they record gaps
|
|
547
|
-
* themselves, see reindex.ts). */
|
|
548
|
-
subgraphId?: string;
|
|
549
|
-
}
|
|
550
|
-
/** Default per-block retry schedule before a failure counts as persistent. */
|
|
551
|
-
declare const BLOCK_RETRY_DELAYS_MS: number[];
|
|
552
|
-
/**
|
|
553
|
-
* processBlock with bounded retries. Throws the last error once the schedule
|
|
554
|
-
* is exhausted — callers decide whether that halts the walk (strict paths) or
|
|
555
|
-
* records a gap (backfill). Never advances any cursor on failure.
|
|
556
|
-
*/
|
|
557
|
-
declare function processBlockWithRetry(subgraph: SubgraphDefinition, subgraphName: string, blockHeight: number, opts?: ProcessBlockOptions, retryDelaysMs?: number[]): Promise<ProcessBlockResult>;
|
|
558
|
-
declare function processBlock(subgraph: SubgraphDefinition, subgraphName: string, blockHeight: number, opts?: ProcessBlockOptions): Promise<ProcessBlockResult>;
|
|
559
|
-
export { resolveFactoryContracts, processBlockWithRetry, processBlock, invalidateSubgraphRoute, ProcessBlockTiming, ProcessBlockResult, ProcessBlockOptions, PreloadedBlockData, BLOCK_RETRY_DELAYS_MS };
|