@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
@@ -331,6 +331,23 @@ interface ContractDeployFilter {
331
331
  deployer?: string;
332
332
  contractName?: string;
333
333
  }
334
+ interface NestedContractCallFilter extends TraitScope, FactoryScope {
335
+ type: "nested_contract_call";
336
+ contractId?: string | readonly string[];
337
+ functionName?: string;
338
+ caller?: string;
339
+ sender?: string;
340
+ }
341
+ interface VarSetFilter extends TraitScope, FactoryScope {
342
+ type: "var_set";
343
+ contractId?: string | readonly string[];
344
+ varName?: string;
345
+ }
346
+ interface MapWriteFilter extends TraitScope, FactoryScope {
347
+ type: "map_set" | "map_insert" | "map_delete";
348
+ contractId?: string | readonly string[];
349
+ map?: string;
350
+ }
334
351
  /**
335
352
  * One declared field of a print payload.
336
353
  *
@@ -418,7 +435,7 @@ type UnpinnedPrintEventFilter = PrintEventFields & {
418
435
  */
419
436
  type PrintEventFilter = PinnedPrintEventFilter | TraitPrintEventFilter | UnpinnedPrintEventFilter;
420
437
  /** All subgraph filter types — discriminated on `type` */
421
- type SubgraphFilter = StxTransferFilter | StxMintFilter | StxBurnFilter | StxLockFilter | FtTransferFilter | FtMintFilter | FtBurnFilter | NftTransferFilter | NftMintFilter | NftBurnFilter | ContractCallFilter | ContractDeployFilter | PrintEventFilter;
438
+ type SubgraphFilter = StxTransferFilter | StxMintFilter | StxBurnFilter | StxLockFilter | FtTransferFilter | FtMintFilter | FtBurnFilter | NftTransferFilter | NftMintFilter | NftBurnFilter | ContractCallFilter | ContractDeployFilter | PrintEventFilter | NestedContractCallFilter | VarSetFilter | MapWriteFilter;
422
439
  /** Transaction metadata available in handlers */
