@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
@@ -2,10 +2,20 @@ import { Database as Database3 } from "@secondlayer/shared/db";
2
2
  import { Webhook } from "@secondlayer/shared/db";
3
3
  import { Kysely as Kysely3 } from "kysely";
4
4
  import { Block, Event, Transaction } from "@secondlayer/shared/db";
5
+ /** A runtime event row. `clock: "vm"` marks an opt-in `vm_events` row whose
6
+ * `event_index` is `ordinal` — a second ordinal that never sorts or
7
+ * dedupes against classic `events.event_index`. */
8
+ type RuntimeEvent = Event & {
9
+ clock?: "vm"
10
+ };
5
11
  interface BlockData {
6
12
  block: Block;
7
13
  txs: Transaction[];
14
+ /** Classic `events` rows (Streams 1.0 clock). */
8
15
  events: Event[];
16
+ /** Opt-in `vm_events` rows on their own clock. Absent/empty on `"*"`
17
+ * nodes and on archives collected without `storage`/`contract_calls`. */
18
+ vmEvents?: RuntimeEvent[];
9
19
  }
10
20
  /**
11
21
  * Where the subgraph runtime reads canonical chain data. Today it taps the
@@ -51,7 +51,7 @@ function printContractId(data) {
51
51
  const id = (typeof data.contract_identifier === "string" ? data.contract_identifier : undefined) ?? (typeof data.contract_id === "string" ? data.contract_id : undefined);
52
52
  return id || undefined;
53
53
  }
54
- function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts = new Map) {
54
+ function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts = new Map, vmEventsByTx = new Map) {
55
55
  const results = [];
56
56
  switch (filter.type) {
57
57
  case "stx_transfer":
@@ -216,6 +216,57 @@ function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryCo
216
216
  }
217
217
  break;
218
218
  }
219
+ case "nested_contract_call":
220
+ case "var_set":
221
+ case "map_set":
222
+ case "map_insert":
223
+ case "map_delete": {
224
+ for (const tx of transactions) {
225
+ const txEvents = vmEventsByTx.get(tx.tx_id) ?? [];
226
+ const matched = txEvents.filter((e) => {
227
+ if (e.type !== filter.type)
228
+ return false;
229
+ const data = e.data;
230
+ if (!data)
231
+ return false;
232
+ const contractId = printContractId(data);
233
+ if ("contractId" in filter && filter.contractId) {
234
+ if (!contractId || !matchAny(contractId, filter.contractId))
235
+ return false;
236
+ }
237
+ if (!traitAllows(filter, contractId, traitContracts))
238
+ return false;
239
+ if (!factoryAllows(filter, contractId, factoryContracts))
240
+ return false;
241
+ if (filter.type === "nested_contract_call") {
242
+ if (filter.functionName) {
243
+ if (typeof data.function_name !== "string" || !matchPattern(data.function_name, filter.functionName))
244
+ return false;
245
+ }
246
+ if (filter.caller) {
247
+ if (typeof data.caller !== "string" || !matchPattern(data.caller, filter.caller))
248
+ return false;
249
+ }
250
+ if (filter.sender) {
251
+ if (typeof data.sender !== "string" || !matchPattern(data.sender, filter.sender))
252
+ return false;
253
+ }
254
+ }
255
+ if (filter.type === "var_set" && filter.varName) {
256
+ if (typeof data.var_name !== "string" || !matchPattern(data.var_name, filter.varName))
257
+ return false;
258
+ }
259
+ if ((filter.type === "map_set" || filter.type === "map_insert" || filter.type === "map_delete") && filter.map) {
260
+ if (typeof data.map_name !== "string" || !matchPattern(data.map_name, filter.map))
261
+ return false;
262
+ }
263
+ return true;
264
+ });
265
+ if (matched.length > 0)
266
+ results.push({ tx, events: matched });
267
+ }
268
+ break;
269
+ }
219
270
  case "print_event": {
220
271
  for (const tx of transactions) {
221
272
  const txEvents = eventsByTx.get(tx.tx_id) ?? [];
@@ -247,17 +298,23 @@ function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryCo
247
298
  }
248
299
  return results;
249
300
  }
250
- function matchSources(sources, transactions, events, traitContracts = new Map, factoryContracts = new Map) {
301
+ function matchSources(sources, transactions, events, traitContracts = new Map, factoryContracts = new Map, vmEvents = []) {
251
302
  const eventsByTx = new Map;
252
303
  for (const event of events) {
253
304
  const list = eventsByTx.get(event.tx_id) ?? [];
254
305
  list.push(event);
255
306
  eventsByTx.set(event.tx_id, list);
256
307
  }
308
+ const vmEventsByTx = new Map;
309
+ for (const event of vmEvents) {
310
+ const list = vmEventsByTx.get(event.tx_id) ?? [];
311
+ list.push(event);
312
+ vmEventsByTx.set(event.tx_id, list);
313
+ }
257
314
  const seen = new Set;
258
315
  const results = [];
259
316
  for (const [sourceName, filter] of Object.entries(sources)) {
260
- const matches = matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts);
317
+ const matches = matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts, vmEventsByTx);
261
318
  for (const match of matches) {
262
319
  const dedupeKey = `${match.tx.tx_id}:${sourceName}`;
263
320
  if (!seen.has(dedupeKey)) {
@@ -299,11 +356,15 @@ import {
299
356
  import { logger } from "@secondlayer/shared/logger";
300
357
 
301
358
  // src/runtime/batch-loader.ts
359
+ function vmEventId(txId, vmEventIndex) {
360
+ return `${txId}#vm:${vmEventIndex}`;
361
+ }
302
362
  async function loadBlockRange(db, fromHeight, toHeight) {
303
- const [blocks, txs, events] = await Promise.all([
363
+ const [blocks, txs, events, vmRows] = await Promise.all([
304
364
  db.selectFrom("blocks").selectAll().where("height", ">=", fromHeight).where("height", "<=", toHeight).where("canonical", "=", true).execute(),
305
365
  db.selectFrom("transactions").selectAll().where("block_height", ">=", fromHeight).where("block_height", "<=", toHeight).execute(),
306
- db.selectFrom("events").selectAll().where("block_height", ">=", fromHeight).where("block_height", "<=", toHeight).execute()
366
+ db.selectFrom("events").selectAll().where("block_height", ">=", fromHeight).where("block_height", "<=", toHeight).execute(),
367
+ db.selectFrom("vm_events").selectAll().where("block_height", ">=", fromHeight).where("block_height", "<=", toHeight).orderBy("ordinal", "asc").execute()
307
368
  ]);
308
369
  const txsByHeight = new Map;
309
370
  for (const tx of txs) {
@@ -319,13 +380,30 @@ async function loadBlockRange(db, fromHeight, toHeight) {
319
380
  list.push(evt);
320
381
  eventsByHeight.set(h, list);
321
382
  }
383
+ const vmByHeight = new Map;
384
+ for (const row of vmRows) {
385
+ const h = Number(row.block_height);
386
+ const list = vmByHeight.get(h) ?? [];
387
+ list.push({
388
+ id: vmEventId(row.tx_id, Number(row.ordinal)),
389
+ tx_id: row.tx_id,
390
+ block_height: h,
391
+ event_index: Number(row.ordinal),
392
+ type: row.type,
393
+ data: row.data,
394
+ created_at: row.created_at,
395
+ clock: "vm"
396
+ });
397
+ vmByHeight.set(h, list);
398
+ }
322
399
  const result = new Map;
323
400
  for (const block of blocks) {
324
401
  const h = Number(block.height);
325
402
  result.set(h, {
326
403
  block,
327
404
  txs: txsByHeight.get(h) ?? [],
328
- events: eventsByHeight.get(h) ?? []
405
+ events: eventsByHeight.get(h) ?? [],
406
+ vmEvents: vmByHeight.get(h) ?? []
329
407
  });
330
408
  }
331
409
  return result;
@@ -341,6 +419,9 @@ function avgEventsPerBlock(batch) {
341
419
  }
342
420
 
343
421
  // src/runtime/observer-http-source.ts
422
+ import {
423
+ VM_NODE_TO_STORED_TYPE
424
+ } from "@secondlayer/shared";
344
425
  var OBSERVER_HTTP_EXPORT_PATH = "/internal/observer-events";
345
426
  function stripTrailingSlash(url) {
346
427
  return url.replace(/\/+$/, "");
@@ -418,7 +499,29 @@ function mapNewBlockPayloadToBlockData(payload) {
418
499
  created_at: new Date(0)
419
500
  };
420
501
  });
421
- return { block, txs, events };
502
+ const vmEvents = [];
503
+ for (const [i, trace] of (p.vm_events ?? []).entries()) {
504
+ const nodeType = trace.type;
505
+ if (typeof nodeType !== "string" || !(nodeType in VM_NODE_TO_STORED_TYPE))
506
+ continue;
507
+ const stored = VM_NODE_TO_STORED_TYPE[nodeType];
508
+ const body = trace[nodeType];
509
+ if (!body || typeof body !== "object")
510
+ continue;
511
+ const txId = trace.txid ?? "";
512
+ const vmIndex = i;
513
+ vmEvents.push({
514
+ id: vmEventId(txId, vmIndex),
515
+ tx_id: txId,
516
+ block_height: height,
517
+ event_index: vmIndex,
518
+ type: stored,
519
+ data: body,
520
+ created_at: new Date(0),
521
+ clock: "vm"
522
+ });
523
+ }
524
+ return { block, txs, events, vmEvents };
422
525
  }
423
526
 
424
527
  class ObserverHttpBlockSource {
@@ -621,6 +724,30 @@ function reconstructEvent(e) {
621
724
  raw_value: e.payload.raw_value
622
725
  }
623
726
  };
727
+ case "nested_contract_call":
728
+ case "var_set":
729
+ case "map_set":
730
+ case "map_insert":
731
+ case "map_delete":
732
+ return {
733
+ ...base,
734
+ id: vmEventId(e.tx_id, e.event_index),
735
+ clock: "vm",
736
+ type: e.event_type,
737
+ data: {
738
+ contract_identifier: e.contract_id,
739
+ ..."payload" in e && e.payload && typeof e.payload === "object" ? e.payload : {},
740
+ sender: "sender" in e ? e.sender : undefined,
741
+ caller: "caller" in e ? e.caller : undefined,
742
+ function_name: "function_name" in e ? e.function_name : undefined,
743
+ function_args: "function_args" in e ? e.function_args : undefined,
744
+ raw_result: "raw_result" in e ? e.raw_result : undefined,
745
+ map_name: "map" in e ? e.map : undefined,
746
+ var_name: "var_name" in e ? e.var_name : undefined,
747
+ raw_key: "raw_key" in e ? e.raw_key : undefined,
748
+ raw_value: "raw_value" in e ? e.raw_value : undefined
749
+ }
750
+ };
624
751
  }
625
752
  }
626
753
 
@@ -667,12 +794,24 @@ var EVENT_FILTER_TO_INDEX_TYPE = {
667
794
  nft_transfer: "nft_transfer",
668
795
  nft_mint: "nft_mint",
669
796
  nft_burn: "nft_burn",
670
- print_event: "print"
797
+ print_event: "print",
798
+ nested_contract_call: "nested_contract_call",
799
+ var_set: "var_set",
800
+ map_set: "map_set",
801
+ map_insert: "map_insert",
802
+ map_delete: "map_delete"
671
803
  };
672
804
  var TX_SOURCE_TYPES = new Set(["contract_call", "contract_deploy"]);
673
- var ALL_INDEX_EVENT_TYPES = [
805
+ var VM_INDEX_EVENT_TYPES = new Set([
806
+ "nested_contract_call",
807
+ "var_set",
808
+ "map_set",
809
+ "map_insert",
810
+ "map_delete"
811
+ ]);
812
+ var CLASSIC_INDEX_EVENT_TYPES = [
674
813
  ...new Set(Object.values(EVENT_FILTER_TO_INDEX_TYPE))
675
- ];
814
+ ].filter((t) => !VM_INDEX_EVENT_TYPES.has(t));
676
815
  function sourceFilters(subgraph) {
677
816
  const sources = subgraph.sources;
678
817
  return Array.isArray(sources) ? sources : Object.values(sources);
@@ -689,10 +828,11 @@ function synthesizeTxsFromEvents(events) {
689
828
  return [...byId.values()];
690
829
  }
691
830
  function indexEventTypesForFilterTypes(filterTypes) {
831
+ const types = new Set;
692
832
  if (filterTypes.some((t) => TX_SOURCE_TYPES.has(t))) {
693
- return ALL_INDEX_EVENT_TYPES;
833
+ for (const t of CLASSIC_INDEX_EVENT_TYPES)
834
+ types.add(t);
694
835
  }
695
- const types = new Set;
696
836
  for (const t of filterTypes) {
697
837
  const indexType = EVENT_FILTER_TO_INDEX_TYPE[t];
698
838
  if (indexType)
@@ -738,6 +878,9 @@ class PublicApiBlockSource {
738
878
  return found.length ? Math.min(...found) : null;
739
879
  }
740
880
  getTip() {
881
+ if (this.eventTypes.some((t) => VM_INDEX_EVENT_TYPES.has(t))) {
882
+ return this.http.getIndexSourceTip();
883
+ }
741
884
  return this.http.getIndexTip();
742
885
  }
743
886
  async loadBlockRange(fromHeight, toHeight) {
@@ -761,12 +904,21 @@ class PublicApiBlockSource {
761
904
  }
762
905
  for (const list of eventLists) {
763
906
  for (const e of list) {
764
- map.get(e.block_height)?.events.push(reconstructEvent(e));
907
+ const bd = map.get(e.block_height);
908
+ if (!bd)
909
+ continue;
910
+ if (VM_INDEX_EVENT_TYPES.has(e.event_type)) {
911
+ bd.vmEvents ??= [];
912
+ bd.vmEvents.push(reconstructEvent(e));
913
+ } else {
914
+ bd.events.push(reconstructEvent(e));
915
+ }
765
916
  }
766
917
  }
767
918
  for (const bd of map.values()) {
768
919
  bd.txs.sort((a, b) => a.tx_index - b.tx_index);
769
920
  bd.events.sort((a, b) => a.event_index - b.event_index);
921
+ bd.vmEvents?.sort((a, b) => a.event_index - b.event_index);
770
922
  }
771
923
  return map;
772
924
  }
@@ -848,7 +1000,7 @@ function decoderNameForEventType(indexEventType) {
848
1000
  return `decode.${indexEventType}.v1`;
849
1001
  }
850
1002
  function decoderNamesForIndexEventTypes(eventTypes) {
851
- return eventTypes.map(decoderNameForEventType);
1003
+ return eventTypes.filter((t) => !VM_INDEX_EVENT_TYPES.has(t)).map(decoderNameForEventType);
852
1004
  }
853
1005
  function decoderNamesForSubgraph(subgraph) {
854
1006
  return decoderNamesForIndexEventTypes(referencedIndexEventTypes(subgraph));
@@ -989,13 +1141,25 @@ async function buildTraitContracts(chainSubs, asOfBlock, opts) {
989
1141
  return resolved;
990
1142
  }
991
1143
  function evaluateBlock(block, sources, traitContracts) {
992
- return matchSources(sources, block.txs, block.events, traitContracts);
1144
+ return matchSources(sources, block.txs, block.events, traitContracts, new Map, block.vmEvents ?? []);
993
1145
  }
994
- function chainDedupKey(webhookId, txId, eventIndex, blockHash, replayId) {
995
- const base = `chain:${webhookId}:${txId}:${eventIndex}:${blockHash}`;
1146
+ function chainDedupKey(webhookId, txId, eventIndex, blockHash, replayId, clock = "classic") {
1147
+ const ordinal = clock === "vm" ? `vm:${eventIndex}` : `${eventIndex}`;
1148
+ const base = `chain:${webhookId}:${txId}:${ordinal}:${blockHash}`;
996
1149
  return replayId ? `replay:${replayId}:${base}` : base;
997
1150
  }
1151
+ var VM_TRIGGER_TYPES = new Set([
1152
+ "nested_contract_call",
1153
+ "var_set",
1154
+ "map_set",
1155
+ "map_insert",
1156
+ "map_delete"
1157
+ ]);
1158
+ function clockFor(triggerType) {
1159
+ return VM_TRIGGER_TYPES.has(triggerType) ? "vm" : "classic";
1160
+ }
998
1161
  function applyRow(meta, blockHeight, blockHash, txId, eventIndex, event, replayId) {
1162
+ const clock = clockFor(meta.triggerType);
999
1163
  const payload = {
1000
1164
  action: "apply",
1001
1165
  block_hash: blockHash,
@@ -1012,10 +1176,10 @@ function applyRow(meta, blockHeight, blockHash, txId, eventIndex, event, replayI
1012
1176
  table_name: null,
1013
1177
  block_height: blockHeight,
1014
1178
  tx_id: txId,
1015
- row_pk: { tx_id: txId, event_index: eventIndex },
1179
+ row_pk: clock === "vm" ? { tx_id: txId, event_index: eventIndex, clock } : { tx_id: txId, event_index: eventIndex },
1016
1180
  event_type: `chain.${meta.triggerType}.apply`,
1017
1181
  payload,
1018
- dedup_key: chainDedupKey(meta.webhookId, txId, eventIndex, blockHash, replayId),
1182
+ dedup_key: chainDedupKey(meta.webhookId, txId, eventIndex, blockHash, replayId, clock),
1019
1183
  ...replayId ? { is_replay: true } : {}
1020
1184
  };
1021
1185
  }
@@ -1313,5 +1477,5 @@ export {
1313
1477
  replayChainWebhook
1314
1478
  };
1315
1479
 
1316
- //# debugId=74CC4063BDD4E6F864756E2164756E21
1480
+ //# debugId=7D5A9CBD8E5E35A364756E2164756E21
1317
1481
  //# sourceMappingURL=replay.js.map