@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
package/dist/src/index.js CHANGED
@@ -1,20 +1,6 @@
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/validate.ts
19
5
  import { z } from "zod/v4";
20
6
  var SubgraphNameSchema = z.string().min(1).max(63).regex(/^[a-z][a-z0-9-]*$/, "Must start with lowercase letter, contain only lowercase alphanumeric and hyphens");
@@ -40,18 +26,43 @@ var SubgraphTableSchema = z.object({
40
26
  uniqueKeys: z.array(z.array(z.string())).optional()
41
27
  });
42
28
  var SubgraphSchemaSchema = z.record(z.string(), SubgraphTableSchema).refine((s) => Object.keys(s).length > 0, "Schema must have at least one table");
43
- var SubgraphSourceSchema = z.object({
44
- contract: z.string().optional(),
45
- event: z.string().optional(),
46
- function: z.string().optional(),
47
- type: z.string().optional(),
48
- minAmount: z.bigint().optional()
49
- }).refine((s) => s.contract || s.type, "Source must specify at least 'contract' or 'type'");
29
+ var VALID_FILTER_TYPES = [
30
+ "stx_transfer",
31
+ "stx_mint",
32
+ "stx_burn",
33
+ "stx_lock",
34
+ "ft_transfer",
35
+ "ft_mint",
36
+ "ft_burn",
37
+ "nft_transfer",
38
+ "nft_mint",
39
+ "nft_burn",
40
+ "contract_call",
41
+ "contract_deploy",
42
+ "print_event"
43
+ ];
44
+ var SubgraphFilterSchema = z.object({
45
+ type: z.enum(VALID_FILTER_TYPES),
46
+ sender: z.string().optional(),
47
+ recipient: z.string().optional(),
48
+ minAmount: z.bigint().optional(),
49
+ maxAmount: z.bigint().optional(),
50
+ assetIdentifier: z.string().optional(),
51
+ contractId: z.string().optional(),
52
+ functionName: z.string().optional(),
53
+ caller: z.string().optional(),
54
+ deployer: z.string().optional(),
55
+ contractName: z.string().optional(),
56
+ topic: z.string().optional(),
57
+ lockedAddress: z.string().optional(),
58
+ abi: z.record(z.string(), z.any()).optional()
59
+ }).passthrough();
50
60
  var SubgraphDefinitionSchema = z.object({
51
61
  name: SubgraphNameSchema,
52
62
  version: z.string().optional(),
53
63
  description: z.string().optional(),
54
- sources: z.array(SubgraphSourceSchema).min(1, "Must have at least one source"),
64
+ startBlock: z.number().int().nonnegative().optional(),
65
+ sources: z.record(z.string(), SubgraphFilterSchema).refine((s) => Object.keys(s).length > 0, "Must have at least one source"),
55
66
  schema: SubgraphSchemaSchema,
56
67
  handlers: z.record(z.string(), z.any())
57
68
  });