423
440
  interface TxMeta {
424
441
  txId: string;
@@ -894,6 +911,42 @@ type ContractCallPayload<F> = F extends {
894
911
  } ? N extends ExtractFunctionNames<A> ? ContractCallEvent & {
895
912
  input: ExtractFunctionArgs2<A, N>
896
913
  } : ContractCallEvent : ContractCallEvent;
914
+ interface NestedContractCallPayload {
915
+ contractId: string;
916
+ sender: string | null;
917
+ caller: string;
918
+ functionName: string;
919
+ /** Decoded `function_args`. */
920
+ args: unknown[];
921
+ /** Original hex `function_args`, preserved. */
922
+ arguments: string[];
923
+ rawResult: string;
924
+ result: unknown;
925
+ tx: TxMeta;
926
+ }
927
+ interface VarSetPayload {
928
+ contractId: string;
929
+ varName: string;
930
+ rawValue: string;
931
+ value: unknown;
932
+ tx: TxMeta;
933
+ }
934
+ interface MapWritePayload {
935
+ contractId: string;
936
+ map: string;
937
+ rawKey: string;
938
+ key: unknown;
939
+ rawValue: string;
940
+ value: unknown;
941
+ tx: TxMeta;
942
+ }
943
+ interface MapDeletePayload {
944
+ contractId: string;
945
+ map: string;
946
+ rawKey: string;
947
+ key: unknown;
948
+ tx: TxMeta;
949
+ }
897
950
  /**
898
951
  * Print event typed per topic. When a `print_event` source declares a `prints`
899
952
  * map, the payload is a discriminated union keyed by `topic` with `data` typed
@@ -955,9 +1008,15 @@ type EventForFilter<F extends SubgraphFilter> = F extends {
955
1008
  type: "contract_call"
956
1009
  } ? ContractCallPayload<F> : F extends {
957
1010
  type: "contract_deploy"
958
- } ? ContractDeployPayload : never;
1011
+ } ? ContractDeployPayload : F extends {
1012
+ type: "nested_contract_call"
1013
+ } ? NestedContractCallPayload : F extends {
1014
+ type: "var_set"
1015
+ } ? VarSetPayload : F extends {
1016
+ type: infer T extends "map_set" | "map_insert" | "map_delete"
1017
+ } ? T extends "map_delete" ? MapDeletePayload : MapWritePayload : never;
959
1018
  /** Union of every event payload — the `"*"` catch-all handler receives this. */
960
- type AnyEvent = FtTransferPayload | FtMintPayload | FtBurnPayload | NftTransferPayload | NftMintPayload | NftBurnPayload | StxTransferPayload | StxMintPayload | StxBurnPayload | StxLockPayload | PrintEventPayload | ContractCallEvent | ContractDeployPayload;
1019
+ type AnyEvent = FtTransferPayload | FtMintPayload | FtBurnPayload | NftTransferPayload | NftMintPayload | NftBurnPayload | StxTransferPayload | StxMintPayload | StxBurnPayload | StxLockPayload | PrintEventPayload | ContractCallEvent | ContractDeployPayload | NestedContractCallPayload | VarSetPayload | MapWritePayload | MapDeletePayload;
961
1020
  type HandlerFn<
962
1021
  F extends SubgraphFilter,
963
1022
  S extends SubgraphSchema
@@ -1268,4 +1327,4 @@ declare function deploySchema(db: AnyDb, def: SubgraphDefinition, handlerPath: s
1268
1327
  /** Non-fatal caveats the caller should surface (e.g. handler grafts at tip). */
1269
1328
  warnings?: string[]
1270
1329
  }>;
1271
- export { validateSubgraphDefinition, validatePrintPayload, sparseProbeTargets, resumeReindex, renderDeployPlan, reindexSubgraph, readContractAt, pgSchemaName, inferPrintTopics, hasBreakingChanges, generateSubgraphSQL, generatePrismaSchema, generateKyselySchema, generateIndexSchema, generateDrizzleSchema, filterFieldsByType, diffSchema, deploySchema, defineSubgraph, defineSchema, canSparseScan, camelizeKeys, camelizeDataKey, backfillSubgraph, WriteRow, WhereInput, UnpinnedPrintEventFilter, TypedSubgraphDefinition, TypedSubgraphContext, TypedHandlers, TxMeta, TraitPrintEventFilter, TableDiff, SystemRow, SubscribeOptions, SubgraphTableClient, SubgraphTable, SubgraphSchema, SubgraphHandler, SubgraphFilter, SubgraphDefinition, SubgraphContext, SubgraphColumn, StxTransferPayload, StxTransferFilter, StxMintPayload, StxMintFilter, StxLockPayload, StxLockFilter, StxBurnPayload, StxBurnFilter, SparseProbeTarget, RowValue, ReindexOptions, PrismaGenOptions, PrintValidationResult, PrintSample, PrintFieldToTS, PrintField, PrintEventPrints, PrintEventPayload, PrintEventFor, PrintEventFilter, PrintDataOf, PinnedPrintEventFilter, NftTransferPayload, NftTransferFilter, NftMintPayload, NftMintFilter, NftBurnPayload, NftBurnFilter, MaterializeSpec, MaterializeColumn, KyselyGenOptions, InferredTopicSchema, InferredPrintField, InferTableRow, InferSubgraphClient, InferContext, InferColumnType, IndexCodegenTarget, IndexCodegenOptions, INDEX_CODEGEN_TABLES, GeneratedSQL, FtTransferPayload, FtTransferFilter, FtMintPayload, FtMintFilter, FtBurnPayload, FtBurnFilter, FindManyOptions, FactoryScope, EventForFilter, ErasedChainReadClient, DrizzleGenOptions, DeployPlan, DeployDiff, ContractDeployPayload, ContractDeployFilter, ContractCallPayload, ContractCallFilter, ContractCallEvent, ComputedValue, ComparisonFilter, ColumnType, ColumnToTS, ColumnDiff, ChainReadOptions, ChainReadMethods, ChainReadError, ChainReadClient, ChainReadCacheMode, AnyEvent, AggregateSpec, AggregateResult };
1330
+ export { validateSubgraphDefinition, validatePrintPayload, sparseProbeTargets, resumeReindex, renderDeployPlan, reindexSubgraph, readContractAt, pgSchemaName, inferPrintTopics, hasBreakingChanges, generateSubgraphSQL, generatePrismaSchema, generateKyselySchema, generateIndexSchema, generateDrizzleSchema, filterFieldsByType, diffSchema, deploySchema, defineSubgraph, defineSchema, canSparseScan, camelizeKeys, camelizeDataKey, backfillSubgraph, WriteRow, WhereInput, VarSetPayload, UnpinnedPrintEventFilter, TypedSubgraphDefinition, TypedSubgraphContext, TypedHandlers, TxMeta, TraitPrintEventFilter, TableDiff, SystemRow, SubscribeOptions, SubgraphTableClient, SubgraphTable, SubgraphSchema, SubgraphHandler, SubgraphFilter, SubgraphDefinition, SubgraphContext, SubgraphColumn, StxTransferPayload, StxTransferFilter, StxMintPayload, StxMintFilter, StxLockPayload, StxLockFilter, StxBurnPayload, StxBurnFilter, SparseProbeTarget, RowValue, ReindexOptions, PrismaGenOptions, PrintValidationResult, PrintSample, PrintFieldToTS, PrintField, PrintEventPrints, PrintEventPayload, PrintEventFor, PrintEventFilter, PrintDataOf, PinnedPrintEventFilter, NftTransferPayload, NftTransferFilter, NftMintPayload, NftMintFilter, NftBurnPayload, NftBurnFilter, NestedContractCallPayload, MaterializeSpec, MaterializeColumn, MapWritePayload, MapDeletePayload, KyselyGenOptions, InferredTopicSchema, InferredPrintField, InferTableRow, InferSubgraphClient, InferContext, InferColumnType, IndexCodegenTarget, IndexCodegenOptions, INDEX_CODEGEN_TABLES, GeneratedSQL, FtTransferPayload, FtTransferFilter, FtMintPayload, FtMintFilter, FtBurnPayload, FtBurnFilter, FindManyOptions, FactoryScope, EventForFilter, ErasedChainReadClient, DrizzleGenOptions, DeployPlan, DeployDiff, ContractDeployPayload, ContractDeployFilter, ContractCallPayload, ContractCallFilter, ContractCallEvent, ComputedValue, ComparisonFilter, ColumnType, ColumnToTS, ColumnDiff, ChainReadOptions, ChainReadMethods, ChainReadError, ChainReadClient, ChainReadCacheMode, AnyEvent, AggregateSpec, AggregateResult };
package/dist/src/index.js CHANGED
@@ -84,7 +84,12 @@ var VALID_FILTER_TYPES = [
84
84
  "nft_burn",
85
85
  "contract_call",
86
86
  "contract_deploy",
87
- "print_event"
87
+ "print_event",
88
+ "nested_contract_call",
89
+ "var_set",
90
+ "map_set",
91
+ "map_insert",
92
+ "map_delete"
88
93
  ];
89
94
  var contractIdField = z.union([
90
95
  z.string(),
@@ -192,6 +197,43 @@ var SubgraphFilterUnion = z.discriminatedUnion("type", [
192
197
  materialize: MaterializeSpecSchema.optional(),
193
198
  ...traitScope,
194
199
  ...factoryScope
200
+ }).strict(),
201
+ z.object({
202
+ type: z.literal("nested_contract_call"),
203
+ contractId: contractIdField.optional(),
204
+ functionName: z.string().optional(),
205
+ caller: z.string().optional(),
206
+ sender: z.string().optional(),
207
+ ...traitScope,
208
+ ...factoryScope
209
+ }).strict(),
210
+ z.object({
211
+ type: z.literal("var_set"),
212
+ contractId: contractIdField.optional(),
213
+ varName: z.string().optional(),
214
+ ...traitScope,
215
+ ...factoryScope
216
+ }).strict(),
217
+ z.object({
218
+ type: z.literal("map_set"),
219
+ contractId: contractIdField.optional(),
220
+ map: z.string().optional(),
221
+ ...traitScope,
222
+ ...factoryScope
223
+ }).strict(),
224
+ z.object({
225
+ type: z.literal("map_insert"),
226
+ contractId: contractIdField.optional(),
227
+ map: z.string().optional(),
228
+ ...traitScope,
229
+ ...factoryScope
230
+ }).strict(),
231
+ z.object({
232
+ type: z.literal("map_delete"),
233
+ contractId: contractIdField.optional(),
234
+ map: z.string().optional(),
235
+ ...traitScope,
236
+ ...factoryScope
195
237
  }).strict()
196
238
  ]);
197
239
  var SubgraphFilterSchema = SubgraphFilterUnion;
@@ -370,6 +412,24 @@ function safeIndexName(name) {
370
412
  const hash = createHash("sha256").update(name).digest("hex").slice(0, 8);
371
413
  return `${name.slice(0, MAX_IDENT_BYTES - hash.length - 1)}_${hash}`;
372
414
  }
415
+ function emitIndexedColumnIndexDDL(schemaName, tableName, colName, opts) {
416
+ const concurrently = opts?.concurrently ? " CONCURRENTLY" : "";
417
+ const qualified = `${quotePgIdent(schemaName)}.${quotePgIdent(tableName)}`;
418
+ const plain = `idx_${schemaName}_${tableName}_${colName}`;
419
+ const composite = safeIndexName(`idx_${schemaName}_${tableName}_${colName}_id`);
420
+ const col = quotePgIdent(colName);
421
+ const id = quotePgIdent("_id");
422
+ return {
423
+ create: [
424
+ `CREATE INDEX${concurrently} IF NOT EXISTS ${quotePgIdent(plain)} ON ${qualified} (${col})`,
425
+ `CREATE INDEX${concurrently} IF NOT EXISTS ${quotePgIdent(composite)} ON ${qualified} (${col}, ${id})`
426
+ ],
427
+ drop: [
428
+ `DROP INDEX${concurrently} IF EXISTS ${quotePgIdent(schemaName)}.${quotePgIdent(plain)}`,
429
+ `DROP INDEX${concurrently} IF EXISTS ${quotePgIdent(schemaName)}.${quotePgIdent(composite)}`
430
+ ]
431
+ };
432
+ }
373
433
  function escapeLiteralDefault(value) {
374
434
  if (value === null || value === undefined)
375
435
  return "NULL";
@@ -411,8 +471,7 @@ function emitTableDDL(schemaName, tableName, tableDef) {
411
471
  statements.push(`CREATE INDEX IF NOT EXISTS ${quotePgIdent(`idx_${schemaName}_${tableName}_tx_id`)} ON ${qualifiedName} (_tx_id)`);
412
472
  for (const [colName, col] of Object.entries(tableDef.columns)) {
413
473
  if (col.indexed) {
414
- statements.push(`CREATE INDEX IF NOT EXISTS ${quotePgIdent(`idx_${schemaName}_${tableName}_${colName}`)} ON ${qualifiedName} (${quotePgIdent(colName)})`);
415
- statements.push(`CREATE INDEX IF NOT EXISTS ${quotePgIdent(safeIndexName(`idx_${schemaName}_${tableName}_${colName}_id`))} ON ${qualifiedName} (${quotePgIdent(colName)}, ${quotePgIdent("_id")})`);
474
+ statements.push(...emitIndexedColumnIndexDDL(schemaName, tableName, colName).create);
416
475
  }
417
476
  }
418
477
  for (const [colName, col] of Object.entries(tableDef.columns)) {
@@ -1321,7 +1380,7 @@ function printContractId(data) {
1321
1380
  const id = (typeof data.contract_identifier === "string" ? data.contract_identifier : undefined) ?? (typeof data.contract_id === "string" ? data.contract_id : undefined);
1322
1381
  return id || undefined;
1323
1382
  }
1324
- function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts = new Map) {
1383
+ function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts = new Map, vmEventsByTx = new Map) {
1325
1384
  const results = [];
1326
1385
  switch (filter.type) {
1327
1386
  case "stx_transfer":
@@ -1486,6 +1545,57 @@ function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryCo
1486
1545
  }
1487
1546
  break;
1488
1547
  }
1548
+ case "nested_contract_call":
1549
+ case "var_set":
1550
+ case "map_set":
1551
+ case "map_insert":
1552
+ case "map_delete": {
1553
+ for (const tx of transactions) {
1554
+ const txEvents = vmEventsByTx.get(tx.tx_id) ?? [];
1555
+ const matched = txEvents.filter((e) => {
1556
+ if (e.type !== filter.type)
1557
+ return false;
1558
+ const data = e.data;
1559
+ if (!data)
1560
+ return false;
1561
+ const contractId = printContractId(data);
1562
+ if ("contractId" in filter && filter.contractId) {
1563
+ if (!contractId || !matchAny(contractId, filter.contractId))
1564
+ return false;
1565
+ }
1566
+ if (!traitAllows(filter, contractId, traitContracts))
1567
+ return false;
1568
+ if (!factoryAllows(filter, contractId, factoryContracts))
1569
+ return false;
1570
+ if (filter.type === "nested_contract_call") {
1571
+ if (filter.functionName) {
1572
+ if (typeof data.function_name !== "string" || !matchPattern(data.function_name, filter.functionName))
1573
+ return false;
1574
+ }
1575
+ if (filter.caller) {
1576
+ if (typeof data.caller !== "string" || !matchPattern(data.caller, filter.caller))
1577
+ return false;
1578
+ }
1579
+ if (filter.sender) {
1580
+ if (typeof data.sender !== "string" || !matchPattern(data.sender, filter.sender))
1581
+ return false;
1582
+ }
1583
+ }
1584
+ if (filter.type === "var_set" && filter.varName) {
1585
+ if (typeof data.var_name !== "string" || !matchPattern(data.var_name, filter.varName))
1586
+ return false;
1587
+ }
1588
+ if ((filter.type === "map_set" || filter.type === "map_insert" || filter.type === "map_delete") && filter.map) {
1589
+ if (typeof data.map_name !== "string" || !matchPattern(data.map_name, filter.map))
1590
+ return false;
1591
+ }
1592
+ return true;
1593
+ });
1594
+ if (matched.length > 0)
1595
+ results.push({ tx, events: matched });
1596
+ }
1597
+ break;
1598
+ }
1489
1599
  case "print_event": {
1490
1600
  for (const tx of transactions) {
1491
1601
  const txEvents = eventsByTx.get(tx.tx_id) ?? [];
@@ -1517,17 +1627,23 @@ function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryCo
1517
1627
  }
1518
1628
  return results;
1519
1629
  }
1520
- function matchSources(sources, transactions, events, traitContracts = new Map, factoryContracts = new Map) {
1630
+ function matchSources(sources, transactions, events, traitContracts = new Map, factoryContracts = new Map, vmEvents = []) {
1521
1631
  const eventsByTx = new Map;
1522
1632
  for (const event of events) {
1523
1633
  const list = eventsByTx.get(event.tx_id) ?? [];
1524
1634
  list.push(event);
1525
1635
  eventsByTx.set(event.tx_id, list);
1526
1636
  }
1637
+ const vmEventsByTx = new Map;
1638
+ for (const event of vmEvents) {
1639
+ const list = vmEventsByTx.get(event.tx_id) ?? [];
1640
+ list.push(event);
1641
+ vmEventsByTx.set(event.tx_id, list);
1642
+ }
1527
1643
  const seen = new Set;
1528
1644
  const results = [];
1529
1645
  for (const [sourceName, filter] of Object.entries(sources)) {
1530
- const matches = matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts);
1646
+ const matches = matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts, vmEventsByTx);
1531
1647
  for (const match of matches) {
1532
1648
  const dedupeKey = `${match.tx.tx_id}:${sourceName}`;
1533
1649
  if (!seen.has(dedupeKey)) {
@@ -2102,6 +2218,12 @@ function decodeRawResult(raw) {
2102
2218
  }
2103
2219
  return null;
2104
2220
  }
2221
+ function decodeRawHex(hex) {
2222
+ if (hex.startsWith("0x") && hex.length > 2) {
2223
+ return decodeClarityValue(hex);
2224
+ }
2225
+ return hex;
2226
+ }
2105
2227
  function safeBigInt(val) {
2106
2228
  if (typeof val === "bigint")
2107
2229
  return val;
@@ -2295,6 +2417,58 @@ function buildEventPayload(filter, tx, event) {
2295
2417
  deployer: tx.sender,
2296
2418
  tx: txMeta
2297
2419
  };
2420
+ case "nested_contract_call": {
2421
+ const data = event.data ?? {};
2422
+ const argsHex = Array.isArray(data.function_args) ? data.function_args.filter((a) => typeof a === "string") : [];
2423
+ return {
2424
+ contractId: printContractId(data) ?? "",
2425
+ sender: typeof data.sender === "string" ? data.sender : null,
2426
+ caller: typeof data.caller === "string" ? data.caller : "",
2427
+ functionName: typeof data.function_name === "string" ? data.function_name : "",
2428
+ args: decodeFunctionArgs2(argsHex),
2429
+ arguments: argsHex,
2430
+ rawResult: typeof data.raw_result === "string" ? data.raw_result : "",
2431
+ result: decodeRawResult(data.raw_result),
2432
+ tx: txMeta
2433
+ };
2434
+ }
2435
+ case "var_set": {
2436
+ const data = event.data ?? {};
2437
+ const rawValue = typeof data.raw_value === "string" ? data.raw_value : "";
2438
+ return {
2439
+ contractId: printContractId(data) ?? "",
2440
+ varName: typeof data.var_name === "string" ? data.var_name : "",
2441
+ rawValue,
2442
+ value: decodeRawHex(rawValue),
2443
+ tx: txMeta
2444
+ };
2445
+ }
2446
+ case "map_set":
2447
+ case "map_insert": {
2448
+ const data = event.data ?? {};
2449
+ const rawKey = typeof data.raw_key === "string" ? data.raw_key : "";
2450
+ const rawValue = typeof data.raw_value === "string" ? data.raw_value : "";
2451
+ return {
2452
+ contractId: printContractId(data) ?? "",
2453
+ map: typeof data.map_name === "string" ? data.map_name : "",
2454
+ rawKey,
2455
+ key: decodeRawHex(rawKey),
2456
+ rawValue,
2457
+ value: decodeRawHex(rawValue),
2458
+ tx: txMeta
2459
+ };
2460
+ }
2461
+ case "map_delete": {
2462
+ const data = event.data ?? {};
2463
+ const rawKey = typeof data.raw_key === "string" ? data.raw_key : "";
2464
+ return {
2465
+ contractId: printContractId(data) ?? "",
2466
+ map: typeof data.map_name === "string" ? data.map_name : "",
2467
+ rawKey,
2468
+ key: decodeRawHex(rawKey),
2469
+ tx: txMeta
2470
+ };
2471
+ }
2298
2472
  default:
2299
2473
  return {
2300
2474
  ...decoded,
@@ -2323,7 +2497,8 @@ async function runHandlers(subgraph, matched, ctx, opts) {
2323
2497
  units.push({ tx, sourceName, event });
2324
2498
  }
2325
2499
  }
2326
- units.sort((a, b) => (a.tx.tx_index ?? 0) - (b.tx.tx_index ?? 0) || (a.event?.event_index ?? -1) - (b.event?.event_index ?? -1));
2500
+ const clockRank = (e) => e?.clock === "vm" ? 1 : 0;
2501
+ 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));
2327
2502
  for (const { tx, event, sourceName } of units) {
2328
2503
  if (errors >= threshold) {
2329
2504
  logger4.error("Subgraph error threshold reached, skipping remaining events", {
@@ -2469,11 +2644,15 @@ import {
2469
2644
  import { logger as logger5 } from "@secondlayer/shared/logger";
2470
2645
 
2471
2646
  // src/runtime/batch-loader.ts
2647
+ function vmEventId(txId, vmEventIndex) {
2648
+ return `${txId}#vm:${vmEventIndex}`;
2649
+ }
2472
2650
  async function loadBlockRange(db, fromHeight, toHeight) {
2473
- const [blocks, txs, events] = await Promise.all([
2651
+ const [blocks, txs, events, vmRows] = await Promise.all([
2474
2652
  db.selectFrom("blocks").selectAll().where("height", ">=", fromHeight).where("height", "<=", toHeight).where("canonical", "=", true).execute(),
2475
2653
  db.selectFrom("transactions").selectAll().where("block_height", ">=", fromHeight).where("block_height", "<=", toHeight).execute(),
2476
- db.selectFrom("events").selectAll().where("block_height", ">=", fromHeight).where("block_height", "<=", toHeight).execute()
2654
+ db.selectFrom("events").selectAll().where("block_height", ">=", fromHeight).where("block_height", "<=", toHeight).execute(),
2655
+ db.selectFrom("vm_events").selectAll().where("block_height", ">=", fromHeight).where("block_height", "<=", toHeight).orderBy("ordinal", "asc").execute()
2477
2656
  ]);
2478
2657
  const txsByHeight = new Map;
2479
2658
  for (const tx of txs) {
@@ -2489,13 +2668,30 @@ async function loadBlockRange(db, fromHeight, toHeight) {
2489
2668
  list.push(evt);
2490
2669
  eventsByHeight.set(h, list);
2491
2670
  }
2671
+ const vmByHeight = new Map;
2672
+ for (const row of vmRows) {
2673
+ const h = Number(row.block_height);
2674
+ const list = vmByHeight.get(h) ?? [];
2675
+ list.push({
2676
+ id: vmEventId(row.tx_id, Number(row.ordinal)),
2677
+ tx_id: row.tx_id,
2678
+ block_height: h,
2679
+ event_index: Number(row.ordinal),
2680
+ type: row.type,
2681
+ data: row.data,
2682
+ created_at: row.created_at,
2683
+ clock: "vm"
2684
+ });
2685
+ vmByHeight.set(h, list);
2686
+ }
2492
2687
  const result = new Map;
2493
2688
  for (const block of blocks) {
2494
2689
  const h = Number(block.height);
2495
2690
  result.set(h, {
2496
2691
  block,
2497
2692
  txs: txsByHeight.get(h) ?? [],
2498
- events: eventsByHeight.get(h) ?? []
2693
+ events: eventsByHeight.get(h) ?? [],
2694
+ vmEvents: vmByHeight.get(h) ?? []
2499
2695
  });
2500
2696
  }
2501
2697
  return result;
@@ -2511,6 +2707,9 @@ function avgEventsPerBlock(batch) {
2511
2707
  }
2512
2708
 
2513
2709
  // src/runtime/observer-http-source.ts
2710
+ import {
2711
+ VM_NODE_TO_STORED_TYPE
2712
+ } from "@secondlayer/shared";
2514
2713
  var OBSERVER_HTTP_EXPORT_PATH = "/internal/observer-events";
2515
2714
  function stripTrailingSlash(url) {
2516
2715
  return url.replace(/\/+$/, "");
@@ -2588,7 +2787,29 @@ function mapNewBlockPayloadToBlockData(payload) {
2588
2787
  created_at: new Date(0)
2589
2788
  };
2590
2789
  });
2591
- return { block, txs, events };
2790
+ const vmEvents = [];
2791
+ for (const [i, trace] of (p.vm_events ?? []).entries()) {
2792
+ const nodeType = trace.type;
2793
+ if (typeof nodeType !== "string" || !(nodeType in VM_NODE_TO_STORED_TYPE))
2794
+ continue;
2795
+ const stored = VM_NODE_TO_STORED_TYPE[nodeType];
2796
+ const body = trace[nodeType];
2797
+ if (!body || typeof body !== "object")
2798
+ continue;
2799
+ const txId = trace.txid ?? "";
2800
+ const vmIndex = i;
2801
+ vmEvents.push({
2802
+ id: vmEventId(txId, vmIndex),
2803
+ tx_id: txId,
2804
+ block_height: height,
2805
+ event_index: vmIndex,
2806
+ type: stored,
2807
+ data: body,
2808
+ created_at: new Date(0),
2809
+ clock: "vm"
2810
+ });
2811
+ }
2812
+ return { block, txs, events, vmEvents };
2592
2813
  }
2593
2814
 
2594
2815
  class ObserverHttpBlockSource {
@@ -2791,6 +3012,30 @@ function reconstructEvent(e) {
2791
3012
  raw_value: e.payload.raw_value
2792
3013
  }
2793
3014
  };
3015
+ case "nested_contract_call":
3016
+ case "var_set":
3017
+ case "map_set":
3018
+ case "map_insert":
3019
+ case "map_delete":
3020
+ return {
3021
+ ...base,
3022
+ id: vmEventId(e.tx_id, e.event_index),
3023
+ clock: "vm",
3024
+ type: e.event_type,
3025
+ data: {
3026
+ contract_identifier: e.contract_id,
3027
+ ..."payload" in e && e.payload && typeof e.payload === "object" ? e.payload : {},
3028
+ sender: "sender" in e ? e.sender : undefined,
3029
+ caller: "caller" in e ? e.caller : undefined,
3030
+ function_name: "function_name" in e ? e.function_name : undefined,
3031
+ function_args: "function_args" in e ? e.function_args : undefined,
3032
+ raw_result: "raw_result" in e ? e.raw_result : undefined,
3033
+ map_name: "map" in e ? e.map : undefined,
3034
+ var_name: "var_name" in e ? e.var_name : undefined,
3035
+ raw_key: "raw_key" in e ? e.raw_key : undefined,
3036
+ raw_value: "raw_value" in e ? e.raw_value : undefined
3037
+ }
3038
+ };
2794
3039
  }
2795
3040
  }
2796
3041
 
@@ -2837,12 +3082,24 @@ var EVENT_FILTER_TO_INDEX_TYPE = {
2837
3082
  nft_transfer: "nft_transfer",
2838
3083
  nft_mint: "nft_mint",
2839
3084
  nft_burn: "nft_burn",
2840
- print_event: "print"
3085
+ print_event: "print",
3086
+ nested_contract_call: "nested_contract_call",
3087
+ var_set: "var_set",
3088
+ map_set: "map_set",
3089
+ map_insert: "map_insert",
3090
+ map_delete: "map_delete"
2841
3091
  };
2842
3092
  var TX_SOURCE_TYPES = new Set(["contract_call", "contract_deploy"]);
2843
- var ALL_INDEX_EVENT_TYPES = [
3093
+ var VM_INDEX_EVENT_TYPES = new Set([
3094
+ "nested_contract_call",
3095
+ "var_set",
3096
+ "map_set",
3097
+ "map_insert",
3098
+ "map_delete"
3099
+ ]);
3100
+ var CLASSIC_INDEX_EVENT_TYPES = [
2844
3101
  ...new Set(Object.values(EVENT_FILTER_TO_INDEX_TYPE))
2845
- ];
3102
+ ].filter((t) => !VM_INDEX_EVENT_TYPES.has(t));
2846
3103
  function sourceFilters(subgraph) {
2847
3104
  const sources = subgraph.sources;
2848
3105
  return Array.isArray(sources) ? sources : Object.values(sources);
@@ -2859,10 +3116,11 @@ function synthesizeTxsFromEvents(events) {
2859
3116
  return [...byId.values()];
2860
3117
  }
2861
3118
  function indexEventTypesForFilterTypes(filterTypes) {
3119
+ const types = new Set;
2862
3120
  if (filterTypes.some((t) => TX_SOURCE_TYPES.has(t))) {
2863
- return ALL_INDEX_EVENT_TYPES;
3121
+ for (const t of CLASSIC_INDEX_EVENT_TYPES)
3122
+ types.add(t);
2864
3123
  }
2865
- const types = new Set;
2866
3124
  for (const t of filterTypes) {
2867
3125
  const indexType = EVENT_FILTER_TO_INDEX_TYPE[t];
2868
3126
  if (indexType)
@@ -2908,6 +3166,9 @@ class PublicApiBlockSource {
2908
3166
  return found.length ? Math.min(...found) : null;
2909
3167
  }
2910
3168
  getTip() {
3169
+ if (this.eventTypes.some((t) => VM_INDEX_EVENT_TYPES.has(t))) {
3170
+ return this.http.getIndexSourceTip();
3171
+ }
2911
3172
  return this.http.getIndexTip();
2912
3173
  }
2913
3174
  async loadBlockRange(fromHeight, toHeight) {
@@ -2931,12 +3192,21 @@ class PublicApiBlockSource {
2931
3192
  }
2932
3193
  for (const list of eventLists) {
2933
3194
  for (const e of list) {
2934
- map.get(e.block_height)?.events.push(reconstructEvent(e));
3195
+ const bd = map.get(e.block_height);
3196
+ if (!bd)
3197
+ continue;
3198
+ if (VM_INDEX_EVENT_TYPES.has(e.event_type)) {
3199
+ bd.vmEvents ??= [];
3200
+ bd.vmEvents.push(reconstructEvent(e));
3201
+ } else {
3202
+ bd.events.push(reconstructEvent(e));
3203
+ }
2935
3204
  }
2936
3205
  }
2937
3206
  for (const bd of map.values()) {
2938
3207
  bd.txs.sort((a, b) => a.tx_index - b.tx_index);
2939
3208
  bd.events.sort((a, b) => a.event_index - b.event_index);
3209
+ bd.vmEvents?.sort((a, b) => a.event_index - b.event_index);
2940
3210
  }
2941
3211
  return map;
2942
3212
  }
@@ -3574,7 +3844,7 @@ function invalidateSubgraphRoute(subgraphName) {
3574
3844
  routeCache.delete(subgraphName);
3575
3845
  evictSandboxWorker(subgraphName);
3576
3846
  }
3577
- async function resolveFactoryContracts(subgraph, blockHeight, schemaName, db, txs, evts) {
3847
+ async function resolveFactoryContracts(subgraph, blockHeight, schemaName, db, txs, evts, vmEvts = []) {
3578
3848
  const factories = new Map;
3579
3849
  for (const source of Object.values(subgraph.sources)) {
3580
3850
  const factory = source.factory;
@@ -3597,7 +3867,7 @@ async function resolveFactoryContracts(subgraph, blockHeight, schemaName, db, tx
3597
3867
  } catch {}
3598
3868
  const discovering = subgraph.sources[factory.from];
3599
3869
  if (discovering) {
3600
- const matches = matchSources({ [factory.from]: discovering }, txs, evts, new Map, new Map);
3870
+ const matches = matchSources({ [factory.from]: discovering }, txs, evts, new Map, new Map, vmEvts);
3601
3871
  for (const match of matches) {
3602
3872
  for (const event of match.events ?? []) {
3603
3873
  const payload = buildEventPayload(discovering, match.tx, event);
@@ -3703,10 +3973,12 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
3703
3973
  let block;
3704
3974
  let txs;
3705
3975
  let evts;
3976
+ let vmEvts;
3706
3977
  if (opts?.preloaded) {
3707
3978
  block = opts.preloaded.block;
3708
3979
  txs = opts.preloaded.txs;
3709
3980
  evts = opts.preloaded.events;
3981
+ vmEvts = opts.preloaded.vmEvents ?? [];
3710
3982
  } else {
3711
3983
  const data = (await resolveBlockSource(subgraph).loadBlockRange(blockHeight, blockHeight)).get(blockHeight);
3712
3984
  if (!data) {
@@ -3720,12 +3992,13 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
3720
3992
  block = data.block;
3721
3993
  txs = data.txs;
3722
3994
  evts = data.events;
3995
+ vmEvts = data.vmEvents ?? [];
3723
3996
  }
3724
3997
  const route = await resolveRoute(subgraphName, targetDb);
3725
3998
  const schemaName = route.schemaName;
3726
3999
  const traitContracts = await resolveTraitContracts(subgraph, blockHeight, targetDb);
3727
- const { resolved: factoryContracts, discovered } = await resolveFactoryContracts(subgraph, blockHeight, schemaName, targetDb, txs, evts);
3728
- const matched = matchSources(subgraph.sources, txs, evts, traitContracts, factoryContracts);
4000
+ const { resolved: factoryContracts, discovered } = await resolveFactoryContracts(subgraph, blockHeight, schemaName, targetDb, txs, evts, vmEvts);
4001
+ const matched = matchSources(subgraph.sources, txs, evts, traitContracts, factoryContracts, vmEvts);
3729
4002
  result.matched = matched.length;
3730
4003
  if (matched.length === 0) {
3731
4004
  if (!opts?.skipProgressUpdate) {
@@ -3997,7 +4270,7 @@ function decoderNameForEventType(indexEventType) {
3997
4270
  return `decode.${indexEventType}.v1`;
3998
4271
  }
3999
4272
  function decoderNamesForIndexEventTypes(eventTypes) {
4000
- return eventTypes.map(decoderNameForEventType);
4273
+ return eventTypes.filter((t) => !VM_INDEX_EVENT_TYPES.has(t)).map(decoderNameForEventType);
4001
4274
  }
4002
4275
  function decoderNamesForSubgraph(subgraph) {
4003
4276
  return decoderNamesForIndexEventTypes(referencedIndexEventTypes(subgraph));
@@ -4527,7 +4800,7 @@ async function processBlockRange(def, opts) {
4527
4800
  }
4528
4801
  let result;
4529
4802
  try {
4530
- const mayWrite = blockData.txs.length > 0 || blockData.events.length > 0;
4803
+ const mayWrite = blockData.txs.length > 0 || blockData.events.length > 0 || (blockData.vmEvents?.length ?? 0) > 0;
4531
4804
  const runBlock = () => processBlockWithRetry(def, subgraphName, height, {
4532
4805
  skipProgressUpdate: true,
4533
4806
  atomicProgress,
@@ -5657,7 +5930,9 @@ async function deploySchema(db, def, handlerPath, opts) {
5657
5930
  const nullable = col.nullable ? "" : ` NOT NULL DEFAULT ${getDefault(col.type)}`;
5658
5931
  await sql6.raw(`ALTER TABLE ${qualifiedName} ADD COLUMN ${colName} ${sqlType}${nullable}`).execute(ddlDb);
5659
5932
  if (col.indexed) {
5660
- await sql6.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName} ON ${qualifiedName} (${colName})`).execute(ddlDb);
5933
+ for (const stmt of emitIndexedColumnIndexDDL(schemaName, tableName, colName).create) {
5934
+ await sql6.raw(stmt).execute(ddlDb);
5935
+ }
5661
5936
  }
5662
5937
  if (col.search) {
5663
5938
  await sql6.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName}_trgm ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`).execute(ddlDb);
@@ -5680,12 +5955,16 @@ async function deploySchema(db, def, handlerPath, opts) {
5680
5955
  const now = def.schema[tableName]?.columns[colName];
5681
5956
  if (!now)
5682
5957
  continue;
5683
- const plainIdx = `idx_${schemaName}_${tableName}_${colName}`;
5684
5958
  const trgmIdx = `idx_${schemaName}_${tableName}_${colName}_trgm`;
5959
+ const indexedIdx = emitIndexedColumnIndexDDL(schemaName, tableName, colName, { concurrently: true });
5685
5960
  if (now.indexed && !was?.indexed) {
5686
- await sql6.raw(`CREATE INDEX CONCURRENTLY IF NOT EXISTS ${plainIdx} ON ${qualifiedName} (${colName})`).execute(ddlDb);
5961
+ for (const stmt of indexedIdx.create) {
5962
+ await sql6.raw(stmt).execute(ddlDb);
5963
+ }
5687
5964
  } else if (!now.indexed && was?.indexed) {
5688
- await sql6.raw(`DROP INDEX CONCURRENTLY IF EXISTS ${schemaName}.${plainIdx}`).execute(ddlDb);
5965
+ for (const stmt of indexedIdx.drop) {
5966
+ await sql6.raw(stmt).execute(ddlDb);
5967
+ }
5689
5968
  }
5690
5969
  if (now.search && !was?.search) {
5691
5970
  await sql6.raw(`CREATE INDEX CONCURRENTLY IF NOT EXISTS ${trgmIdx} ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`).execute(ddlDb);
@@ -5798,5 +6077,5 @@ export {
5798
6077
  ChainReadError
5799
6078
  };
5800
6079
 
5801
- //# debugId=ADB058A57FECD09264756E2164756E21
6080
+ //# debugId=60A8B6033C8DF75764756E2164756E21
5802
6081
  //# sourceMappingURL=index.js.map