@secondlayer/subgraphs 0.8.1 → 0.9.1

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 (42) hide show
  1. package/dist/src/index.d.ts +113 -32
  2. package/dist/src/index.js +486 -99
  3. package/dist/src/index.js.map +9 -10
  4. package/dist/src/runtime/block-processor.d.ts +110 -20
  5. package/dist/src/runtime/block-processor.js +452 -90
  6. package/dist/src/runtime/block-processor.js.map +7 -8
  7. package/dist/src/runtime/catchup.d.ts +110 -20
  8. package/dist/src/runtime/catchup.js +457 -94
  9. package/dist/src/runtime/catchup.js.map +8 -9
  10. package/dist/src/runtime/clarity.js +3 -3
  11. package/dist/src/runtime/clarity.js.map +3 -3
  12. package/dist/src/runtime/context.d.ts +23 -5
  13. package/dist/src/runtime/context.js +57 -1
  14. package/dist/src/runtime/context.js.map +3 -3
  15. package/dist/src/runtime/processor.js +457 -94
  16. package/dist/src/runtime/processor.js.map +8 -9
  17. package/dist/src/runtime/reindex.d.ts +110 -20
  18. package/dist/src/runtime/reindex.js +452 -90
  19. package/dist/src/runtime/reindex.js.map +7 -8
  20. package/dist/src/runtime/reorg.d.ts +110 -20
  21. package/dist/src/runtime/reorg.js +452 -90
  22. package/dist/src/runtime/reorg.js.map +7 -8
  23. package/dist/src/runtime/runner.d.ts +152 -42
  24. package/dist/src/runtime/runner.js +226 -30
  25. package/dist/src/runtime/runner.js.map +4 -4
  26. package/dist/src/runtime/source-matcher.d.ts +88 -31
  27. package/dist/src/runtime/source-matcher.js +171 -61
  28. package/dist/src/runtime/source-matcher.js.map +4 -5
  29. package/dist/src/schema/index.d.ts +110 -20
  30. package/dist/src/schema/index.js +34 -9
  31. package/dist/src/schema/index.js.map +3 -3
  32. package/dist/src/service.js +457 -94
  33. package/dist/src/service.js.map +8 -9
  34. package/dist/src/templates.js +52 -51
  35. package/dist/src/templates.js.map +3 -3
  36. package/dist/src/types.d.ts +111 -29
  37. package/dist/src/types.js +1 -20
  38. package/dist/src/types.js.map +3 -4
  39. package/dist/src/validate.d.ts +112 -22
  40. package/dist/src/validate.js +35 -10
  41. package/dist/src/validate.js.map +3 -3
  42. package/package.json +2 -2
@@ -1,22 +1,9 @@
1
1
  import { createRequire } from "node:module";
2
2
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
3
 
4
- // src/types.ts
5
- function sourceKey(source) {
6
- if (source.contract) {
7
- if (source.function)
8
- return `${source.contract}::${source.function}`;
9
- if (source.event)
10
- return `${source.contract}::${source.event}`;
11
- return source.contract;
12
- }
13
- if (source.type)
14
- return source.type;
15
- return "*";
16
- }
17
-
18
4
  // src/runtime/context.ts
19
5
  import { logger } from "@secondlayer/shared/logger";
6
+ import { formatUnits } from "@secondlayer/stacks/utils";
20
7
  import { sql } from "kysely";