@@ -61,6 +72,7 @@ function validateSubgraphDefinition(def) {
61
72
 
62
73
  // src/runtime/context.ts
63
74
  import { logger } from "@secondlayer/shared/logger";
75
+ import { formatUnits } from "@secondlayer/stacks/utils";
64
76
  import { sql } from "kysely";
65
77
  function validateColumnName(name) {
66
78
  if (!/^[a-z_][a-z0-9_]*$/i.test(name)) {
@@ -128,6 +140,20 @@ class SubgraphContext {
128
140
  this.validateTable(table);
129
141
  this.ops.push({ kind: "delete", table, data: where });
130
142
  }
143
+ patch(table, where, set) {
144
+ this.update(table, where, set);
145
+ }
146
+ async patchOrInsert(table, key, row) {
147
+ const existing = await this.findOne(table, key);
148
+ const resolved = {};
149
+ for (const [k, v] of Object.entries(row)) {
150
+ resolved[k] = typeof v === "function" ? v(existing) : v;
151
+ }
152
+ this.upsert(table, key, resolved);
153
+ }
154
+ formatUnits(value, decimals) {
155
+ return formatUnits(value, decimals);
156
+ }
131
157
  async findOne(table, where) {
132
158
  this.validateTable(table);
133
159
  const qualifiedTable = `"${this.pgSchemaName}"."${table}"`;
@@ -145,6 +171,47 @@ class SubgraphContext {
145
171
  const { rows } = await sql.raw(query).execute(this.db);
146
172
  return rows.map((r) => this.coerceRow(table, r));
147
173
  }
174
+ async count(table, where) {
175
+ this.validateTable(table);
176
+ const qualifiedTable = `"${this.pgSchemaName}"."${table}"`;
177
+ const whereClause = where ? `WHERE ${buildWhereClause(where).clause}` : "";
178
+ const { rows } = await sql.raw(`SELECT COUNT(*)::int AS count FROM ${qualifiedTable} ${whereClause}`).execute(this.db);
179
+ return Number(rows[0]?.count ?? 0);
180
+ }
181
+ async sum(table, column, where) {
182
+ this.validateTable(table);
183
+ validateColumnName(column);
184
+ const qualifiedTable = `"${this.pgSchemaName}"."${table}"`;
185
+ const whereClause = where ? `WHERE ${buildWhereClause(where).clause}` : "";
186
+ const { rows } = await sql.raw(`SELECT COALESCE(SUM("${column}"), 0) AS total FROM ${qualifiedTable} ${whereClause}`).execute(this.db);
187
+ return BigInt(rows[0]?.total?.toString() ?? "0");
188
+ }
189
+ async min(table, column, where) {
190
+ this.validateTable(table);
191
+ validateColumnName(column);
192
+ const qualifiedTable = `"${this.pgSchemaName}"."${table}"`;
193
+ const whereClause = where ? `WHERE ${buildWhereClause(where).clause}` : "";
194
+ const { rows } = await sql.raw(`SELECT MIN("${column}") AS val FROM ${qualifiedTable} ${whereClause}`).execute(this.db);
195
+ const val = rows[0]?.val;
196
+ return val != null ? BigInt(val.toString()) : null;
197
+ }
198
+ async max(table, column, where) {
199
+ this.validateTable(table);
200
+ validateColumnName(column);
201
+ const qualifiedTable = `"${this.pgSchemaName}"."${table}"`;
202
+ const whereClause = where ? `WHERE ${buildWhereClause(where).clause}` : "";
203
+ const { rows } = await sql.raw(`SELECT MAX("${column}") AS val FROM ${qualifiedTable} ${whereClause}`).execute(this.db);
204
+ const val = rows[0]?.val;
205
+ return val != null ? BigInt(val.toString()) : null;
206
+ }
207
+ async countDistinct(table, column, where) {
208
+ this.validateTable(table);
209
+ validateColumnName(column);
210
+ const qualifiedTable = `"${this.pgSchemaName}"."${table}"`;
211
+ const whereClause = where ? `WHERE ${buildWhereClause(where).clause}` : "";
212
+ const { rows } = await sql.raw(`SELECT COUNT(DISTINCT "${column}")::int AS count FROM ${qualifiedTable} ${whereClause}`).execute(this.db);
213
+ return Number(rows[0]?.count ?? 0);
214
+ }
148
215
  coerceRow(table, row) {
149
216
  const tableDef = this.subgraphSchema[table];
150
217
  if (!tableDef)
@@ -285,12 +352,12 @@ function buildWhereClause(where) {
285
352
  }
286
353
 
287
354
  // src/runtime/clarity.ts
288
- import { cvToJSON, deserializeCV } from "@secondlayer/stacks/clarity";
355
+ import { cvToValue, deserializeCV } from "@secondlayer/stacks/clarity";
289
356
  function decodeClarityValue(hex) {
290
357
  try {
291
358
  const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
292
359
  const cv = deserializeCV(cleanHex);
293
- return cvToJSON(cv);
360
+ return cvToValue(cv);
294
361
  } catch {
295
362
  return hex;
296
363
  }
@@ -319,19 +386,210 @@ function decodeFunctionArgs(args) {
319
386
  import { getErrorMessage } from "@secondlayer/shared";
320
387
  import { logger as logger2 } from "@secondlayer/shared/logger";
321
388
  var DEFAULT_ERROR_THRESHOLD = 50;
322
- function resolveHandler(handlers, key) {
323
- return handlers[key] ?? handlers["*"] ?? null;
389
+ function camelCase(str) {
390
+ return str.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
391
+ }
392
+ function camelizeKeys(obj) {
393
+ if (obj === null || obj === undefined)
394
+ return obj;
395
+ if (typeof obj !== "object")
396
+ return obj;
397
+ if (Array.isArray(obj))
398
+ return obj.map(camelizeKeys);
399
+ const result = {};
400
+ for (const [k, v] of Object.entries(obj)) {
401
+ result[camelCase(k)] = camelizeKeys(v);
402
+ }
403
+ return result;
404
+ }
405
+ function decodeFunctionArgs2(args) {
406
+ if (!Array.isArray(args))
407
+ return [];
408
+ return args.map((arg) => {
409
+ if (typeof arg === "string")
410
+ return decodeClarityValue(arg);
411
+ return arg;
412
+ });
413
+ }
414
+ function decodeRawResult(raw) {
415
+ if (typeof raw === "string" && raw.length > 2) {
416
+ return decodeClarityValue(raw);
417
+ }
418
+ return null;
419
+ }
420
+ function safeBigInt(val) {
421
+ if (typeof val === "bigint")
422
+ return val;
423
+ if (typeof val === "number")
424
+ return BigInt(val);
425
+ if (typeof val === "string") {
426
+ try {
427
+ return BigInt(val);
428
+ } catch {
429
+ return 0n;
430
+ }
431
+ }
432
+ return 0n;
433
+ }
434
+ function buildEventPayload(filter, tx, event) {
435
+ const txMeta = {
436
+ txId: tx.tx_id,
437
+ sender: tx.sender,
438
+ type: tx.type,
439
+ status: tx.status,
440
+ contractId: tx.contract_id ?? null,
441
+ functionName: tx.function_name ?? null
442
+ };
443
+ const decodedArgs = decodeFunctionArgs2(tx.function_args);
444
+ const decodedResult = decodeRawResult(tx.raw_result);
445
+ if (!event) {
446
+ switch (filter.type) {
447
+ case "contract_call":
448
+ return {
449
+ contractId: tx.contract_id ?? "",
450
+ functionName: tx.function_name ?? "",
451
+ caller: tx.sender,
452
+ args: decodedArgs,
453
+ result: decodedResult,
454
+ tx: txMeta
455
+ };
456
+ case "contract_deploy":
457
+ return {
458
+ contractId: tx.contract_id ?? "",
459
+ deployer: tx.sender,
460
+ tx: txMeta
461
+ };
462
+ default:
463
+ return { tx: txMeta };
464
+ }
465
+ }
466
+ const decoded = decodeEventData(event.data);
467
+ switch (filter.type) {
468
+ case "ft_transfer":
469
+ return {
470
+ sender: decoded.sender,
471
+ recipient: decoded.recipient,
472
+ amount: safeBigInt(decoded.amount),
473
+ assetIdentifier: decoded.asset_identifier,
474
+ tx: txMeta
475
+ };
476
+ case "ft_mint":
477
+ return {
478
+ recipient: decoded.recipient,
479
+ amount: safeBigInt(decoded.amount),
480
+ assetIdentifier: decoded.asset_identifier,
481
+ tx: txMeta
482
+ };
483
+ case "ft_burn":
484
+ return {
485
+ sender: decoded.sender,
486
+ amount: safeBigInt(decoded.amount),
487
+ assetIdentifier: decoded.asset_identifier,
488
+ tx: txMeta
489
+ };
490
+ case "nft_transfer":
491
+ return {
492
+ sender: decoded.sender,
493
+ recipient: decoded.recipient,
494
+ tokenId: decoded.value,
495
+ assetIdentifier: decoded.asset_identifier,
496
+ tx: txMeta
497
+ };
498
+ case "nft_mint":
499
+ return {
500
+ recipient: decoded.recipient,
501
+ tokenId: decoded.value,
502
+ assetIdentifier: decoded.asset_identifier,
503
+ tx: txMeta
504
+ };
505
+ case "nft_burn":
506
+ return {
507
+ sender: decoded.sender,
508
+ tokenId: decoded.value,
509
+ assetIdentifier: decoded.asset_identifier,
510
+ tx: txMeta
511
+ };
512
+ case "stx_transfer":
513
+ return {
514
+ sender: decoded.sender,
515
+ recipient: decoded.recipient,
516
+ amount: safeBigInt(decoded.amount),
517
+ memo: decoded.memo ?? "",
518
+ tx: txMeta
519
+ };
520
+ case "stx_mint":
521
+ return {
522
+ recipient: decoded.recipient,
523
+ amount: safeBigInt(decoded.amount),
524
+ tx: txMeta
525
+ };
526
+ case "stx_burn":
527
+ return {
528
+ sender: decoded.sender,
529
+ amount: safeBigInt(decoded.amount),
530
+ tx: txMeta
531
+ };
532
+ case "stx_lock":
533
+ return {
534
+ lockedAddress: decoded.locked_address,
535
+ lockedAmount: safeBigInt(decoded.locked_amount),
536
+ unlockHeight: safeBigInt(decoded.unlock_height),
537
+ tx: txMeta
538
+ };
539
+ case "print_event": {
540
+ const rawValue = decoded.value;
541
+ const clarityObj = rawValue && typeof rawValue === "object" && !Array.isArray(rawValue) ? rawValue : null;
542
+ const topic = clarityObj?.topic ? String(clarityObj.topic) : decoded.topic ?? "";
543
+ const { topic: _, ...rest } = clarityObj ?? {};
544
+ const data = Object.keys(rest).length > 0 ? camelizeKeys(rest) : rawValue && typeof rawValue !== "object" ? rawValue : {};
545
+ return {
546
+ contractId: decoded.contract_identifier ?? tx.contract_id ?? "",
547
+ topic,
548
+ data: data ?? {},
549
+ tx: txMeta
550
+ };
551
+ }
552
+ case "contract_call":
553
+ return {
554
+ ...decoded,
555
+ _eventType: event.type,
556
+ contractId: tx.contract_id ?? "",
557
+ functionName: tx.function_name ?? "",
558
+ caller: tx.sender,
559
+ args: decodedArgs,
560
+ result: decodedResult,
561
+ tx: txMeta
562
+ };
563
+ case "contract_deploy":
564
+ return {
565
+ contractId: tx.contract_id ?? "",
566
+ deployer: tx.sender,
567
+ tx: txMeta
568
+ };
569
+ default:
570
+ return {
571
+ ...decoded,
572
+ _eventType: event.type,
573
+ tx: txMeta
574
+ };
575
+ }
324
576
  }
325
577
  async function runHandlers(subgraph, matched, ctx, opts) {
326
578
  let processed = 0;
327
579
  let errors = 0;
328
580
  const threshold = opts?.errorThreshold ?? DEFAULT_ERROR_THRESHOLD;
329
- for (const { tx, events, sourceKey: sourceKey2 } of matched) {
330
- const handler = resolveHandler(subgraph.handlers, sourceKey2);
581
+ const filterLookup = new Map;
582
+ if (!Array.isArray(subgraph.sources)) {
583
+ for (const [name, filter] of Object.entries(subgraph.sources)) {
584
+ filterLookup.set(name, filter);
585
+ }
586
+ }
587
+ for (const { tx, events, sourceName } of matched) {
588
+ const handler = subgraph.handlers[sourceName] ?? subgraph.handlers["*"] ?? null;
331
589
  if (!handler) {
332
- logger2.warn("No handler found for source key", {
590
+ logger2.warn("No handler found for source", {
333
591
  subgraph: subgraph.name,
334
- sourceKey: sourceKey2,
592
+ sourceName,
335
593
  txId: tx.tx_id
336
594
  });
337
595
  continue;
@@ -340,11 +598,14 @@ async function runHandlers(subgraph, matched, ctx, opts) {
340
598
  txId: tx.tx_id,
341
599
  sender: tx.sender,
342
600
  type: tx.type,
343
- status: tx.status
601
+ status: tx.status,
602
+ contractId: tx.contract_id ?? null,
603
+ functionName: tx.function_name ?? null
344
604
  });
605
+ const filter = filterLookup.get(sourceName);
345
606
  if (events.length === 0) {
346
607
  try {
347
- const txPayload = {
608
+ const payload = filter ? buildEventPayload(filter, tx, null) : {
348
609
  tx: {
349
610
  txId: tx.tx_id,
350
611
  sender: tx.sender,
@@ -354,13 +615,13 @@ async function runHandlers(subgraph, matched, ctx, opts) {
354
615
  functionName: tx.function_name
355
616
  }
356
617
  };
357
- await handler(txPayload, ctx);
618
+ await handler(payload, ctx);
358
619
  processed++;
359
620
  } catch (err) {
360
621
  errors++;
361
622
  logger2.error("Subgraph handler error", {
362
623
  subgraph: subgraph.name,
363
- sourceKey: sourceKey2,
624
+ sourceName,
364
625
  txId: tx.tx_id,
365
626
  error: getErrorMessage(err)
366
627
  });
@@ -377,28 +638,30 @@ async function runHandlers(subgraph, matched, ctx, opts) {
377
638
  return { processed, errors };
378
639
  }
379
640
  try {
380
- const decoded = decodeEventData(event.data);
381
- const eventPayload = {
382
- ...decoded,
383
- _eventId: event.id,
384
- _eventType: event.type,
385
- _eventIndex: event.event_index,
386
- tx: {
387
- txId: tx.tx_id,
388
- sender: tx.sender,
389
- type: tx.type,
390
- status: tx.status,
391
- contractId: tx.contract_id,
392
- functionName: tx.function_name
393
- }
394
- };
395
- await handler(eventPayload, ctx);
641
+ const payload = filter ? buildEventPayload(filter, tx, event) : (() => {
642
+ const decoded = decodeEventData(event.data);
643
+ return {
644
+ ...decoded,
645
+ _eventId: event.id,
646
+ _eventType: event.type,
647
+ _eventIndex: event.event_index,
648
+ tx: {
649
+ txId: tx.tx_id,
650
+ sender: tx.sender,
651
+ type: tx.type,
652
+ status: tx.status,
653
+ contractId: tx.contract_id,
654
+ functionName: tx.function_name
655
+ }
656
+ };
657
+ })();
658
+ await handler(payload, ctx);
396
659
  processed++;
397
660
  } catch (err) {
398
661
  errors++;
399
662
  logger2.error("Subgraph handler error", {
400
663
  subgraph: subgraph.name,
401
- sourceKey: sourceKey2,
664
+ sourceName,
402
665
  txId: tx.tx_id,
403
666
  eventId: event.id,
404
667
  eventType: event.type,
@@ -423,62 +686,186 @@ function matchPattern(value, pattern) {
423
686
  }
424
687
  return re.test(value);
425
688
  }
426
- function matchSource(source, transactions, eventsByTx) {
689
+ function matchFilter(filter, transactions, eventsByTx) {
427
690
  const results = [];
428
- const key = sourceKey(source);
429
- for (const tx of transactions) {
430
- if (source.type) {
431
- if (!matchPattern(tx.type, source.type))
432
- continue;
433
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
434
- let matchedEvents = txEvents;
435
- if (source.minAmount !== undefined) {
436
- const amountEvents = matchedEvents.filter((e) => {
691
+ switch (filter.type) {
692
+ case "stx_transfer":
693
+ case "stx_mint":
694
+ case "stx_burn":
695
+ case "stx_lock": {
696
+ const eventType = `${filter.type}_event`;
697
+ for (const tx of transactions) {
698
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
699
+ const matched = txEvents.filter((e) => e.type === eventType);
700
+ if (matched.length === 0)
701
+ continue;
702
+ const filtered = matched.filter((e) => {
437
703
  const data = e.data;
438
- const rawAmount = data?.amount;
439
- if (rawAmount === undefined)
704
+ if (!data)
440
705
  return false;
441
- const amount = BigInt(rawAmount);
442
- return amount >= source.minAmount;
706
+ if ("sender" in filter && filter.sender) {
707
+ if (!matchPattern(data.sender, filter.sender))
708
+ return false;
709
+ }
710
+ if ("recipient" in filter && filter.recipient) {
711
+ if (!matchPattern(data.recipient, filter.recipient))
712
+ return false;
713
+ }
714
+ if ("lockedAddress" in filter && filter.lockedAddress) {
715
+ if (!matchPattern(data.locked_address, filter.lockedAddress))
716
+ return false;
717
+ }
718
+ if ("minAmount" in filter && filter.minAmount !== undefined) {
719
+ const amount = BigInt(data.amount ?? data.locked_amount ?? "0");
720
+ if (amount < filter.minAmount)
721
+ return false;
722
+ }
723
+ if ("maxAmount" in filter && filter.maxAmount !== undefined) {
724
+ const amount = BigInt(data.amount ?? "0");
725
+ if (amount > filter.maxAmount)
726
+ return false;
727
+ }
728
+ return true;
443
729
  });
444
- if (amountEvents.length === 0)
445
- continue;
446
- matchedEvents = amountEvents;
730
+ if (filtered.length > 0) {
731
+ results.push({ tx, events: filtered });
732
+ }
447
733
  }
448
- results.push({ tx, events: matchedEvents, sourceKey: key });
449
- continue;
734
+ break;
450
735
  }
451
- if (source.contract) {
452
- const txContractMatch = tx.contract_id && matchPattern(tx.contract_id, source.contract);
453
- if (source.function && tx.function_name) {
454
- if (!matchPattern(tx.function_name, source.function))
455
- continue;
456
- } else if (source.function && !tx.function_name) {
457
- continue;
736
+ case "ft_transfer":
737
+ case "ft_mint":
738
+ case "ft_burn": {
739
+ const eventType = `${filter.type}_event`;
740
+ for (const tx of transactions) {
741
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
742
+ const matched = txEvents.filter((e) => {
743
+ if (e.type !== eventType)
744
+ return false;
745
+ const data = e.data;
746
+ if (!data)
747
+ return false;
748
+ if (filter.assetIdentifier) {
749
+ const assetId = data.asset_identifier;
750
+ if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
751
+ return false;
752
+ }
753
+ if ("sender" in filter && filter.sender) {
754
+ if (!matchPattern(data.sender, filter.sender))
755
+ return false;
756
+ }
757
+ if ("recipient" in filter && filter.recipient) {
758
+ if (!matchPattern(data.recipient, filter.recipient))
759
+ return false;
760
+ }
761
+ if (filter.minAmount !== undefined) {
762
+ const amount = BigInt(data.amount ?? "0");
763
+ if (amount < filter.minAmount)
764
+ return false;
765
+ }
766
+ return true;
767
+ });
768
+ if (matched.length > 0) {
769
+ results.push({ tx, events: matched });
770
+ }
458
771
  }
459
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
460
- let matchedEvents = txEvents;
461
- if (!txContractMatch) {
462
- matchedEvents = txEvents.filter((e) => {
772
+ break;
773
+ }
774
+ case "nft_transfer":
775
+ case "nft_mint":
776
+ case "nft_burn": {
777
+ const eventType = `${filter.type}_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 !== eventType)
782
+ return false;
463
783
  const data = e.data;
464
- const contractIdentifier = data?.contract_identifier;
465
- return contractIdentifier && matchPattern(contractIdentifier, source.contract);
784
+ if (!data)
785
+ return false;
786
+ if (filter.assetIdentifier) {
787
+ const assetId = data.asset_identifier;
788
+ if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
789
+ return false;
790
+ }
791
+ if ("sender" in filter && filter.sender) {
792
+ if (!matchPattern(data.sender, filter.sender))
793
+ return false;
794
+ }
795
+ if ("recipient" in filter && filter.recipient) {
796
+ if (!matchPattern(data.recipient, filter.recipient))
797
+ return false;
798
+ }
799
+ return true;
466
800
  });
467
- if (matchedEvents.length === 0)
801
+ if (matched.length > 0) {
802
+ results.push({ tx, events: matched });
803
+ }
804
+ }
805
+ break;
806
+ }
807
+ case "contract_call": {
808
+ for (const tx of transactions) {
809
+ if (tx.type !== "contract_call")
810
+ continue;
811
+ if (filter.contractId) {
812
+ if (!tx.contract_id || !matchPattern(tx.contract_id, filter.contractId))
813
+ continue;
814
+ }
815
+ if (filter.functionName) {
816
+ if (!tx.function_name || !matchPattern(tx.function_name, filter.functionName))
817
+ continue;
818
+ }
819
+ if (filter.caller) {
820
+ if (!matchPattern(tx.sender, filter.caller))
821
+ continue;
822
+ }
823
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
824
+ results.push({ tx, events: txEvents });
825
+ }
826
+ break;
827
+ }
828
+ case "contract_deploy": {
829
+ for (const tx of transactions) {
830
+ if (tx.type !== "smart_contract")
468
831
  continue;
832
+ if (filter.deployer) {
833
+ if (!matchPattern(tx.sender, filter.deployer))
834
+ continue;
835
+ }
836
+ if (filter.contractName) {
837
+ const name = tx.contract_id?.split(".")[1] ?? "";
838
+ if (!matchPattern(name, filter.contractName))
839
+ continue;
840
+ }
841
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
842
+ results.push({ tx, events: txEvents });
469
843
  }
470
- if (source.event) {
471
- matchedEvents = matchedEvents.filter((e) => {
472
- if (matchPattern(e.type, source.event))
473
- return true;
844
+ break;
845
+ }
846
+ case "print_event": {
847
+ for (const tx of transactions) {
848
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
849
+ const matched = txEvents.filter((e) => {
850
+ if (e.type !== "smart_contract_event")
851
+ return false;
474
852
  const data = e.data;
475
- const topic = data?.topic;
476
- return topic ? matchPattern(topic, source.event) : false;
853
+ if (!data)
854
+ return false;
855
+ if (data.topic !== "print")
856
+ return false;
857
+ if (filter.contractId) {
858
+ const contractId = data.contract_identifier;
859
+ if (!contractId || !matchPattern(contractId, filter.contractId))
860
+ return false;
861
+ }
862
+ return true;
477
863
  });
864
+ if (matched.length > 0) {
865
+ results.push({ tx, events: matched });
866
+ }
478
867
  }
479
- if (txContractMatch || matchedEvents.length > 0) {
480
- results.push({ tx, events: matchedEvents, sourceKey: key });
481
- }
868
+ break;
482
869
  }
483
870
  }
484
871
  return results;
@@ -492,13 +879,13 @@ function matchSources(sources, transactions, events) {
492
879
  }
493
880
  const seen = new Set;
494
881
  const results = [];
495
- for (const source of sources) {
496
- const matches = matchSource(source, transactions, eventsByTx);
882
+ for (const [sourceName, filter] of Object.entries(sources)) {
883
+ const matches = matchFilter(filter, transactions, eventsByTx);
497
884
  for (const match of matches) {
498
- const dedupeKey = `${match.tx.tx_id}:${match.sourceKey}`;
885
+ const dedupeKey = `${match.tx.tx_id}:${sourceName}`;
499
886
  if (!seen.has(dedupeKey)) {
500
887
  seen.add(dedupeKey);
501
- results.push(match);
888
+ results.push({ ...match, sourceName });
502
889
  }
503
890
  }
504
891
  }
@@ -1175,6 +1562,7 @@ async function backfillSubgraph(def, opts) {
1175
1562
  throw err;
1176
1563
  }
1177
1564
  }
1565
+
1178
1566
  // src/define.ts
1179
1567
  function defineSubgraph(def) {
1180
1568
  return def;
@@ -1351,7 +1739,6 @@ function getDefault(type) {
1351
1739
  }
1352
1740
  export {
1353
1741
  validateSubgraphDefinition,
1354
- sourceKey,
1355
1742
  resumeReindex,
1356
1743
  reindexSubgraph,
1357
1744
  pgSchemaName,
@@ -1362,5 +1749,5 @@ export {
1362
1749
  backfillSubgraph
1363
1750
  };
1364
1751
 
1365
- //# debugId=4B7B76347D5AD39564756E2164756E21
1752
+ //# debugId=E806638CA5CB2E9E64756E2164756E21
1366
1753
  //# sourceMappingURL=index.js.map