@secondlayer/subgraphs 4.3.4 → 4.4.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 (42) hide show
  1. package/dist/src/index.d.ts +63 -4
  2. package/dist/src/index.js +307 -28
  3. package/dist/src/index.js.map +14 -14
  4. package/dist/src/runtime/block-processor.d.ts +24 -2
  5. package/dist/src/runtime/block-processor.js +252 -21
  6. package/dist/src/runtime/block-processor.js.map +10 -10
  7. package/dist/src/runtime/catchup.d.ts +18 -1
  8. package/dist/src/runtime/catchup.js +253 -22
  9. package/dist/src/runtime/catchup.js.map +11 -11
  10. package/dist/src/runtime/context.js +20 -3
  11. package/dist/src/runtime/context.js.map +3 -3
  12. package/dist/src/runtime/processor.js +254 -23
  13. package/dist/src/runtime/processor.js.map +12 -12
  14. package/dist/src/runtime/reindex.d.ts +28 -1
  15. package/dist/src/runtime/reindex.js +254 -23
  16. package/dist/src/runtime/reindex.js.map +12 -12
  17. package/dist/src/runtime/reorg.d.ts +18 -1
  18. package/dist/src/runtime/reorg.js +253 -22
  19. package/dist/src/runtime/reorg.js.map +11 -11
  20. package/dist/src/runtime/replay.d.ts +10 -0
  21. package/dist/src/runtime/replay.js +184 -20
  22. package/dist/src/runtime/replay.js.map +9 -9
  23. package/dist/src/runtime/runner.d.ts +21 -1
  24. package/dist/src/runtime/runner.js +121 -5
  25. package/dist/src/runtime/runner.js.map +4 -4
  26. package/dist/src/runtime/source-matcher.d.ts +22 -2
  27. package/dist/src/runtime/source-matcher.js +61 -4
  28. package/dist/src/runtime/source-matcher.js.map +3 -3
  29. package/dist/src/schema/index.d.ts +18 -1
  30. package/dist/src/schema/index.js +73 -8
  31. package/dist/src/schema/index.js.map +5 -5
  32. package/dist/src/service.js +254 -23
  33. package/dist/src/service.js.map +12 -12
  34. package/dist/src/testing/index.d.ts +64 -2
  35. package/dist/src/testing/index.js +176 -7
  36. package/dist/src/testing/index.js.map +6 -6
  37. package/dist/src/triggers/index.d.ts +18 -1
  38. package/dist/src/types.d.ts +19 -2
  39. package/dist/src/validate.d.ts +19 -2
  40. package/dist/src/validate.js +44 -2
  41. package/dist/src/validate.js.map +3 -3
  42. package/package.json +3 -3
@@ -260,6 +260,23 @@ interface ContractDeployFilter {
260
260
  deployer?: string;
261
261
  contractName?: string;
262
262
  }
263
+ interface NestedContractCallFilter extends TraitScope, FactoryScope {
264
+ type: "nested_contract_call";
265
+ contractId?: string | readonly string[];
266
+ functionName?: string;
267
+ caller?: string;
268
+ sender?: string;
269
+ }
270
+ interface VarSetFilter extends TraitScope, FactoryScope {
271
+ type: "var_set";
272
+ contractId?: string | readonly string[];
273
+ varName?: string;
274
+ }
275
+ interface MapWriteFilter extends TraitScope, FactoryScope {
276
+ type: "map_set" | "map_insert" | "map_delete";
277
+ contractId?: string | readonly string[];
278
+ map?: string;
279
+ }
263
280
  /**
264
281
  * One declared field of a print payload.
265
282
  *
@@ -347,7 +364,7 @@ type UnpinnedPrintEventFilter = PrintEventFields & {
347
364
  */
348
365
  type PrintEventFilter = PinnedPrintEventFilter | TraitPrintEventFilter | UnpinnedPrintEventFilter;
349
366
  /** All subgraph filter types — discriminated on `type` */