21
8
  function validateColumnName(name) {
22
9
  if (!/^[a-z_][a-z0-9_]*$/i.test(name)) {
@@ -84,6 +71,20 @@ class SubgraphContext {
84
71
  this.validateTable(table);
85
72
  this.ops.push({ kind: "delete", table, data: where });
86
73
  }
74
+ patch(table, where, set) {
75
+ this.update(table, where, set);
76
+ }
77
+ async patchOrInsert(table, key, row) {
78
+ const existing = await this.findOne(table, key);
79
+ const resolved = {};
80
+ for (const [k, v] of Object.entries(row)) {
81
+ resolved[k] = typeof v === "function" ? v(existing) : v;
82
+ }
83
+ this.upsert(table, key, resolved);
84
+ }
85
+ formatUnits(value, decimals) {
86
+ return formatUnits(value, decimals);
87
+ }
87
88
  async findOne(table, where) {
88
89
  this.validateTable(table);
89
90
  const qualifiedTable = `"${this.pgSchemaName}"."${table}"`;
@@ -101,6 +102,47 @@ class SubgraphContext {
101
102
  const { rows } = await sql.raw(query).execute(this.db);
102
103
  return rows.map((r) => this.coerceRow(table, r));
103
104
  }
105
+ async count(table, where) {
106
+ this.validateTable(table);
107
+ const qualifiedTable = `"${this.pgSchemaName}"."${table}"`;
108
+ const whereClause = where ? `WHERE ${buildWhereClause(where).clause}` : "";
109
+ const { rows } = await sql.raw(`SELECT COUNT(*)::int AS count FROM ${qualifiedTable} ${whereClause}`).execute(this.db);
110
+ return Number(rows[0]?.count ?? 0);
111
+ }
112
+ async sum(table, column, where) {
113
+ this.validateTable(table);
114
+ validateColumnName(column);
115
+ const qualifiedTable = `"${this.pgSchemaName}"."${table}"`;
116
+ const whereClause = where ? `WHERE ${buildWhereClause(where).clause}` : "";
117
+ const { rows } = await sql.raw(`SELECT COALESCE(SUM("${column}"), 0) AS total FROM ${qualifiedTable} ${whereClause}`).execute(this.db);
118
+ return BigInt(rows[0]?.total?.toString() ?? "0");
119
+ }
120
+ async min(table, column, where) {
121
+ this.validateTable(table);
122
+ validateColumnName(column);
123
+ const qualifiedTable = `"${this.pgSchemaName}"."${table}"`;
124
+ const whereClause = where ? `WHERE ${buildWhereClause(where).clause}` : "";
125
+ const { rows } = await sql.raw(`SELECT MIN("${column}") AS val FROM ${qualifiedTable} ${whereClause}`).execute(this.db);
126
+ const val = rows[0]?.val;
127
+ return val != null ? BigInt(val.toString()) : null;
128
+ }
129
+ async max(table, column, where) {
130
+ this.validateTable(table);
131
+ validateColumnName(column);
132
+ const qualifiedTable = `"${this.pgSchemaName}"."${table}"`;
133
+ const whereClause = where ? `WHERE ${buildWhereClause(where).clause}` : "";
134
+ const { rows } = await sql.raw(`SELECT MAX("${column}") AS val FROM ${qualifiedTable} ${whereClause}`).execute(this.db);
135
+ const val = rows[0]?.val;
136
+ return val != null ? BigInt(val.toString()) : null;
137
+ }
138
+ async countDistinct(table, column, where) {
139
+ this.validateTable(table);
140
+ validateColumnName(column);
141
+ const qualifiedTable = `"${this.pgSchemaName}"."${table}"`;
142
+ const whereClause = where ? `WHERE ${buildWhereClause(where).clause}` : "";
143
+ const { rows } = await sql.raw(`SELECT COUNT(DISTINCT "${column}")::int AS count FROM ${qualifiedTable} ${whereClause}`).execute(this.db);
144
+ return Number(rows[0]?.count ?? 0);
145
+ }
104
146
  coerceRow(table, row) {
105
147
  const tableDef = this.subgraphSchema[table];
106
148
  if (!tableDef)
@@ -241,12 +283,12 @@ function buildWhereClause(where) {
241
283
  }
242
284
 
243
285
  // src/runtime/clarity.ts
244
- import { cvToJSON, deserializeCV } from "@secondlayer/stacks/clarity";
286
+ import { cvToValue, deserializeCV } from "@secondlayer/stacks/clarity";
245
287
  function decodeClarityValue(hex) {
246
288
  try {
247
289
  const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
248
290
  const cv = deserializeCV(cleanHex);
249
- return cvToJSON(cv);
291
+ return cvToValue(cv);
250
292
  } catch {
251
293
  return hex;
252
294
  }
@@ -275,19 +317,210 @@ function decodeFunctionArgs(args) {
275
317
  import { getErrorMessage } from "@secondlayer/shared";
276
318
  import { logger as logger2 } from "@secondlayer/shared/logger";
277
319
  var DEFAULT_ERROR_THRESHOLD = 50;
278
- function resolveHandler(handlers, key) {
279
- return handlers[key] ?? handlers["*"] ?? null;
320
+ function camelCase(str) {
321
+ return str.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
322
+ }
323
+ function camelizeKeys(obj) {
324
+ if (obj === null || obj === undefined)
325
+ return obj;
326
+ if (typeof obj !== "object")
327
+ return obj;
328
+ if (Array.isArray(obj))
329
+ return obj.map(camelizeKeys);
330
+ const result = {};
331
+ for (const [k, v] of Object.entries(obj)) {
332
+ result[camelCase(k)] = camelizeKeys(v);
333
+ }
334
+ return result;
335
+ }
336
+ function decodeFunctionArgs2(args) {
337
+ if (!Array.isArray(args))
338
+ return [];
339
+ return args.map((arg) => {
340
+ if (typeof arg === "string")
341
+ return decodeClarityValue(arg);
342
+ return arg;
343
+ });
344
+ }
345
+ function decodeRawResult(raw) {
346
+ if (typeof raw === "string" && raw.length > 2) {
347
+ return decodeClarityValue(raw);
348
+ }
349
+ return null;
350
+ }
351
+ function safeBigInt(val) {
352
+ if (typeof val === "bigint")
353
+ return val;
354
+ if (typeof val === "number")
355
+ return BigInt(val);
356
+ if (typeof val === "string") {
357
+ try {
358
+ return BigInt(val);
359
+ } catch {
360
+ return 0n;
361
+ }
362
+ }
363
+ return 0n;
364
+ }
365
+ function buildEventPayload(filter, tx, event) {
366
+ const txMeta = {
367
+ txId: tx.tx_id,
368
+ sender: tx.sender,
369
+ type: tx.type,
370
+ status: tx.status,
371
+ contractId: tx.contract_id ?? null,
372
+ functionName: tx.function_name ?? null
373
+ };
374
+ const decodedArgs = decodeFunctionArgs2(tx.function_args);
375
+ const decodedResult = decodeRawResult(tx.raw_result);
376
+ if (!event) {
377
+ switch (filter.type) {
378
+ case "contract_call":
379
+ return {
380
+ contractId: tx.contract_id ?? "",
381
+ functionName: tx.function_name ?? "",
382
+ caller: tx.sender,
383
+ args: decodedArgs,
384
+ result: decodedResult,
385
+ tx: txMeta
386
+ };
387
+ case "contract_deploy":
388
+ return {
389
+ contractId: tx.contract_id ?? "",
390
+ deployer: tx.sender,
391
+ tx: txMeta
392
+ };
393
+ default:
394
+ return { tx: txMeta };
395
+ }
396
+ }
397
+ const decoded = decodeEventData(event.data);
398
+ switch (filter.type) {
399
+ case "ft_transfer":
400
+ return {
401
+ sender: decoded.sender,
402
+ recipient: decoded.recipient,
403
+ amount: safeBigInt(decoded.amount),
404
+ assetIdentifier: decoded.asset_identifier,
405
+ tx: txMeta
406
+ };
407
+ case "ft_mint":
408
+ return {
409
+ recipient: decoded.recipient,
410
+ amount: safeBigInt(decoded.amount),
411
+ assetIdentifier: decoded.asset_identifier,
412
+ tx: txMeta
413
+ };
414
+ case "ft_burn":
415
+ return {
416
+ sender: decoded.sender,
417
+ amount: safeBigInt(decoded.amount),
418
+ assetIdentifier: decoded.asset_identifier,
419
+ tx: txMeta
420
+ };
421
+ case "nft_transfer":
422
+ return {
423
+ sender: decoded.sender,
424
+ recipient: decoded.recipient,
425
+ tokenId: decoded.value,
426
+ assetIdentifier: decoded.asset_identifier,
427
+ tx: txMeta
428
+ };
429
+ case "nft_mint":
430
+ return {
431
+ recipient: decoded.recipient,
432
+ tokenId: decoded.value,
433
+ assetIdentifier: decoded.asset_identifier,
434
+ tx: txMeta
435
+ };
436
+ case "nft_burn":
437
+ return {
438
+ sender: decoded.sender,
439
+ tokenId: decoded.value,
440
+ assetIdentifier: decoded.asset_identifier,
441
+ tx: txMeta
442
+ };
443
+ case "stx_transfer":
444
+ return {
445
+ sender: decoded.sender,
446
+ recipient: decoded.recipient,
447
+ amount: safeBigInt(decoded.amount),
448
+ memo: decoded.memo ?? "",
449
+ tx: txMeta
450
+ };
451
+ case "stx_mint":
452
+ return {
453
+ recipient: decoded.recipient,
454
+ amount: safeBigInt(decoded.amount),
455
+ tx: txMeta
456
+ };
457
+ case "stx_burn":
458
+ return {
459
+ sender: decoded.sender,
460
+ amount: safeBigInt(decoded.amount),
461
+ tx: txMeta
462
+ };
463
+ case "stx_lock":
464
+ return {
465
+ lockedAddress: decoded.locked_address,
466
+ lockedAmount: safeBigInt(decoded.locked_amount),
467
+ unlockHeight: safeBigInt(decoded.unlock_height),
468
+ tx: txMeta
469
+ };
470
+ case "print_event": {
471
+ const rawValue = decoded.value;
472
+ const clarityObj = rawValue && typeof rawValue === "object" && !Array.isArray(rawValue) ? rawValue : null;
473
+ const topic = clarityObj?.topic ? String(clarityObj.topic) : decoded.topic ?? "";
474
+ const { topic: _, ...rest } = clarityObj ?? {};
475
+ const data = Object.keys(rest).length > 0 ? camelizeKeys(rest) : rawValue && typeof rawValue !== "object" ? rawValue : {};
476
+ return {
477
+ contractId: decoded.contract_identifier ?? tx.contract_id ?? "",
478
+ topic,
479
+ data: data ?? {},
480
+ tx: txMeta
481
+ };
482
+ }
483
+ case "contract_call":
484
+ return {
485
+ ...decoded,
486
+ _eventType: event.type,
487
+ contractId: tx.contract_id ?? "",
488
+ functionName: tx.function_name ?? "",
489
+ caller: tx.sender,
490
+ args: decodedArgs,
491
+ result: decodedResult,
492
+ tx: txMeta
493
+ };
494
+ case "contract_deploy":
495
+ return {
496
+ contractId: tx.contract_id ?? "",
497
+ deployer: tx.sender,
498
+ tx: txMeta
499
+ };
500
+ default:
501
+ return {
502
+ ...decoded,
503
+ _eventType: event.type,
504
+ tx: txMeta
505
+ };
506
+ }
280
507
  }
281
508
  async function runHandlers(subgraph, matched, ctx, opts) {
282
509
  let processed = 0;
283
510
  let errors = 0;
284
511
  const threshold = opts?.errorThreshold ?? DEFAULT_ERROR_THRESHOLD;
285
- for (const { tx, events, sourceKey: sourceKey2 } of matched) {
286
- const handler = resolveHandler(subgraph.handlers, sourceKey2);
512
+ const filterLookup = new Map;
513
+ if (!Array.isArray(subgraph.sources)) {
514
+ for (const [name, filter] of Object.entries(subgraph.sources)) {
515
+ filterLookup.set(name, filter);
516
+ }
517
+ }
518
+ for (const { tx, events, sourceName } of matched) {
519
+ const handler = subgraph.handlers[sourceName] ?? subgraph.handlers["*"] ?? null;
287
520
  if (!handler) {
288
- logger2.warn("No handler found for source key", {
521
+ logger2.warn("No handler found for source", {
289
522
  subgraph: subgraph.name,
290
- sourceKey: sourceKey2,
523
+ sourceName,
291
524
  txId: tx.tx_id
292
525
  });
293
526
  continue;
@@ -296,11 +529,14 @@ async function runHandlers(subgraph, matched, ctx, opts) {
296
529
  txId: tx.tx_id,
297
530
  sender: tx.sender,
298
531
  type: tx.type,
299
- status: tx.status
532
+ status: tx.status,
533
+ contractId: tx.contract_id ?? null,
534
+ functionName: tx.function_name ?? null
300
535
  });
536
+ const filter = filterLookup.get(sourceName);
301
537
  if (events.length === 0) {
302
538
  try {
303
- const txPayload = {
539
+ const payload = filter ? buildEventPayload(filter, tx, null) : {
304
540
  tx: {
305
541
  txId: tx.tx_id,
306
542
  sender: tx.sender,
@@ -310,13 +546,13 @@ async function runHandlers(subgraph, matched, ctx, opts) {
310
546
  functionName: tx.function_name
311
547
  }
312
548
  };
313
- await handler(txPayload, ctx);
549
+ await handler(payload, ctx);
314
550
  processed++;
315
551
  } catch (err) {
316
552
  errors++;
317
553
  logger2.error("Subgraph handler error", {
318
554
  subgraph: subgraph.name,
319
- sourceKey: sourceKey2,
555
+ sourceName,
320
556
  txId: tx.tx_id,
321
557
  error: getErrorMessage(err)
322
558
  });
@@ -333,28 +569,30 @@ async function runHandlers(subgraph, matched, ctx, opts) {
333
569
  return { processed, errors };
334
570
  }
335
571
  try {
336
- const decoded = decodeEventData(event.data);
337
- const eventPayload = {
338
- ...decoded,
339
- _eventId: event.id,
340
- _eventType: event.type,
341
- _eventIndex: event.event_index,
342
- tx: {
343
- txId: tx.tx_id,
344
- sender: tx.sender,
345
- type: tx.type,
346
- status: tx.status,
347
- contractId: tx.contract_id,
348
- functionName: tx.function_name
349
- }
350
- };
351
- await handler(eventPayload, ctx);
572
+ const payload = filter ? buildEventPayload(filter, tx, event) : (() => {
573
+ const decoded = decodeEventData(event.data);
574
+ return {
575
+ ...decoded,
576
+ _eventId: event.id,
577
+ _eventType: event.type,
578
+ _eventIndex: event.event_index,
579
+ tx: {
580
+ txId: tx.tx_id,
581
+ sender: tx.sender,
582
+ type: tx.type,
583
+ status: tx.status,
584
+ contractId: tx.contract_id,
585
+ functionName: tx.function_name
586
+ }
587
+ };
588
+ })();
589
+ await handler(payload, ctx);
352
590
  processed++;
353
591
  } catch (err) {
354
592
  errors++;
355
593
  logger2.error("Subgraph handler error", {
356
594
  subgraph: subgraph.name,
357
- sourceKey: sourceKey2,
595
+ sourceName,
358
596
  txId: tx.tx_id,
359
597
  eventId: event.id,
360
598
  eventType: event.type,
@@ -379,62 +617,186 @@ function matchPattern(value, pattern) {
379
617
  }
380
618
  return re.test(value);
381
619
  }
382
- function matchSource(source, transactions, eventsByTx) {
620
+ function matchFilter(filter, transactions, eventsByTx) {
383
621
  const results = [];
384
- const key = sourceKey(source);
385
- for (const tx of transactions) {
386
- if (source.type) {
387
- if (!matchPattern(tx.type, source.type))
388
- continue;
389
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
390
- let matchedEvents = txEvents;
391
- if (source.minAmount !== undefined) {
392
- const amountEvents = matchedEvents.filter((e) => {
622
+ switch (filter.type) {
623
+ case "stx_transfer":
624
+ case "stx_mint":
625
+ case "stx_burn":
626
+ case "stx_lock": {
627
+ const eventType = `${filter.type}_event`;
628
+ for (const tx of transactions) {
629
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
630
+ const matched = txEvents.filter((e) => e.type === eventType);
631
+ if (matched.length === 0)
632
+ continue;
633
+ const filtered = matched.filter((e) => {
393
634
  const data = e.data;
394
- const rawAmount = data?.amount;
395
- if (rawAmount === undefined)
635
+ if (!data)
396
636
  return false;
397
- const amount = BigInt(rawAmount);
398
- return amount >= source.minAmount;
637
+ if ("sender" in filter && filter.sender) {
638
+ if (!matchPattern(data.sender, filter.sender))
639
+ return false;
640
+ }
641
+ if ("recipient" in filter && filter.recipient) {
642
+ if (!matchPattern(data.recipient, filter.recipient))
643
+ return false;
644
+ }
645
+ if ("lockedAddress" in filter && filter.lockedAddress) {
646
+ if (!matchPattern(data.locked_address, filter.lockedAddress))
647
+ return false;
648
+ }
649
+ if ("minAmount" in filter && filter.minAmount !== undefined) {
650
+ const amount = BigInt(data.amount ?? data.locked_amount ?? "0");
651
+ if (amount < filter.minAmount)
652
+ return false;
653
+ }
654
+ if ("maxAmount" in filter && filter.maxAmount !== undefined) {
655
+ const amount = BigInt(data.amount ?? "0");
656
+ if (amount > filter.maxAmount)
657
+ return false;
658
+ }
659
+ return true;
399
660
  });
400
- if (amountEvents.length === 0)
401
- continue;
402
- matchedEvents = amountEvents;
661
+ if (filtered.length > 0) {
662
+ results.push({ tx, events: filtered });
663
+ }
403
664
  }
404
- results.push({ tx, events: matchedEvents, sourceKey: key });
405
- continue;
665
+ break;
406
666
  }
407
- if (source.contract) {
408
- const txContractMatch = tx.contract_id && matchPattern(tx.contract_id, source.contract);
409
- if (source.function && tx.function_name) {
410
- if (!matchPattern(tx.function_name, source.function))
411
- continue;
412
- } else if (source.function && !tx.function_name) {
413
- continue;
667
+ case "ft_transfer":
668
+ case "ft_mint":
669
+ case "ft_burn": {
670
+ const eventType = `${filter.type}_event`;
671
+ for (const tx of transactions) {
672
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
673
+ const matched = txEvents.filter((e) => {
674
+ if (e.type !== eventType)
675
+ return false;
676
+ const data = e.data;
677
+ if (!data)
678
+ return false;
679
+ if (filter.assetIdentifier) {
680
+ const assetId = data.asset_identifier;
681
+ if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
682
+ return false;
683
+ }
684
+ if ("sender" in filter && filter.sender) {
685
+ if (!matchPattern(data.sender, filter.sender))
686
+ return false;
687
+ }
688
+ if ("recipient" in filter && filter.recipient) {
689
+ if (!matchPattern(data.recipient, filter.recipient))
690
+ return false;
691
+ }
692
+ if (filter.minAmount !== undefined) {
693
+ const amount = BigInt(data.amount ?? "0");
694
+ if (amount < filter.minAmount)
695
+ return false;
696
+ }
697
+ return true;
698
+ });
699
+ if (matched.length > 0) {
700
+ results.push({ tx, events: matched });
701
+ }
414
702
  }
415
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
416
- let matchedEvents = txEvents;
417
- if (!txContractMatch) {
418
- matchedEvents = txEvents.filter((e) => {
703
+ break;
704
+ }
705
+ case "nft_transfer":
706
+ case "nft_mint":
707
+ case "nft_burn": {
708
+ const eventType = `${filter.type}_event`;
709
+ for (const tx of transactions) {
710
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
711
+ const matched = txEvents.filter((e) => {
712
+ if (e.type !== eventType)
713
+ return false;
419
714
  const data = e.data;
420
- const contractIdentifier = data?.contract_identifier;
421
- return contractIdentifier && matchPattern(contractIdentifier, source.contract);
715
+ if (!data)
716
+ return false;
717
+ if (filter.assetIdentifier) {
718
+ const assetId = data.asset_identifier;
719
+ if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
720
+ return false;
721
+ }
722
+ if ("sender" in filter && filter.sender) {
723
+ if (!matchPattern(data.sender, filter.sender))
724
+ return false;
725
+ }
726
+ if ("recipient" in filter && filter.recipient) {
727
+ if (!matchPattern(data.recipient, filter.recipient))
728
+ return false;
729
+ }
730
+ return true;
422
731
  });
423
- if (matchedEvents.length === 0)
732
+ if (matched.length > 0) {
733
+ results.push({ tx, events: matched });
734
+ }
735
+ }
736
+ break;
737
+ }
738
+ case "contract_call": {
739
+ for (const tx of transactions) {
740
+ if (tx.type !== "contract_call")
741
+ continue;
742
+ if (filter.contractId) {
743
+ if (!tx.contract_id || !matchPattern(tx.contract_id, filter.contractId))
744
+ continue;
745
+ }
746
+ if (filter.functionName) {
747
+ if (!tx.function_name || !matchPattern(tx.function_name, filter.functionName))
748
+ continue;
749
+ }
750
+ if (filter.caller) {
751
+ if (!matchPattern(tx.sender, filter.caller))
752
+ continue;
753
+ }
754
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
755
+ results.push({ tx, events: txEvents });
756
+ }
757
+ break;
758
+ }
759
+ case "contract_deploy": {
760
+ for (const tx of transactions) {
761
+ if (tx.type !== "smart_contract")
424
762
  continue;
763
+ if (filter.deployer) {
764
+ if (!matchPattern(tx.sender, filter.deployer))
765
+ continue;
766
+ }
767
+ if (filter.contractName) {
768
+ const name = tx.contract_id?.split(".")[1] ?? "";
769
+ if (!matchPattern(name, filter.contractName))
770
+ continue;
771
+ }
772
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
773
+ results.push({ tx, events: txEvents });
425
774
  }
426
- if (source.event) {
427
- matchedEvents = matchedEvents.filter((e) => {
428
- if (matchPattern(e.type, source.event))
429
- return true;
775
+ break;
776
+ }
777
+ case "print_event": {
778
+ for (const tx of transactions) {
779
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
780
+ const matched = txEvents.filter((e) => {
781
+ if (e.type !== "smart_contract_event")
782
+ return false;
430
783
  const data = e.data;
431
- const topic = data?.topic;
432
- return topic ? matchPattern(topic, source.event) : false;
784
+ if (!data)
785
+ return false;
786
+ if (data.topic !== "print")
787
+ return false;
788
+ if (filter.contractId) {
789
+ const contractId = data.contract_identifier;
790
+ if (!contractId || !matchPattern(contractId, filter.contractId))
791
+ return false;
792
+ }
793
+ return true;
433
794
  });
795
+ if (matched.length > 0) {
796
+ results.push({ tx, events: matched });
797
+ }
434
798
  }
435
- if (txContractMatch || matchedEvents.length > 0) {
436
- results.push({ tx, events: matchedEvents, sourceKey: key });
437
- }
799
+ break;
438
800
  }
439
801
  }
440
802
  return results;
@@ -448,13 +810,13 @@ function matchSources(sources, transactions, events) {
448
810
  }
449
811
  const seen = new Set;
450
812
  const results = [];
451
- for (const source of sources) {
452
- const matches = matchSource(source, transactions, eventsByTx);
813
+ for (const [sourceName, filter] of Object.entries(sources)) {
814
+ const matches = matchFilter(filter, transactions, eventsByTx);
453
815
  for (const match of matches) {
454
- const dedupeKey = `${match.tx.tx_id}:${match.sourceKey}`;
816
+ const dedupeKey = `${match.tx.tx_id}:${sourceName}`;
455
817
  if (!seen.has(dedupeKey)) {
456
818
  seen.add(dedupeKey);
457
- results.push(match);
819
+ results.push({ ...match, sourceName });
458
820
  }
459
821
  }
460
822
  }
@@ -766,7 +1128,8 @@ async function catchUpSubgraph(subgraph, subgraphName) {
766
1128
  let processed = 0;
767
1129
  let batchSize = DEFAULT_BATCH_SIZE;
768
1130
  let currentHeight = startBlock;
769
- let nextBatchPromise = loadBlockRange(db, currentHeight, Math.min(currentHeight + batchSize - 1, chainTip));
1131
+ let nextBatchEnd = Math.min(currentHeight + batchSize - 1, chainTip);
1132
+ let nextBatchPromise = loadBlockRange(db, currentHeight, nextBatchEnd);
770
1133
  while (currentHeight <= chainTip) {
771
1134
  const currentRow = await getSubgraph(db, subgraphName);
772
1135
  if (!currentRow || currentRow.status !== "active") {
@@ -777,11 +1140,11 @@ async function catchUpSubgraph(subgraph, subgraphName) {
777
1140
  break;
778
1141
  }
779
1142
  const batch = await nextBatchPromise;
780
- const batchEnd = Math.min(currentHeight + batchSize - 1, chainTip);
1143
+ const batchEnd = nextBatchEnd;
781
1144
  const nextStart = batchEnd + 1;
782
1145
  if (nextStart <= chainTip) {
783
- const nextEnd = Math.min(nextStart + batchSize - 1, chainTip);
784
- nextBatchPromise = loadBlockRange(db, nextStart, nextEnd);
1146
+ nextBatchEnd = Math.min(nextStart + batchSize - 1, chainTip);
1147
+ nextBatchPromise = loadBlockRange(db, nextStart, nextBatchEnd);
785
1148
  }
786
1149
  const batchFailedBlocks = [];
787
1150
  for (let height = currentHeight;height <= batchEnd; height++) {
@@ -1014,5 +1377,5 @@ var shutdown = async () => {
1014
1377
  process.on("SIGINT", shutdown);
1015
1378
  process.on("SIGTERM", shutdown);
1016
1379
 
1017
- //# debugId=1DD34E9C0E67AAF764756E2164756E21
1380
+ //# debugId=8F216EE232BD89E464756E2164756E21
1018
1381
  //# sourceMappingURL=service.js.map