@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.
Files changed (64) hide show
  1. package/README.md +13 -21
  2. package/dist/{src/index.d.ts → index.d.ts} +59 -189
  3. package/dist/index.js +2151 -0
  4. package/dist/index.js.map +24 -0
  5. package/dist/{src/runtime → runtime}/emitter.d.ts +1 -1
  6. package/dist/{src/runtime → runtime}/emitter.js +8 -12
  7. package/dist/{src/runtime → runtime}/emitter.js.map +3 -3
  8. package/dist/{src/runtime → runtime}/replay.d.ts +1 -1
  9. package/dist/{src/runtime → runtime}/replay.js +342 -683
  10. package/dist/{src/runtime → runtime}/replay.js.map +5 -5
  11. package/dist/{src/schema → schema}/index.d.ts +10 -69
  12. package/dist/{src/schema → schema}/index.js +188 -281
  13. package/dist/schema/index.js.map +13 -0
  14. package/dist/{src/testing → testing}/index.d.ts +65 -115
  15. package/dist/{src/testing → testing}/index.js +554 -968
  16. package/dist/testing/index.js.map +21 -0
  17. package/dist/{src/types.d.ts → types.d.ts} +10 -66
  18. package/dist/{src/validate.d.ts → validate.d.ts} +11 -69
  19. package/dist/{src/validate.js → validate.js} +18 -98
  20. package/dist/validate.js.map +10 -0
  21. package/package.json +18 -26
  22. package/dist/src/index.js +0 -6081
  23. package/dist/src/index.js.map +0 -44
  24. package/dist/src/runtime/block-processor.d.ts +0 -559
  25. package/dist/src/runtime/block-processor.js +0 -3810
  26. package/dist/src/runtime/block-processor.js.map +0 -31
  27. package/dist/src/runtime/catchup.d.ts +0 -473
  28. package/dist/src/runtime/catchup.js +0 -4228
  29. package/dist/src/runtime/catchup.js.map +0 -35
  30. package/dist/src/runtime/clarity.d.ts +0 -15
  31. package/dist/src/runtime/clarity.js +0 -41
  32. package/dist/src/runtime/clarity.js.map +0 -10
  33. package/dist/src/runtime/context.d.ts +0 -325
  34. package/dist/src/runtime/context.js +0 -936
  35. package/dist/src/runtime/context.js.map +0 -13
  36. package/dist/src/runtime/processor.d.ts +0 -11
  37. package/dist/src/runtime/processor.js +0 -5372
  38. package/dist/src/runtime/processor.js.map +0 -40
  39. package/dist/src/runtime/reindex.d.ts +0 -539
  40. package/dist/src/runtime/reindex.js +0 -4802
  41. package/dist/src/runtime/reindex.js.map +0 -37
  42. package/dist/src/runtime/reorg.d.ts +0 -432
  43. package/dist/src/runtime/reorg.js +0 -4355
  44. package/dist/src/runtime/reorg.js.map +0 -36
  45. package/dist/src/runtime/runner.d.ts +0 -719
  46. package/dist/src/runtime/runner.js +0 -1332
  47. package/dist/src/runtime/runner.js.map +0 -16
  48. package/dist/src/runtime/source-matcher.d.ts +0 -304
  49. package/dist/src/runtime/source-matcher.js +0 -335
  50. package/dist/src/runtime/source-matcher.js.map +0 -10
  51. package/dist/src/runtime/stats.d.ts +0 -23
  52. package/dist/src/runtime/stats.js +0 -71
  53. package/dist/src/runtime/stats.js.map +0 -10
  54. package/dist/src/schema/index.js.map +0 -13
  55. package/dist/src/service.d.ts +0 -0
  56. package/dist/src/service.js +0 -5398
  57. package/dist/src/service.js.map +0 -41
  58. package/dist/src/testing/index.js.map +0 -22
  59. package/dist/src/triggers/index.d.ts +0 -381
  60. package/dist/src/triggers/index.js +0 -31
  61. package/dist/src/triggers/index.js.map +0 -10
  62. package/dist/src/validate.js.map +0 -10
  63. /package/dist/{src/types.js → types.js} +0 -0
  64. /package/dist/{src/types.js.map → types.js.map} +0 -0