350
- type SubgraphFilter = StxTransferFilter | StxMintFilter | StxBurnFilter | StxLockFilter | FtTransferFilter | FtMintFilter | FtBurnFilter | NftTransferFilter | NftMintFilter | NftBurnFilter | ContractCallFilter | ContractDeployFilter | PrintEventFilter;
367
+ type SubgraphFilter = StxTransferFilter | StxMintFilter | StxBurnFilter | StxLockFilter | FtTransferFilter | FtMintFilter | FtBurnFilter | NftTransferFilter | NftMintFilter | NftBurnFilter | ContractCallFilter | ContractDeployFilter | PrintEventFilter | NestedContractCallFilter | VarSetFilter | MapWriteFilter;
351
368
  /** Transaction metadata available in handlers */
352
369
  interface TxMeta {
353
370
  txId: string;
@@ -383,8 +400,11 @@ type EventRecord = {
383
400
  id: string
384
401
  tx_id: string
385
402
  type: string
403
+ /** Classic `event_index`, or `ordinal` when `clock === "vm"`. */
386
404
  event_index: number
387
405
  data: unknown
406
+ /** Set on opt-in `vm_events` rows. Two clocks never sort or dedupe together. */
407
+ clock?: "vm"
388
408
  };
389
409
  interface BlockMeta {
390
410
  height: number;
@@ -610,6 +630,42 @@ type ContractCallPayload<F> = F extends {
610
630
  } ? N extends ExtractFunctionNames<A> ? ContractCallEvent & {
611
631
  input: ExtractFunctionArgs2<A, N>
612
632
  } : ContractCallEvent : ContractCallEvent;
633
+ interface NestedContractCallPayload {
634
+ contractId: string;
635
+ sender: string | null;
636
+ caller: string;
637
+ functionName: string;
638
+ /** Decoded `function_args`. */
639
+ args: unknown[];
640
+ /** Original hex `function_args`, preserved. */
641
+ arguments: string[];
642
+ rawResult: string;
643
+ result: unknown;
644
+ tx: TxMeta;
645
+ }
646
+ interface VarSetPayload {
647
+ contractId: string;
648
+ varName: string;
649
+ rawValue: string;
650
+ value: unknown;
651
+ tx: TxMeta;
652
+ }
653
+ interface MapWritePayload {
654
+ contractId: string;
655
+ map: string;
656
+ rawKey: string;
657
+ key: unknown;
658
+ rawValue: string;
659
+ value: unknown;
660
+ tx: TxMeta;
661
+ }
662
+ interface MapDeletePayload {
663
+ contractId: string;
664
+ map: string;
665
+ rawKey: string;
666
+ key: unknown;
667
+ tx: TxMeta;
668
+ }
613
669
  /**
614
670
  * Print event typed per topic. When a `print_event` source declares a `prints`
615
671
  * map, the payload is a discriminated union keyed by `topic` with `data` typed
@@ -671,7 +727,13 @@ type EventForFilter<F extends SubgraphFilter> = F extends {
671
727
  type: "contract_call"
672
728
  } ? ContractCallPayload<F> : F extends {
673
729
  type: "contract_deploy"
674
- } ? ContractDeployPayload : never;
730
+ } ? ContractDeployPayload : F extends {
731
+ type: "nested_contract_call"
732
+ } ? NestedContractCallPayload : F extends {
733
+ type: "var_set"
734
+ } ? VarSetPayload : F extends {
735
+ type: infer T extends "map_set" | "map_insert" | "map_delete"
736
+ } ? T extends "map_delete" ? MapDeletePayload : MapWritePayload : never;
675
737
  /** An in-memory subgraph context for tests. */
676
738
  interface TestSubgraphContext<S extends SubgraphSchema> extends Omit<TypedSubgraphContext<S>, never> {
677
739
  /**
@@ -41,6 +41,24 @@ function safeIndexName(name) {
41
41
  const hash = createHash("sha256").update(name).digest("hex").slice(0, 8);
42
42
  return `${name.slice(0, MAX_IDENT_BYTES - hash.length - 1)}_${hash}`;
43
43
  }
44
+ function emitIndexedColumnIndexDDL(schemaName, tableName, colName, opts) {
45
+ const concurrently = opts?.concurrently ? " CONCURRENTLY" : "";
46
+ const qualified = `${quotePgIdent(schemaName)}.${quotePgIdent(tableName)}`;
47
+ const plain = `idx_${schemaName}_${tableName}_${colName}`;
48
+ const composite = safeIndexName(`idx_${schemaName}_${tableName}_${colName}_id`);
49
+ const col = quotePgIdent(colName);
50
+ const id = quotePgIdent("_id");
51
+ return {
52
+ create: [
53
+ `CREATE INDEX${concurrently} IF NOT EXISTS ${quotePgIdent(plain)} ON ${qualified} (${col})`,
54
+ `CREATE INDEX${concurrently} IF NOT EXISTS ${quotePgIdent(composite)} ON ${qualified} (${col}, ${id})`
55
+ ],
56
+ drop: [
57
+ `DROP INDEX${concurrently} IF EXISTS ${quotePgIdent(schemaName)}.${quotePgIdent(plain)}`,
58
+ `DROP INDEX${concurrently} IF EXISTS ${quotePgIdent(schemaName)}.${quotePgIdent(composite)}`
59
+ ]
60
+ };
61
+ }
44
62
  function escapeLiteralDefault(value) {
45
63
  if (value === null || value === undefined)
46
64
  return "NULL";
@@ -82,8 +100,7 @@ function emitTableDDL(schemaName, tableName, tableDef) {
82
100
  statements.push(`CREATE INDEX IF NOT EXISTS ${quotePgIdent(`idx_${schemaName}_${tableName}_tx_id`)} ON ${qualifiedName} (_tx_id)`);
83
101
  for (const [colName, col] of Object.entries(tableDef.columns)) {
84
102
  if (col.indexed) {
85
- statements.push(`CREATE INDEX IF NOT EXISTS ${quotePgIdent(`idx_${schemaName}_${tableName}_${colName}`)} ON ${qualifiedName} (${quotePgIdent(colName)})`);
86
- statements.push(`CREATE INDEX IF NOT EXISTS ${quotePgIdent(safeIndexName(`idx_${schemaName}_${tableName}_${colName}_id`))} ON ${qualifiedName} (${quotePgIdent(colName)}, ${quotePgIdent("_id")})`);
103
+ statements.push(...emitIndexedColumnIndexDDL(schemaName, tableName, colName).create);
87
104
  }
88
105
  }
89
106
  for (const [colName, col] of Object.entries(tableDef.columns)) {
@@ -992,7 +1009,7 @@ function printContractId(data) {
992
1009
  const id = (typeof data.contract_identifier === "string" ? data.contract_identifier : undefined) ?? (typeof data.contract_id === "string" ? data.contract_id : undefined);
993
1010
  return id || undefined;
994
1011
  }
995
- function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts = new Map) {
1012
+ function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts = new Map, vmEventsByTx = new Map) {
996
1013
  const results = [];
997
1014
  switch (filter.type) {
998
1015
  case "stx_transfer":
@@ -1157,6 +1174,57 @@ function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryCo
1157
1174
  }
1158
1175
  break;
1159
1176
  }
1177
+ case "nested_contract_call":
1178
+ case "var_set":
1179
+ case "map_set":
1180
+ case "map_insert":
1181
+ case "map_delete": {
1182
+ for (const tx of transactions) {
1183
+ const txEvents = vmEventsByTx.get(tx.tx_id) ?? [];
1184
+ const matched = txEvents.filter((e) => {
1185
+ if (e.type !== filter.type)
1186
+ return false;
1187
+ const data = e.data;
1188
+ if (!data)
1189
+ return false;
1190
+ const contractId = printContractId(data);
1191
+ if ("contractId" in filter && filter.contractId) {
1192
+ if (!contractId || !matchAny(contractId, filter.contractId))
1193
+ return false;
1194
+ }
1195
+ if (!traitAllows(filter, contractId, traitContracts))
1196
+ return false;
1197
+ if (!factoryAllows(filter, contractId, factoryContracts))
1198
+ return false;
1199
+ if (filter.type === "nested_contract_call") {
1200
+ if (filter.functionName) {
1201
+ if (typeof data.function_name !== "string" || !matchPattern(data.function_name, filter.functionName))
1202
+ return false;
1203
+ }
1204
+ if (filter.caller) {
1205
+ if (typeof data.caller !== "string" || !matchPattern(data.caller, filter.caller))
1206
+ return false;
1207
+ }
1208
+ if (filter.sender) {
1209
+ if (typeof data.sender !== "string" || !matchPattern(data.sender, filter.sender))
1210
+ return false;
1211
+ }
1212
+ }
1213
+ if (filter.type === "var_set" && filter.varName) {
1214
+ if (typeof data.var_name !== "string" || !matchPattern(data.var_name, filter.varName))
1215
+ return false;
1216
+ }
1217
+ if ((filter.type === "map_set" || filter.type === "map_insert" || filter.type === "map_delete") && filter.map) {
1218
+ if (typeof data.map_name !== "string" || !matchPattern(data.map_name, filter.map))
1219
+ return false;
1220
+ }
1221
+ return true;
1222
+ });
1223
+ if (matched.length > 0)
1224
+ results.push({ tx, events: matched });
1225
+ }
1226
+ break;
1227
+ }
1160
1228
  case "print_event": {
1161
1229
  for (const tx of transactions) {
1162
1230
  const txEvents = eventsByTx.get(tx.tx_id) ?? [];
@@ -1188,17 +1256,23 @@ function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryCo
1188
1256
  }
1189
1257
  return results;
1190
1258
  }
1191
- function matchSources(sources, transactions, events, traitContracts = new Map, factoryContracts = new Map) {
1259
+ function matchSources(sources, transactions, events, traitContracts = new Map, factoryContracts = new Map, vmEvents = []) {
1192
1260
  const eventsByTx = new Map;
1193
1261
  for (const event of events) {
1194
1262
  const list = eventsByTx.get(event.tx_id) ?? [];
1195
1263
  list.push(event);
1196
1264
  eventsByTx.set(event.tx_id, list);
1197
1265
  }
1266
+ const vmEventsByTx = new Map;
1267
+ for (const event of vmEvents) {
1268
+ const list = vmEventsByTx.get(event.tx_id) ?? [];
1269
+ list.push(event);
1270
+ vmEventsByTx.set(event.tx_id, list);
1271
+ }
1198
1272
  const seen = new Set;
1199
1273
  const results = [];
1200
1274
  for (const [sourceName, filter] of Object.entries(sources)) {
1201
- const matches = matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts);
1275
+ const matches = matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts, vmEventsByTx);
1202
1276
  for (const match of matches) {
1203
1277
  const dedupeKey = `${match.tx.tx_id}:${sourceName}`;
1204
1278
  if (!seen.has(dedupeKey)) {
@@ -1773,6 +1847,12 @@ function decodeRawResult(raw) {
1773
1847
  }
1774
1848
  return null;
1775
1849
  }
1850
+ function decodeRawHex(hex) {
1851
+ if (hex.startsWith("0x") && hex.length > 2) {
1852
+ return decodeClarityValue(hex);
1853
+ }
1854
+ return hex;
1855
+ }
1776
1856
  function safeBigInt(val) {
1777
1857
  if (typeof val === "bigint")
1778
1858
  return val;
@@ -1966,6 +2046,58 @@ function buildEventPayload(filter, tx, event) {
1966
2046
  deployer: tx.sender,
1967
2047
  tx: txMeta
1968
2048
  };
2049
+ case "nested_contract_call": {
2050
+ const data = event.data ?? {};
2051
+ const argsHex = Array.isArray(data.function_args) ? data.function_args.filter((a) => typeof a === "string") : [];
2052
+ return {
2053
+ contractId: printContractId(data) ?? "",
2054
+ sender: typeof data.sender === "string" ? data.sender : null,
2055
+ caller: typeof data.caller === "string" ? data.caller : "",
2056
+ functionName: typeof data.function_name === "string" ? data.function_name : "",
2057
+ args: decodeFunctionArgs2(argsHex),
2058
+ arguments: argsHex,
2059
+ rawResult: typeof data.raw_result === "string" ? data.raw_result : "",
2060
+ result: decodeRawResult(data.raw_result),
2061
+ tx: txMeta
2062
+ };
2063
+ }
2064
+ case "var_set": {
2065
+ const data = event.data ?? {};
2066
+ const rawValue = typeof data.raw_value === "string" ? data.raw_value : "";
2067
+ return {
2068
+ contractId: printContractId(data) ?? "",
2069
+ varName: typeof data.var_name === "string" ? data.var_name : "",
2070
+ rawValue,
2071
+ value: decodeRawHex(rawValue),
2072
+ tx: txMeta
2073
+ };
2074
+ }
2075
+ case "map_set":
2076
+ case "map_insert": {
2077
+ const data = event.data ?? {};
2078
+ const rawKey = typeof data.raw_key === "string" ? data.raw_key : "";
2079
+ const rawValue = typeof data.raw_value === "string" ? data.raw_value : "";
2080
+ return {
2081
+ contractId: printContractId(data) ?? "",
2082
+ map: typeof data.map_name === "string" ? data.map_name : "",
2083
+ rawKey,
2084
+ key: decodeRawHex(rawKey),
2085
+ rawValue,
2086
+ value: decodeRawHex(rawValue),
2087
+ tx: txMeta
2088
+ };
2089
+ }
2090
+ case "map_delete": {
2091
+ const data = event.data ?? {};
2092
+ const rawKey = typeof data.raw_key === "string" ? data.raw_key : "";
2093
+ return {
2094
+ contractId: printContractId(data) ?? "",
2095
+ map: typeof data.map_name === "string" ? data.map_name : "",
2096
+ rawKey,
2097
+ key: decodeRawHex(rawKey),
2098
+ tx: txMeta
2099
+ };
2100
+ }
1969
2101
  default:
1970
2102
  return {
1971
2103
  ...decoded,
@@ -1994,7 +2126,8 @@ async function runHandlers(subgraph, matched, ctx, opts) {
1994
2126
  units.push({ tx, sourceName, event });
1995
2127
  }
1996
2128
  }
1997
- units.sort((a, b) => (a.tx.tx_index ?? 0) - (b.tx.tx_index ?? 0) || (a.event?.event_index ?? -1) - (b.event?.event_index ?? -1));
2129
+ const clockRank = (e) => e?.clock === "vm" ? 1 : 0;
2130
+ 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));
1998
2131
  for (const { tx, event, sourceName } of units) {
1999
2132
  if (errors >= threshold) {
2000
2133
  logger4.error("Subgraph error threshold reached, skipping remaining events", {
@@ -2260,6 +2393,42 @@ function toHandlerPayload(_filter, row) {
2260
2393
  };
2261
2394
  }
2262
2395
  const r = row;
2396
+ if (row.event_type === "var_set") {
2397
+ const rawValue = typeof r.raw_value === "string" ? r.raw_value : "";
2398
+ return {
2399
+ contractId: row.contract_id ?? "",
2400
+ varName: typeof r.var_name === "string" ? r.var_name : "",
2401
+ rawValue,
2402
+ value: rawValue,
2403
+ tx
2404
+ };
2405
+ }
2406
+ if (row.event_type === "nested_contract_call") {
2407
+ const argsHex = Array.isArray(r.function_args) ? r.function_args.filter((a) => typeof a === "string") : [];
2408
+ return {
2409
+ contractId: row.contract_id ?? "",
2410
+ sender: typeof r.sender === "string" ? r.sender : null,
2411
+ caller: typeof r.caller === "string" ? r.caller : "",
2412
+ functionName: typeof r.function_name === "string" ? r.function_name : "",
2413
+ args: argsHex,
2414
+ arguments: argsHex,
2415
+ rawResult: typeof r.raw_result === "string" ? r.raw_result : "",
2416
+ result: null,
2417
+ tx
2418
+ };
2419
+ }
2420
+ if (row.event_type === "map_set" || row.event_type === "map_insert" || row.event_type === "map_delete") {
2421
+ const rawKey = typeof r.raw_key === "string" ? r.raw_key : "";
2422
+ const rawValue = typeof r.raw_value === "string" ? r.raw_value : "";
2423
+ return {
2424
+ contractId: row.contract_id ?? "",
2425
+ map: typeof r.map === "string" ? r.map : "",
2426
+ rawKey,
2427
+ key: rawKey,
2428
+ ...row.event_type === "map_delete" ? {} : { rawValue, value: rawValue },
2429
+ tx
2430
+ };
2431
+ }
2263
2432
  return {
2264
2433
  ...r.sender !== undefined ? { sender: r.sender } : {},
2265
2434
  ...r.recipient !== undefined ? { recipient: r.recipient } : {},
@@ -2530,5 +2699,5 @@ export {
2530
2699
  buildEvent
2531
2700
  };
2532
2701
 
2533
- //# debugId=863DBB30C4526B5164756E2164756E21
2702
+ //# debugId=BBBAFF796E747D6364756E2164756E21
2534
2703
  //# sourceMappingURL=index.js.map