@@ -1,1332 +0,0 @@
1
- import { createRequire } from "node:module";
2
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
-
4
- // src/runtime/clarity.ts
5
- import { cvToValue, deserializeCV } from "@secondlayer/stacks/clarity";
6
- function decodeClarityValue(hex) {
7
- try {
8
- const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
9
- const cv = deserializeCV(cleanHex);
10
- return cvToValue(cv);
11
- } catch {
12
- return hex;
13
- }
14
- }
15
- function decodeEventData(data) {
16
- if (typeof data === "string" && data.startsWith("0x") && data.length > 10) {
17
- return decodeClarityValue(data);
18
- }
19
- if (Array.isArray(data)) {
20
- return data.map(decodeEventData);
21
- }
22
- if (typeof data === "object" && data !== null) {
23
- const decoded = {};
24
- for (const [key, value] of Object.entries(data)) {
25
- decoded[key] = decodeEventData(value);
26
- }
27
- return decoded;
28
- }
29
- return data;
30
- }
31
- function decodeFunctionArgs(args) {
32
- return args.map(decodeClarityValue);
33
- }
34
-
35
- // src/runtime/source-matcher.ts
36
- var patternCache = new Map;
37
- function matchAny(value, pattern) {
38
- return Array.isArray(pattern) ? pattern.some((p) => matchPattern(value, p)) : matchPattern(value, pattern);
39
- }
40
- function matchPattern(value, pattern) {
41
- if (!pattern.includes("*"))
42
- return value === pattern;
43
- let re = patternCache.get(pattern);
44
- if (!re) {
45
- const regex = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
46
- re = new RegExp(`^${regex}$`);
47
- patternCache.set(pattern, re);
48
- }
49
- return re.test(value);
50
- }
51
- var EMPTY_SET = new Set;
52
- function factoryAllows(filter, contractId, factoryContracts) {
53
- if (!filter.factory)
54
- return true;
55
- if (!contractId)
56
- return false;
57
- return (factoryContracts.get(filter.factory.from) ?? EMPTY_SET).has(contractId);
58
- }
59
- function readPath(payload, path) {
60
- let cur = payload;
61
- for (const key of path.split(".")) {
62
- if (cur === null || typeof cur !== "object")
63
- return;
64
- cur = cur[key];
65
- }
66
- return cur;
67
- }
68
- function traitAllows(filter, contractId, traitContracts) {
69
- const trait = filter.trait;
70
- if (!trait)
71
- return true;
72
- if (!contractId)
73
- return false;
74
- return (traitContracts.get(trait) ?? EMPTY_SET).has(contractId);
75
- }
76
- function assetContract(assetId) {
77
- return assetId?.split("::")[0];
78
- }
79
- function printContractId(data) {
80
- if (!data)
81
- return;
82
- const id = (typeof data.contract_identifier === "string" ? data.contract_identifier : undefined) ?? (typeof data.contract_id === "string" ? data.contract_id : undefined);
83
- return id || undefined;
84
- }
85
- function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts = new Map, vmEventsByTx = new Map) {
86
- const results = [];
87
- switch (filter.type) {
88
- case "stx_transfer":
89
- case "stx_mint":
90
- case "stx_burn":
91
- case "stx_lock": {
92
- const eventType = `${filter.type}_event`;
93
- for (const tx of transactions) {
94
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
95
- const matched = txEvents.filter((e) => e.type === eventType);
96
- if (matched.length === 0)
97
- continue;
98
- const filtered = matched.filter((e) => {
99
- const data = e.data;
100
- if (!data)
101
- return false;
102
- if ("sender" in filter && filter.sender) {
103
- if (!matchPattern(data.sender, filter.sender))
104
- return false;
105
- }
106
- if ("recipient" in filter && filter.recipient) {
107
- if (!matchPattern(data.recipient, filter.recipient))
108
- return false;
109
- }
110
- if ("lockedAddress" in filter && filter.lockedAddress) {
111
- if (!matchPattern(data.locked_address, filter.lockedAddress))
112
- return false;
113
- }
114
- if ("minAmount" in filter && filter.minAmount !== undefined) {
115
- const amount = BigInt(data.amount ?? data.locked_amount ?? "0");
116
- if (amount < filter.minAmount)
117
- return false;
118
- }
119
- if ("maxAmount" in filter && filter.maxAmount !== undefined) {
120
- const amount = BigInt(data.amount ?? "0");
121
- if (amount > filter.maxAmount)
122
- return false;
123
- }
124
- return true;
125
- });
126
- if (filtered.length > 0) {
127
- results.push({ tx, events: filtered });
128
- }
129
- }
130
- break;
131
- }
132
- case "ft_transfer":
133
- case "ft_mint":
134
- case "ft_burn": {
135
- const eventType = `${filter.type}_event`;
136
- for (const tx of transactions) {
137
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
138
- const matched = txEvents.filter((e) => {
139
- if (e.type !== eventType)
140
- return false;
141
- const data = e.data;
142
- if (!data)
143
- return false;
144
- if (filter.assetIdentifier) {
145
- const assetId = data.asset_identifier;
146
- if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
147
- return false;
148
- }
149
- if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
150
- return false;
151
- if ("sender" in filter && filter.sender) {
152
- if (!matchPattern(data.sender, filter.sender))
153
- return false;
154
- }
155
- if ("recipient" in filter && filter.recipient) {
156
- if (!matchPattern(data.recipient, filter.recipient))
157
- return false;
158
- }
159
- if (filter.minAmount !== undefined) {
160
- const amount = BigInt(data.amount ?? "0");
161
- if (amount < filter.minAmount)
162
- return false;
163
- }
164
- return true;
165
- });
166
- if (matched.length > 0) {
167
- results.push({ tx, events: matched });
168
- }
169
- }
170
- break;
171
- }
172
- case "nft_transfer":
173
- case "nft_mint":
174
- case "nft_burn": {
175
- const eventType = `${filter.type}_event`;
176
- for (const tx of transactions) {
177
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
178
- const matched = txEvents.filter((e) => {
179
- if (e.type !== eventType)
180
- return false;
181
- const data = e.data;
182
- if (!data)
183
- return false;
184
- if (filter.assetIdentifier) {
185
- const assetId = data.asset_identifier;
186
- if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
187
- return false;
188
- }
189
- if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
190
- return false;
191
- if ("sender" in filter && filter.sender) {
192
- if (!matchPattern(data.sender, filter.sender))
193
- return false;
194
- }
195
- if ("recipient" in filter && filter.recipient) {
196
- if (!matchPattern(data.recipient, filter.recipient))
197
- return false;
198
- }
199
- return true;
200
- });
201
- if (matched.length > 0) {
202
- results.push({ tx, events: matched });
203
- }
204
- }
205
- break;
206
- }
207
- case "contract_call": {
208
- for (const tx of transactions) {
209
- if (tx.type !== "contract_call")
210
- continue;
211
- if (filter.contractId) {
212
- if (!tx.contract_id || !matchAny(tx.contract_id, filter.contractId))
213
- continue;
214
- }
215
- if (!factoryAllows(filter, tx.contract_id, factoryContracts))
216
- continue;
217
- if (filter.functionName) {
218
- if (!tx.function_name || !matchPattern(tx.function_name, filter.functionName))
219
- continue;
220
- }
221
- if (filter.caller) {
222
- if (!matchPattern(tx.sender, filter.caller))
223
- continue;
224
- }
225
- if (!traitAllows(filter, tx.contract_id, traitContracts))
226
- continue;
227
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
228
- results.push({ tx, events: txEvents });
229
- }
230
- break;
231
- }
232
- case "contract_deploy": {
233
- for (const tx of transactions) {
234
- if (tx.type !== "smart_contract")
235
- continue;
236
- if (filter.deployer) {
237
- if (!matchPattern(tx.sender, filter.deployer))
238
- continue;
239
- }
240
- if (filter.contractName) {
241
- const name = tx.contract_id?.split(".")[1] ?? "";
242
- if (!matchPattern(name, filter.contractName))
243
- continue;
244
- }
245
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
246
- results.push({ tx, events: txEvents });
247
- }
248
- break;
249
- }
250
- case "nested_contract_call":
251
- case "var_set":
252
- case "map_set":
253
- case "map_insert":
254
- case "map_delete": {
255
- for (const tx of transactions) {
256
- const txEvents = vmEventsByTx.get(tx.tx_id) ?? [];
257
- const matched = txEvents.filter((e) => {
258
- if (e.type !== filter.type)
259
- return false;
260
- const data = e.data;
261
- if (!data)
262
- return false;
263
- const contractId = printContractId(data);
264
- if ("contractId" in filter && filter.contractId) {
265
- if (!contractId || !matchAny(contractId, filter.contractId))
266
- return false;
267
- }
268
- if (!traitAllows(filter, contractId, traitContracts))
269
- return false;
270
- if (!factoryAllows(filter, contractId, factoryContracts))
271
- return false;
272
- if (filter.type === "nested_contract_call") {
273
- if (filter.functionName) {
274
- if (typeof data.function_name !== "string" || !matchPattern(data.function_name, filter.functionName))
275
- return false;
276
- }
277
- if (filter.caller) {
278
- if (typeof data.caller !== "string" || !matchPattern(data.caller, filter.caller))
279
- return false;
280
- }
281
- if (filter.sender) {
282
- if (typeof data.sender !== "string" || !matchPattern(data.sender, filter.sender))
283
- return false;
284
- }
285
- }
286
- if (filter.type === "var_set" && filter.varName) {
287
- if (typeof data.var_name !== "string" || !matchPattern(data.var_name, filter.varName))
288
- return false;
289
- }
290
- if ((filter.type === "map_set" || filter.type === "map_insert" || filter.type === "map_delete") && filter.map) {
291
- if (typeof data.map_name !== "string" || !matchPattern(data.map_name, filter.map))
292
- return false;
293
- }
294
- return true;
295
- });
296
- if (matched.length > 0)
297
- results.push({ tx, events: matched });
298
- }
299
- break;
300
- }
301
- case "print_event": {
302
- for (const tx of transactions) {
303
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
304
- const matched = txEvents.filter((e) => {
305
- if (e.type !== "smart_contract_event" && e.type !== "contract_event")
306
- return false;
307
- const data = e.data;
308
- if (!data)
309
- return false;
310
- if (data.topic !== "print")
311
- return false;
312
- const contractId = printContractId(data);
313
- if (filter.contractId) {
314
- if (!contractId || !matchAny(contractId, filter.contractId))
315
- return false;
316
- }
317
- if (!traitAllows(filter, contractId, traitContracts))
318
- return false;
319
- if (!factoryAllows(filter, contractId, factoryContracts))
320
- return false;
321
- return true;
322
- });
323
- if (matched.length > 0) {
324
- results.push({ tx, events: matched });
325
- }
326
- }
327
- break;
328
- }
329
- }
330
- return results;
331
- }
332
- function matchSources(sources, transactions, events, traitContracts = new Map, factoryContracts = new Map, vmEvents = []) {
333
- const eventsByTx = new Map;
334
- for (const event of events) {
335
- const list = eventsByTx.get(event.tx_id) ?? [];
336
- list.push(event);
337
- eventsByTx.set(event.tx_id, list);
338
- }
339
- const vmEventsByTx = new Map;
340
- for (const event of vmEvents) {
341
- const list = vmEventsByTx.get(event.tx_id) ?? [];
342
- list.push(event);
343
- vmEventsByTx.set(event.tx_id, list);
344
- }
345
- const seen = new Set;
346
- const results = [];
347
- for (const [sourceName, filter] of Object.entries(sources)) {
348
- const matches = matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts, vmEventsByTx);
349
- for (const match of matches) {
350
- const dedupeKey = `${match.tx.tx_id}:${sourceName}`;
351
- if (!seen.has(dedupeKey)) {
352
- seen.add(dedupeKey);
353
- results.push({ ...match, sourceName });
354
- }
355
- }
356
- }
357
- return results;
358
- }
359
-
360
- // src/runtime/runner.ts
361
- import { getErrorMessage } from "@secondlayer/shared";
362
- import { logger as logger2 } from "@secondlayer/shared/logger";
363
- import {
364
- clarityValueToJS,
365
- deserializeCV as deserializeCV3,
366
- toCamelCase
367
- } from "@secondlayer/stacks/clarity";
368
-
369
- // src/print-schema.ts
370
- import {
371
- deserializeCV as deserializeCV2
372
- } from "@secondlayer/stacks/clarity";
373
- function camelizeDataKey(str) {
374
- return str.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
375
- }
376
- function camelizeKeys(obj) {
377
- if (obj === null || obj === undefined)
378
- return obj;
379
- if (typeof obj !== "object")
380
- return obj;
381
- if (Array.isArray(obj))
382
- return obj.map(camelizeKeys);
383
- const result = {};
384
- for (const [k, v] of Object.entries(obj)) {
385
- result[camelizeDataKey(k)] = camelizeKeys(v);
386
- }
387
- return result;
388
- }
389
- function cvToTree(cv) {
390
- switch (cv.type) {
391
- case "uint":
392
- case "int":
393
- return { kind: cv.type };
394
- case "true":
395
- case "false":
396
- return { kind: "bool" };
397
- case "address":
398
- case "contract":
399
- return { kind: "principal" };
400
- case "buffer":
401
- return { kind: "buffer", len: cv.value.length / 2 };
402
- case "ascii":
403
- return { kind: "ascii", len: cv.value.length };
404
- case "utf8":
405
- return { kind: "utf8", len: new TextEncoder().encode(cv.value).length };
406
- case "none":
407
- return { kind: "optional", inner: null };
408
- case "some":
409
- return { kind: "optional", inner: cvToTree(cv.value) };
410
- case "ok":
411
- return { kind: "response", ok: cvToTree(cv.value), err: null };
412
- case "err":
413
- return { kind: "response", ok: null, err: cvToTree(cv.value) };
414
- case "list": {
415
- let inner = null;
416
- for (const el of cv.value) {
417
- const t = cvToTree(el);
418
- inner = inner ? unify(inner, t) : t;
419
- }
420
- return { kind: "list", inner };
421
- }
422
- case "tuple": {
423
- const entries = new Map;
424
- for (const [k, v] of Object.entries(cv.value)) {
425
- entries.set(k, { tree: cvToTree(v), present: 1 });
426
- }
427
- return { kind: "tuple", count: 1, entries };
428
- }
429
- }
430
- }
431
- function unifyNullable(a, b) {
432
- if (!a)
433
- return b;
434
- if (!b)
435
- return a;
436
- return unify(a, b);
437
- }
438
- var UNION_KIND_ORDER = {
439
- uint: 0,
440
- int: 1,
441
- bool: 2,
442
- principal: 3,
443
- buffer: 4,
444
- ascii: 5,
445
- utf8: 6,
446
- list: 7,
447
- tuple: 8,
448
- response: 9
449
- };
450
- function makeUnion(members) {
451
- const sorted = [...members].sort((a, b) => (UNION_KIND_ORDER[a.kind] ?? 99) - (UNION_KIND_ORDER[b.kind] ?? 99));
452
- return { kind: "union", members: sorted };
453
- }
454
- function unify(a, b) {
455
- if (a.kind === "optional" || b.kind === "optional") {
456
- const ai = a.kind === "optional" ? a.inner : a;
457
- const bi = b.kind === "optional" ? b.inner : b;
458
- return { kind: "optional", inner: unifyNullable(ai, bi) };
459
- }
460
- if (a.kind === "union")
461
- return unionAdd(a.members, b);
462
- if (b.kind === "union")
463
- return unionAdd(b.members, a);
464
- if (a.kind !== b.kind)
465
- return makeUnion([a, b]);
466
- switch (a.kind) {
467
- case "uint":
468
- case "int":
469
- case "bool":
470
- case "principal":
471
- return a;
472
- case "buffer":
473
- case "ascii":
474
- case "utf8":
475
- return { kind: a.kind, len: Math.max(a.len, b.len) };
476
- case "list":
477
- return {
478
- kind: "list",
479
- inner: unifyNullable(a.inner, b.inner)
480
- };
481
- case "response": {
482
- const rb = b;
483
- return {
484
- kind: "response",
485
- ok: unifyNullable(a.ok, rb.ok),
486
- err: unifyNullable(a.err, rb.err)
487
- };
488
- }
489
- case "tuple": {
490
- const tb = b;
491
- const entries = new Map([...a.entries].map(([k, e]) => [k, { ...e }]));
492
- for (const [k, e] of tb.entries) {
493
- const existing = entries.get(k);
494
- entries.set(k, existing ? {
495
- tree: unify(existing.tree, e.tree),
496
- present: existing.present + e.present
497
- } : { ...e });
498
- }
499
- return { kind: "tuple", count: a.count + tb.count, entries };
500
- }
501
- }
502
- }
503
- function unionAdd(members, t) {
504
- if (t.kind === "union") {
505
- let acc = { kind: "union", members };
506
- for (const m of t.members)
507
- acc = unify(acc, m);
508
- return acc;
509
- }
510
- const next = [...members];
511
- for (let i = 0;i < next.length; i++) {
512
- const member = next[i];
513
- if (!member)
514
- continue;
515
- const merged = unify(member, t);
516
- if (merged.kind !== "union") {
517
- next[i] = merged;
518
- return makeUnion(next);
519
- }
520
- }
521
- next.push(t);
522
- return makeUnion(next);
523
- }
524
- function wrapOptional(t) {
525
- return t.kind === "optional" ? t : { kind: "optional", inner: t };
526
- }
527
- function renderClarity(t) {
528
- if (!t)
529
- return "?";
530
- switch (t.kind) {
531
- case "uint":
532
- case "int":
533
- case "bool":
534
- case "principal":
535
- return t.kind;
536
- case "buffer":
537
- return `(buff ${t.len})`;
538
- case "ascii":
539
- return `(string-ascii ${t.len})`;
540
- case "utf8":
541
- return `(string-utf8 ${t.len})`;
542
- case "optional":
543
- return `(optional ${renderClarity(t.inner)})`;
544
- case "list":
545
- return `(list ${renderClarity(t.inner)})`;
546
- case "response":
547
- return `(response ${renderClarity(t.ok)} ${renderClarity(t.err)})`;
548
- case "tuple": {
549
- const parts = [...t.entries].map(([k, e]) => {
550
- const tree = e.present < t.count ? wrapOptional(e.tree) : e.tree;
551
- return `(${k} ${renderClarity(tree)})`;
552
- });
553
- return `(tuple ${parts.join(" ")})`;
554
- }
555
- case "union":
556
- return t.members.map(renderClarity).join(" | ");
557
- }
558
- }
559
- function renderTs(t) {
560
- if (!t)
561
- return "unknown";
562
- switch (t.kind) {
563
- case "uint":
564
- case "int":
565
- return "bigint";
566
- case "bool":
567
- return "boolean";
568
- case "principal":
569
- case "buffer":
570
- case "ascii":
571
- case "utf8":
572
- return "string";
573
- case "optional":
574
- return t.inner ? `${renderTs(t.inner)} | null` : "unknown | null";
575
- case "list": {
576
- const inner = renderTs(t.inner);
577
- return inner.includes(" | ") ? `(${inner})[]` : `${inner}[]`;
578
- }
579
- case "response": {
580
- const sides = [...new Set([renderTs(t.ok), renderTs(t.err)])];
581
- return sides.join(" | ");
582
- }
583
- case "tuple": {
584
- const parts = [...t.entries].map(([k, e]) => {
585
- const opt = e.present < t.count ? "?" : "";
586
- return `${camelizeDataKey(k)}${opt}: ${renderTs(e.tree)}`;
587
- });
588
- return `{ ${parts.join("; ")} }`;
589
- }
590
- case "union":
591
- return [...new Set(t.members.map((m) => renderTs(m)))].join(" | ");
592
- }
593
- }
594
- function toColumnType(t) {
595
- if (!t)
596
- return "jsonb";
597
- switch (t.kind) {
598
- case "uint":
599
- return "uint";
600
- case "int":
601
- return "int";
602
- case "bool":
603
- return "boolean";
604
- case "principal":
605
- return "principal";
606
- case "buffer":
607
- case "ascii":
608
- case "utf8":
609
- return "text";
610
- case "list":
611
- case "tuple":
612
- return "jsonb";
613
- case "optional":
614
- return toColumnType(t.inner);
615
- case "response":
616
- return t.ok ? toColumnType(t.ok) : "jsonb";
617
- case "union":
618
- return "jsonb";
619
- }
620
- }
621
- var MAX_DECODED_PER_TOPIC_NEWEST = 75;
622
- var MAX_DECODED_PER_TOPIC_OLDEST = 25;
623
- function inferPrintTopics(samples) {
624
- const groups = new Map;
625
- for (const s of samples) {
626
- const group = groups.get(s.topic);
627
- if (group)
628
- group.push(s);
629
- else
630
- groups.set(s.topic, [s]);
631
- }
632
- const out = [];
633
- for (const [topic, rows] of groups) {
634
- let first = Number.POSITIVE_INFINITY;
635
- let last = Number.NEGATIVE_INFINITY;
636
- for (const r of rows) {
637
- if (r.blockHeight < first)
638
- first = r.blockHeight;
639
- if (r.blockHeight > last)
640
- last = r.blockHeight;
641
- }
642
- const withHex = [...rows].filter((r) => r.rawHex !== null).sort((a, b) => b.blockHeight - a.blockHeight);
643
- const budget = MAX_DECODED_PER_TOPIC_NEWEST + MAX_DECODED_PER_TOPIC_OLDEST;
644
- const picked = withHex.length <= budget ? withHex : [
645
- ...withHex.slice(0, MAX_DECODED_PER_TOPIC_NEWEST),
646
- ...withHex.slice(-MAX_DECODED_PER_TOPIC_OLDEST)
647
- ];
648
- const tuples = [];
649
- let decoded = 0;
650
- for (const p of picked) {
651
- try {
652
- const cv = deserializeCV2(p.rawHex);
653
- decoded++;
654
- if (cv.type === "tuple")
655
- tuples.push(cv);
656
- } catch {}
657
- }
658
- const nonTuple = decoded > 0 && tuples.length === 0;
659
- const fields = [];
660
- if (!nonTuple) {
661
- const stats = new Map;
662
- for (const t of tuples) {
663
- for (const [key, value] of Object.entries(t.value)) {
664
- if (key === "topic")
665
- continue;
666
- const tree = cvToTree(value);
667
- const existing = stats.get(key);
668
- if (existing) {
669
- existing.present++;
670
- existing.tree = unify(existing.tree, tree);
671
- if (value.type === "none")
672
- existing.noneCount++;
673
- if (value.type === "none" || value.type === "some") {
674
- existing.optionalSeen = true;
675
- }
676
- } else {
677
- stats.set(key, {
678
- tree,
679
- present: 1,
680
- noneCount: value.type === "none" ? 1 : 0,
681
- optionalSeen: value.type === "none" || value.type === "some"
682
- });
683
- }
684
- }
685
- }
686
- for (const [name, st] of [...stats].sort(([a], [b]) => a.localeCompare(b))) {
687
- const field = {
688
- name,
689
- camel_name: camelizeDataKey(name),
690
- clarity_type: renderClarity(st.tree),
691
- ts_type: renderTs(st.tree),
692
- column_type: toColumnType(st.tree),
693
- always_present: st.present === tuples.length
694
- };
695
- if (st.optionalSeen) {
696
- field.optional_some_rate = (st.present - st.noneCount) / st.present;
697
- }
698
- fields.push(field);
699
- }
700
- }
701
- out.push({
702
- topic,
703
- count: rows.length,
704
- first_height: first,
705
- last_height: last,
706
- non_tuple: nonTuple,
707
- fields
708
- });
709
- }
710
- return out.sort((a, b) => b.count - a.count);
711
- }
712
-
713
- // src/runtime/materialize.ts
714
- function resolveColumn(col, event, tx, block) {
715
- if ("fromTx" in col) {
716
- return { ok: true, value: tx[col.fromTx] };
717
- }
718
- if ("fromBlock" in col) {
719
- return { ok: true, value: block[col.fromBlock] };
720
- }
721
- const data = event.data;
722
- const dataObj = data !== null && typeof data === "object" && !Array.isArray(data) ? data : undefined;
723
- if (dataObj && Object.prototype.hasOwnProperty.call(dataObj, col.from)) {
724
- return { ok: true, value: dataObj[col.from] };
725
- }
726
- if (col.from === "topic" && "topic" in event) {
727
- return { ok: true, value: event.topic };
728
- }
729
- if (Object.prototype.hasOwnProperty.call(event, col.from)) {
730
- return { ok: true, value: event[col.from] };
731
- }
732
- return { ok: false, missing: col.from };
733
- }
734
- function buildMaterializeRow(spec, event, ctx, tableColumns) {
735
- const row = {};
736
- for (const [colName, mapping] of Object.entries(spec.columns)) {
737
- const resolved = resolveColumn(mapping, event, ctx.tx, ctx.block);
738
- if (!resolved.ok) {
739
- const colDef = tableColumns[colName];
740
- if (colDef?.nullable) {
741
- row[colName] = null;
742
- continue;
743
- }
744
- return {
745
- ok: false,
746
- reason: `required materialize field "${resolved.missing}" missing for column "${colName}"`
747
- };
748
- }
749
- row[colName] = resolved.value ?? null;
750
- }
751
- return { ok: true, row };
752
- }
753
- function applyMaterializeInsert(spec, event, ctx, schema) {
754
- const table = schema[spec.table];
755
- if (!table) {
756
- return { ok: false, reason: `materialize table "${spec.table}" missing` };
757
- }
758
- const built = buildMaterializeRow(spec, event, ctx, table.columns);
759
- if (!built.ok)
760
- return built;
761
- ctx.insert(spec.table, built.row);
762
- return { ok: true };
763
- }
764
-
765
- // src/runtime/print-validate.ts
766
- var COLUMN_TYPES = new Set([
767
- "text",
768
- "uint",
769
- "int",
770
- "principal",
771
- "boolean",
772
- "timestamp",
773
- "jsonb"
774
- ]);
775
- function isColumnType(field) {
776
- return typeof field === "string" && COLUMN_TYPES.has(field);
777
- }
778
- function matchesField(field, value) {
779
- if (isColumnType(field)) {
780
- switch (field) {
781
- case "uint":
782
- case "int":
783
- return typeof value === "bigint" || typeof value === "number";
784
- case "boolean":
785
- return typeof value === "boolean";
786
- case "text":
787
- case "principal":
788
- case "timestamp":
789
- return typeof value === "string" || typeof value === "number" || typeof value === "bigint";
790
- case "jsonb":
791
- return value !== undefined;
792
- }
793
- }
794
- if (typeof field !== "object" || field === null)
795
- return false;
796
- if ("optional" in field) {
797
- return value === undefined || value === null || matchesField(field.type, value);
798
- }
799
- if ("tuple" in field) {
800
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
801
- return false;
802
- }
803
- return matchesTopic(field.tuple, value).ok;
804
- }
805
- if ("list" in field) {
806
- return Array.isArray(value) && value.every((item) => matchesField(field.list, item));
807
- }
808
- return false;
809
- }
810
- function matchesTopic(declared, data) {
811
- for (const [name, field] of Object.entries(declared)) {
812
- const present = name in data;
813
- const optional = typeof field === "object" && field !== null && "optional" in field;
814
- if (!present) {
815
- if (optional)
816
- continue;
817
- return { ok: false, reason: `missing declared field "${name}"` };
818
- }
819
- if (!matchesField(field, data[name])) {
820
- return {
821
- ok: false,
822
- reason: `field "${name}" does not match its declared type (got ${describe(data[name])})`
823
- };
824
- }
825
- }
826
- return { ok: true };
827
- }
828
- function describe(value) {
829
- if (value === null)
830
- return "null";
831
- if (Array.isArray(value))
832
- return "array";
833
- return typeof value;
834
- }
835
- function validatePrintPayload(prints, topic, data) {
836
- if (!prints)
837
- return { ok: true };
838
- const declared = prints[topic];
839
- if (!declared)
840
- return { ok: true };
841
- return matchesTopic(declared, data);
842
- }
843
-
844
- // src/runtime/violations.ts
845
- import { getTargetDb } from "@secondlayer/shared/db";
846
- import { logger } from "@secondlayer/shared/logger";
847
- import { sql } from "kysely";
848
- var VIOLATIONS_CAP_PER_SUBGRAPH = 100;
849
- function jsonSafe(value) {
850
- if (typeof value === "bigint")
851
- return value.toString();
852
- if (value === null || value === undefined)
853
- return value;
854
- if (Array.isArray(value))
855
- return value.map(jsonSafe);
856
- if (typeof value === "object") {
857
- const out = {};
858
- for (const [k, v] of Object.entries(value)) {
859
- out[k] = jsonSafe(v);
860
- }
861
- return out;
862
- }
863
- return value;
864
- }
865
- async function defaultRecordPrintViolation(input) {
866
- try {
867
- const db = getTargetDb();
868
- const sample = jsonSafe(input.samplePayload);
869
- await db.insertInto("subgraph_violations").values({
870
- subgraph_name: input.subgraphName,
871
- source_name: input.sourceName,
872
- block_height: input.blockHeight,
873
- tx_id: input.txId,
874
- reason: input.reason,
875
- sample_payload: sample
876
- }).execute();
877
- await sql`
878
- DELETE FROM subgraph_violations
879
- WHERE id IN (
880
- SELECT id FROM subgraph_violations
881
- WHERE subgraph_name = ${input.subgraphName}
882
- ORDER BY seen_at DESC
883
- OFFSET ${VIOLATIONS_CAP_PER_SUBGRAPH}
884
- )
885
- `.execute(db);
886
- } catch (err) {
887
- logger.warn("Failed to record print violation", {
888
- subgraph: input.subgraphName,
889
- error: err instanceof Error ? err.message : String(err)
890
- });
891
- }
892
- }
893
- var recorder = defaultRecordPrintViolation;
894
- async function recordPrintViolation(input) {
895
- await recorder(input);
896
- }
897
-
898
- // src/runtime/runner.ts
899
- var DEFAULT_ERROR_THRESHOLD = 50;
900
- function decodeFunctionArgs2(args) {
901
- let parsed = args;
902
- if (typeof parsed === "string") {
903
- try {
904
- parsed = JSON.parse(parsed);
905
- } catch {
906
- return [];
907
- }
908
- }
909
- if (!Array.isArray(parsed))
910
- return [];
911
- return parsed.map((arg) => {
912
- if (typeof arg === "string")
913
- return decodeClarityValue(arg);
914
- return arg;
915
- });
916
- }
917
- function decodeRawResult(raw) {
918
- if (typeof raw === "string" && raw.length > 2) {
919
- return decodeClarityValue(raw);
920
- }
921
- return null;
922
- }
923
- function decodeRawHex(hex) {
924
- if (hex.startsWith("0x") && hex.length > 2) {
925
- return decodeClarityValue(hex);
926
- }
927
- return hex;
928
- }
929
- function safeBigInt(val) {
930
- if (typeof val === "bigint")
931
- return val;
932
- if (typeof val === "number")
933
- return BigInt(val);
934
- if (typeof val === "string") {
935
- try {
936
- return BigInt(val);
937
- } catch {
938
- return 0n;
939
- }
940
- }
941
- return 0n;
942
- }
943
- function buildContractCallInput(filter, tx) {
944
- const abi = filter.abi;
945
- const fnName = tx.function_name;
946
- if (!abi || !fnName)
947
- return;
948
- const fn = abi.functions?.find((f) => f.name === fnName);
949
- if (!fn || !Array.isArray(fn.args))
950
- return;
951
- let rawArgs = tx.function_args;
952
- if (typeof rawArgs === "string") {
953
- try {
954
- rawArgs = JSON.parse(rawArgs);
955
- } catch {
956
- return;
957
- }
958
- }
959
- if (!Array.isArray(rawArgs))
960
- return;
961
- const decodeArg = clarityValueToJS;
962
- const input = {};
963
- fn.args.forEach((arg, i) => {
964
- const hex = rawArgs[i];
965
- if (typeof hex !== "string")
966
- return;
967
- try {
968
- const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
969
- input[toCamelCase(arg.name)] = decodeArg(arg.type, deserializeCV3(clean));
970
- } catch {}
971
- });
972
- return input;
973
- }
974
- function buildEventPayload(filter, tx, event) {
975
- const txMeta = {
976
- txId: tx.tx_id,
977
- sender: tx.sender,
978
- type: tx.type,
979
- status: tx.status,
980
- contractId: tx.contract_id ?? null,
981
- functionName: tx.function_name ?? null
982
- };
983
- const decodedArgs = decodeFunctionArgs2(tx.function_args);
984
- const decodedResult = decodeRawResult(tx.raw_result);
985
- if (!event) {
986
- switch (filter.type) {
987
- case "contract_call": {
988
- const input = buildContractCallInput(filter, tx);
989
- return {
990
- type: "contract_call",
991
- contractId: tx.contract_id ?? "",
992
- functionName: tx.function_name ?? "",
993
- sender: tx.sender,
994
- args: decodedArgs,
995
- ...input !== undefined ? { input } : {},
996
- result: decodedResult,
997
- resultHex: tx.raw_result ?? null,
998
- tx: txMeta
999
- };
1000
- }
1001
- case "contract_deploy":
1002
- return {
1003
- contractId: tx.contract_id ?? "",
1004
- deployer: tx.sender,
1005
- tx: txMeta
1006
- };
1007
- default:
1008
- return { tx: txMeta };
1009
- }
1010
- }
1011
- const decoded = decodeEventData(event.data);
1012
- switch (filter.type) {
1013
- case "ft_transfer":
1014
- return {
1015
- sender: decoded.sender,
1016
- recipient: decoded.recipient,
1017
- amount: safeBigInt(decoded.amount),
1018
- assetIdentifier: decoded.asset_identifier,
1019
- tx: txMeta
1020
- };
1021
- case "ft_mint":
1022
- return {
1023
- recipient: decoded.recipient,
1024
- amount: safeBigInt(decoded.amount),
1025
- assetIdentifier: decoded.asset_identifier,
1026
- tx: txMeta
1027
- };
1028
- case "ft_burn":
1029
- return {
1030
- sender: decoded.sender,
1031
- amount: safeBigInt(decoded.amount),
1032
- assetIdentifier: decoded.asset_identifier,
1033
- tx: txMeta
1034
- };
1035
- case "nft_transfer":
1036
- return {
1037
- sender: decoded.sender,
1038
- recipient: decoded.recipient,
1039
- tokenId: decoded.raw_value ?? decoded.value,
1040
- assetIdentifier: decoded.asset_identifier,
1041
- tx: txMeta
1042
- };
1043
- case "nft_mint":
1044
- return {
1045
- recipient: decoded.recipient,
1046
- tokenId: decoded.raw_value ?? decoded.value,
1047
- assetIdentifier: decoded.asset_identifier,
1048
- tx: txMeta
1049
- };
1050
- case "nft_burn":
1051
- return {
1052
- sender: decoded.sender,
1053
- tokenId: decoded.raw_value ?? decoded.value,
1054
- assetIdentifier: decoded.asset_identifier,
1055
- tx: txMeta
1056
- };
1057
- case "stx_transfer":
1058
- return {
1059
- sender: decoded.sender,
1060
- recipient: decoded.recipient,
1061
- amount: safeBigInt(decoded.amount),
1062
- memo: decoded.memo ?? "",
1063
- tx: txMeta
1064
- };
1065
- case "stx_mint":
1066
- return {
1067
- recipient: decoded.recipient,
1068
- amount: safeBigInt(decoded.amount),
1069
- tx: txMeta
1070
- };
1071
- case "stx_burn":
1072
- return {
1073
- sender: decoded.sender,
1074
- amount: safeBigInt(decoded.amount),
1075
- tx: txMeta
1076
- };
1077
- case "stx_lock":
1078
- return {
1079
- lockedAddress: decoded.locked_address,
1080
- lockedAmount: safeBigInt(decoded.locked_amount),
1081
- unlockHeight: safeBigInt(decoded.unlock_height),
1082
- tx: txMeta
1083
- };
1084
- case "print_event": {
1085
- const rawHex = event.data?.raw_value;
1086
- const rawValue = typeof rawHex === "string" && rawHex.startsWith("0x") ? decodeClarityValue(rawHex) : decoded.value;
1087
- const clarityObj = rawValue && typeof rawValue === "object" && !Array.isArray(rawValue) ? rawValue : null;
1088
- const topic = clarityObj?.topic ? String(clarityObj.topic) : decoded.topic ?? "";
1089
- const { topic: _, ...rest } = clarityObj ?? {};
1090
- const data = Object.keys(rest).length > 0 ? camelizeKeys(rest) : rawValue && typeof rawValue !== "object" ? rawValue : {};
1091
- return {
1092
- contractId: printContractId(decoded) ?? tx.contract_id ?? "",
1093
- topic,
1094
- data: data ?? {},
1095
- tx: txMeta
1096
- };
1097
- }
1098
- case "contract_call": {
1099
- const ccRawHex = event.data?.raw_value;
1100
- const normalized = typeof ccRawHex === "string" && ccRawHex.startsWith("0x") ? { ...decoded, value: decodeClarityValue(ccRawHex) } : decoded;
1101
- const input = buildContractCallInput(filter, tx);
1102
- return {
1103
- ...normalized,
1104
- type: "contract_call",
1105
- _eventType: event.type,
1106
- contractId: tx.contract_id ?? "",
1107
- functionName: tx.function_name ?? "",
1108
- sender: tx.sender,
1109
- args: decodedArgs,
1110
- ...input !== undefined ? { input } : {},
1111
- result: decodedResult,
1112
- resultHex: tx.raw_result ?? null,
1113
- tx: txMeta
1114
- };
1115
- }
1116
- case "contract_deploy":
1117
- return {
1118
- contractId: tx.contract_id ?? "",
1119
- deployer: tx.sender,
1120
- tx: txMeta
1121
- };
1122
- case "nested_contract_call": {
1123
- const data = event.data ?? {};
1124
- const argsHex = Array.isArray(data.function_args) ? data.function_args.filter((a) => typeof a === "string") : [];
1125
- return {
1126
- contractId: printContractId(data) ?? "",
1127
- sender: typeof data.sender === "string" ? data.sender : null,
1128
- caller: typeof data.caller === "string" ? data.caller : "",
1129
- functionName: typeof data.function_name === "string" ? data.function_name : "",
1130
- args: decodeFunctionArgs2(argsHex),
1131
- arguments: argsHex,
1132
- rawResult: typeof data.raw_result === "string" ? data.raw_result : "",
1133
- result: decodeRawResult(data.raw_result),
1134
- tx: txMeta
1135
- };
1136
- }
1137
- case "var_set": {
1138
- const data = event.data ?? {};
1139
- const rawValue = typeof data.raw_value === "string" ? data.raw_value : "";
1140
- return {
1141
- contractId: printContractId(data) ?? "",
1142
- varName: typeof data.var_name === "string" ? data.var_name : "",
1143
- rawValue,
1144
- value: decodeRawHex(rawValue),
1145
- tx: txMeta
1146
- };
1147
- }
1148
- case "map_set":
1149
- case "map_insert": {
1150
- const data = event.data ?? {};
1151
- const rawKey = typeof data.raw_key === "string" ? data.raw_key : "";
1152
- const rawValue = typeof data.raw_value === "string" ? data.raw_value : "";
1153
- return {
1154
- contractId: printContractId(data) ?? "",
1155
- map: typeof data.map_name === "string" ? data.map_name : "",
1156
- rawKey,
1157
- key: decodeRawHex(rawKey),
1158
- rawValue,
1159
- value: decodeRawHex(rawValue),
1160
- tx: txMeta
1161
- };
1162
- }
1163
- case "map_delete": {
1164
- const data = event.data ?? {};
1165
- const rawKey = typeof data.raw_key === "string" ? data.raw_key : "";
1166
- return {
1167
- contractId: printContractId(data) ?? "",
1168
- map: typeof data.map_name === "string" ? data.map_name : "",
1169
- rawKey,
1170
- key: decodeRawHex(rawKey),
1171
- tx: txMeta
1172
- };
1173
- }
1174
- default:
1175
- return {
1176
- ...decoded,
1177
- _eventType: event.type,
1178
- tx: txMeta
1179
- };
1180
- }
1181
- }
1182
- async function runHandlers(subgraph, matched, ctx, opts) {
1183
- let processed = 0;
1184
- let errors = 0;
1185
- let skipped = 0;
1186
- const threshold = opts?.errorThreshold ?? DEFAULT_ERROR_THRESHOLD;
1187
- const filterLookup = new Map;
1188
- if (!Array.isArray(subgraph.sources)) {
1189
- for (const [name, filter] of Object.entries(subgraph.sources)) {
1190
- filterLookup.set(name, filter);
1191
- }
1192
- }
1193
- const units = [];
1194
- for (const { tx, events, sourceName } of matched) {
1195
- if (events.length === 0) {
1196
- units.push({ tx, sourceName, event: null });
1197
- } else {
1198
- for (const event of events)
1199
- units.push({ tx, sourceName, event });
1200
- }
1201
- }
1202
- const clockRank = (e) => e?.clock === "vm" ? 1 : 0;
1203
- units.sort((a, b) => (a.tx.tx_index ?? 0) - (b.tx.tx_index ?? 0) || clockRank(a.event) - clockRank(b.event) || (a.event?.event_index ?? -1) - (b.event?.event_index ?? -1));
1204
- for (const { tx, event, sourceName } of units) {
1205
- if (errors >= threshold) {
1206
- logger2.error("Subgraph error threshold reached, skipping remaining events", {
1207
- subgraph: subgraph.name,
1208
- errors,
1209
- threshold
1210
- });
1211
- return { processed, errors, ...skipped > 0 ? { skipped } : {} };
1212
- }
1213
- const filter = filterLookup.get(sourceName);
1214
- const materialize = filter && "materialize" in filter && filter.materialize !== undefined ? filter.materialize : undefined;
1215
- const handler = subgraph.handlers[sourceName] ?? subgraph.handlers["*"] ?? null;
1216
- if (!handler && !materialize) {
1217
- logger2.warn("No handler found for source", {
1218
- subgraph: subgraph.name,
1219
- sourceName,
1220
- txId: tx.tx_id
1221
- });
1222
- continue;
1223
- }
1224
- ctx.setTx({
1225
- txId: tx.tx_id,
1226
- sender: tx.sender,
1227
- type: tx.type,
1228
- status: tx.status,
1229
- contractId: tx.contract_id ?? null,
1230
- functionName: tx.function_name ?? null
1231
- });
1232
- const checkpoint = ctx.opsCheckpoint();
1233
- try {
1234
- let payload;
1235
- if (event === null) {
1236
- payload = filter ? buildEventPayload(filter, tx, null) : {
1237
- tx: {
1238
- txId: tx.tx_id,
1239
- sender: tx.sender,
1240
- type: tx.type,
1241
- status: tx.status,
1242
- contractId: tx.contract_id,
1243
- functionName: tx.function_name
1244
- }
1245
- };
1246
- } else if (filter) {
1247
- payload = buildEventPayload(filter, tx, event);
1248
- } else {
1249
- const decoded = decodeEventData(event.data);
1250
- payload = {
1251
- ...decoded,
1252
- _eventId: event.id,
1253
- _eventType: event.type,
1254
- _eventIndex: event.event_index,
1255
- tx: {
1256
- txId: tx.tx_id,
1257
- sender: tx.sender,
1258
- type: tx.type,
1259
- status: tx.status,
1260
- contractId: tx.contract_id,
1261
- functionName: tx.function_name
1262
- }
1263
- };
1264
- }
1265
- if (event !== null && filter?.type === "print_event" && filter.topic && payload.topic !== filter.topic) {
1266
- continue;
1267
- }
1268
- if (event !== null && filter?.type === "print_event" && filter.prints) {
1269
- const printPayload = payload;
1270
- const verdict = validatePrintPayload(filter.prints, printPayload.topic ?? "", printPayload.data ?? {});
1271
- if (!verdict.ok) {
1272
- skipped++;
1273
- logger2.warn("Print payload does not match declared prints schema", {
1274
- subgraph: subgraph.name,
1275
- sourceName,
1276
- topic: printPayload.topic,
1277
- txId: tx.tx_id,
1278
- reason: verdict.reason
1279
- });
1280
- recordPrintViolation({
1281
- subgraphName: subgraph.name,
1282
- sourceName,
1283
- blockHeight: ctx.block.height,
1284
- txId: tx.tx_id,
1285
- reason: verdict.reason,
1286
- samplePayload: {
1287
- topic: printPayload.topic ?? "",
1288
- data: printPayload.data ?? {}
1289
- }
1290
- });
1291
- continue;
1292
- }
1293
- }
1294
- if (materialize) {
1295
- const result = applyMaterializeInsert(materialize, payload, ctx, subgraph.schema);
1296
- if (!result.ok) {
1297
- skipped++;
1298
- logger2.warn("Materialize skipped: required field missing", {
1299
- subgraph: subgraph.name,
1300
- sourceName,
1301
- txId: tx.tx_id,
1302
- reason: result.reason
1303
- });
1304
- continue;
1305
- }
1306
- processed++;
1307
- } else if (handler) {
1308
- await handler(payload, ctx);
1309
- processed++;
1310
- }
1311
- } catch (err) {
1312
- ctx.rollbackTo(checkpoint);
1313
- errors++;
1314
- logger2.error("Subgraph handler error", {
1315
- subgraph: subgraph.name,
1316
- sourceName,
1317
- txId: tx.tx_id,
1318
- ...event !== null ? { eventId: event.id, eventType: event.type } : {},
1319
- error: getErrorMessage(err)
1320
- });
1321
- }
1322
- }
1323
- return { processed, errors, ...skipped > 0 ? { skipped } : {} };
1324
- }
1325
- export {
1326
- runHandlers,
1327
- buildEventPayload,
1328
- buildContractCallInput
1329
- };
1330
-
1331
- //# debugId=90E8BB694CDBE41B64756E2164756E21
1332
- //# sourceMappingURL=runner.js.map