@secondlayer/subgraphs 3.23.0 → 3.23.2

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.
@@ -1,4 +1,5 @@
1
1
  import { AbiContract as AbiContract2 } from "@secondlayer/stacks/clarity";
2
+ import { AssetIdentifier } from "@secondlayer/stacks/filters";
2
3
  /**
3
4
  * How long a read stays valid.
4
5
  *
@@ -147,38 +148,38 @@ interface FactoryScope {
147
148
  /** FT event filters */
148
149
  interface FtTransferFilter extends TraitScope {
149
150
  type: "ft_transfer";
150
- assetIdentifier?: string;
151
+ assetIdentifier?: AssetIdentifier;
151
152
  sender?: string;
152
153
  recipient?: string;
153
154
  minAmount?: bigint;
154
155
  }
155
156
  interface FtMintFilter extends TraitScope {
156
157
  type: "ft_mint";
157
- assetIdentifier?: string;
158
+ assetIdentifier?: AssetIdentifier;
158
159
  recipient?: string;
159
160
  minAmount?: bigint;
160
161
  }
161
162
  interface FtBurnFilter extends TraitScope {
162
163
  type: "ft_burn";
163
- assetIdentifier?: string;
164
+ assetIdentifier?: AssetIdentifier;
164
165
  sender?: string;
165
166
  minAmount?: bigint;
166
167
  }
167
168
  /** NFT event filters */
168
169
  interface NftTransferFilter extends TraitScope {
169
170
  type: "nft_transfer";
170
- assetIdentifier?: string;
171
+ assetIdentifier?: AssetIdentifier;
171
172
  sender?: string;
172
173
  recipient?: string;
173
174
  }
174
175
  interface NftMintFilter extends TraitScope {
175
176
  type: "nft_mint";
176
- assetIdentifier?: string;
177
+ assetIdentifier?: AssetIdentifier;
177
178
  recipient?: string;
178
179
  }
179
180
  interface NftBurnFilter extends TraitScope {
180
181
  type: "nft_burn";
181
- assetIdentifier?: string;
182
+ assetIdentifier?: AssetIdentifier;
182
183
  sender?: string;
183
184
  }
184
185
  /** Contract event filters */
@@ -235,7 +236,10 @@ type PrintField = ColumnType | {
235
236
  interface PrintEventFilter extends TraitScope, FactoryScope {
236
237
  type: "print_event";
237
238
  /** One contract id, or a set of them (max 20). Supports `*` wildcards.
238
- * Mutually exclusive with `trait`. */
239
+ * COMPOSES with `trait` — the matcher ANDs them, so the pair means
240
+ * "contracts conforming to this trait, narrowed to these ids". (The Index
241
+ * read API refuses the combination, but that is its query planner's
242
+ * constraint, not a semantic one.) */
239
243
  contractId?: string | readonly string[];
240
244
  topic?: string;
241
245
  /**
@@ -2,7 +2,7 @@ import { createRequire } from "node:module";
2
2
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
3
 
4
4
  // src/runtime/context.ts
5
- import { logger } from "@secondlayer/shared/logger";
5
+ import { logger as logger2 } from "@secondlayer/shared/logger";
6
6
  import { formatUnits } from "@secondlayer/stacks/utils";
7
7
  import { sql } from "kysely";
8
8
 
@@ -147,6 +147,7 @@ function generateSubgraphSQL(def, schemaNameOverride) {
147
147
 
148
148
  // src/runtime/chain-read.ts
149
149
  import { getSourceDb } from "@secondlayer/shared/db";
150
+ import { logger } from "@secondlayer/shared/logger";
150
151
  import { http, createPublicClient } from "@secondlayer/stacks";
151
152
  import {
152
153
  ContractResponseError,
@@ -232,6 +233,14 @@ function createChainReadClient(ctx) {
232
233
  const pinnedTo = constant ? null : ctx.indexBlockHash;
233
234
  const cached = await db.selectFrom("chain_read_cache").select("result_hex").where("contract_id", "=", contractId).where("function_name", "=", fn.name).where("args_hash", "=", argsHash).where((eb) => pinnedTo === null ? eb("index_block_hash", "is", null) : eb("index_block_hash", "=", pinnedTo)).executeTakeFirst();
234
235
  const resultHex = cached?.result_hex ?? await gate.run(async () => {
236
+ if (!ctx.indexBlockHash) {
237
+ logger.warn("Chain read is not pinned to a block (no index_block_hash)", {
238
+ contractId,
239
+ functionName: fn.name,
240
+ blockHeight: ctx.blockHeight,
241
+ note: 'cache: "contract-constant" asserts this value cannot change; the node answered at its own tip'
242
+ });
243
+ }
235
244
  const value2 = await readContract(nodeClient(rpcUrl), {
236
245
  contract: contractId,
237
246
  functionName: fn.name,
@@ -346,7 +355,7 @@ class SubgraphContext {
346
355
  data: { ...key, ...row, ...meta, _upsert_keys: keyColumns }
347
356
  });
348
357
  } else {
349
- logger.warn("upsert called without matching uniqueKeys constraint, using fallback", {
358
+ logger2.warn("upsert called without matching uniqueKeys constraint, using fallback", {
350
359
  table,
351
360
  keys: keyColumns
352
361
  });
@@ -906,7 +915,7 @@ function decodeFunctionArgs(args) {
906
915
 
907
916
  // src/runtime/runner.ts
908
917
  import { getErrorMessage } from "@secondlayer/shared";
909
- import { logger as logger2 } from "@secondlayer/shared/logger";
918
+ import { logger as logger3 } from "@secondlayer/shared/logger";
910
919
  import {
911
920
  clarityValueToJS,
912
921
  deserializeCV as deserializeCV3,
@@ -1257,7 +1266,7 @@ async function runHandlers(subgraph, matched, ctx, opts) {
1257
1266
  units.sort((a, b) => (a.tx.tx_index ?? 0) - (b.tx.tx_index ?? 0) || (a.event?.event_index ?? -1) - (b.event?.event_index ?? -1));
1258
1267
  for (const { tx, event, sourceName } of units) {
1259
1268
  if (errors >= threshold) {
1260
- logger2.error("Subgraph error threshold reached, skipping remaining events", {
1269
+ logger3.error("Subgraph error threshold reached, skipping remaining events", {
1261
1270
  subgraph: subgraph.name,
1262
1271
  errors,
1263
1272
  threshold
@@ -1266,7 +1275,7 @@ async function runHandlers(subgraph, matched, ctx, opts) {
1266
1275
  }
1267
1276
  const handler = subgraph.handlers[sourceName] ?? subgraph.handlers["*"] ?? null;
1268
1277
  if (!handler) {
1269
- logger2.warn("No handler found for source", {
1278
+ logger3.warn("No handler found for source", {
1270
1279
  subgraph: subgraph.name,
1271
1280
  sourceName,
1272
1281
  txId: tx.tx_id
@@ -1323,7 +1332,7 @@ async function runHandlers(subgraph, matched, ctx, opts) {
1323
1332
  const verdict = validatePrintPayload(filter.prints, printPayload.topic ?? "", printPayload.data ?? {});
1324
1333
  if (!verdict.ok) {
1325
1334
  skipped++;
1326
- logger2.warn("Print payload does not match declared prints schema", {
1335
+ logger3.warn("Print payload does not match declared prints schema", {
1327
1336
  subgraph: subgraph.name,
1328
1337
  sourceName,
1329
1338
  topic: printPayload.topic,
@@ -1338,7 +1347,7 @@ async function runHandlers(subgraph, matched, ctx, opts) {
1338
1347
  } catch (err) {
1339
1348
  ctx.rollbackTo(checkpoint);
1340
1349
  errors++;
1341
- logger2.error("Subgraph handler error", {
1350
+ logger3.error("Subgraph handler error", {
1342
1351
  subgraph: subgraph.name,
1343
1352
  sourceName,
1344
1353
  txId: tx.tx_id,
@@ -1623,7 +1632,7 @@ import {
1623
1632
  resolveSubgraphDb,
1624
1633
  updateSubgraphStatus
1625
1634
  } from "@secondlayer/shared/db/queries/subgraphs";
1626
- import { logger as logger5 } from "@secondlayer/shared/logger";
1635
+ import { logger as logger6 } from "@secondlayer/shared/logger";
1627
1636
  import { sql as sql3 } from "kysely";
1628
1637
 
1629
1638
  // src/runtime/block-source.ts
@@ -1631,7 +1640,7 @@ import { getSourceDb as getSourceDb2 } from "@secondlayer/shared/db";
1631
1640
  import {
1632
1641
  IndexHttpClient
1633
1642
  } from "@secondlayer/shared/index-http";
1634
- import { logger as logger3 } from "@secondlayer/shared/logger";
1643
+ import { logger as logger4 } from "@secondlayer/shared/logger";
1635
1644
 
1636
1645
  // src/runtime/batch-loader.ts
1637
1646
  async function loadBlockRange(db, fromHeight, toHeight) {
@@ -1955,7 +1964,7 @@ class FallbackBlockSource {
1955
1964
  try {
1956
1965
  return await this.primary.getTip();
1957
1966
  } catch (err) {
1958
- logger3.warn("block source primary getTip failed — using DB tap", {
1967
+ logger4.warn("block source primary getTip failed — using DB tap", {
1959
1968
  error: err instanceof Error ? err.message : String(err)
1960
1969
  });
1961
1970
  return this.fallback.getTip();
@@ -1965,7 +1974,7 @@ class FallbackBlockSource {
1965
1974
  try {
1966
1975
  return await this.primary.loadBlockRange(fromHeight, toHeight);
1967
1976
  } catch (err) {
1968
- logger3.warn("block source primary loadBlockRange failed — using DB tap", {
1977
+ logger4.warn("block source primary loadBlockRange failed — using DB tap", {
1969
1978
  from: fromHeight,
1970
1979
  to: toHeight,
1971
1980
  error: err instanceof Error ? err.message : String(err)
@@ -1997,7 +2006,7 @@ function resolveBlockSource(subgraph) {
1997
2006
  return new FallbackBlockSource(new PublicApiBlockSource(buildHttpClient(), referencedIndexEventTypes(subgraph), canSparseScan(subgraph) ? sparseProbeTargets(subgraph) : undefined, needsTransactionData(subgraph)), postgresBlockSource);
1998
2007
  }
1999
2008
  if (process.env.SUBGRAPH_SOURCE === "streams-index" && subgraph) {
2000
- logger3.debug("Subgraph not streams-index eligible, using DB tap", {
2009
+ logger4.debug("Subgraph not streams-index eligible, using DB tap", {
2001
2010
  subgraph: subgraph.name
2002
2011
  });
2003
2012
  }
@@ -2006,7 +2015,7 @@ function resolveBlockSource(subgraph) {
2006
2015
 
2007
2016
  // src/runtime/outbox-emit.ts
2008
2017
  import { createHash as createHash2 } from "node:crypto";
2009
- import { logger as logger4 } from "@secondlayer/shared/logger";
2018
+ import { logger as logger5 } from "@secondlayer/shared/logger";
2010
2019
  var loggedKillSwitch = false;
2011
2020
  var OP_VERB = {
2012
2021
  insert: "created",
@@ -2030,7 +2039,7 @@ function stableStringify(obj) {
2030
2039
  async function emitSubscriptionOutbox(tx, subgraphName, manifest, matcher, blockHeight) {
2031
2040
  if (!isEmitOutboxEnabled()) {
2032
2041
  if (!loggedKillSwitch) {
2033
- logger4.warn("SECONDLAYER_EMIT_OUTBOX=false — outbox emission bypassed");
2042
+ logger5.warn("SECONDLAYER_EMIT_OUTBOX=false — outbox emission bypassed");
2034
2043
  loggedKillSwitch = true;
2035
2044
  }
2036
2045
  return 0;
@@ -2350,7 +2359,7 @@ async function processBlockWithRetry(subgraph, subgraphName, blockHeight, opts,
2350
2359
  const delay = retryDelaysMs[attempt];
2351
2360
  if (delay === undefined)
2352
2361
  break;
2353
- logger5.warn("Block processing failed, retrying", {
2362
+ logger6.warn("Block processing failed, retrying", {
2354
2363
  subgraph: subgraphName,
2355
2364
  blockHeight,
2356
2365
  attempt: attempt + 1,
@@ -2382,7 +2391,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
2382
2391
  } else {
2383
2392
  const data = (await resolveBlockSource(subgraph).loadBlockRange(blockHeight, blockHeight)).get(blockHeight);
2384
2393
  if (!data) {
2385
- logger5.debug("Block not found or non-canonical for subgraph processing", {
2394
+ logger6.debug("Block not found or non-canonical for subgraph processing", {
2386
2395
  subgraph: subgraphName,
2387
2396
  blockHeight
2388
2397
  });
@@ -2523,7 +2532,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
2523
2532
  });
2524
2533
  } catch (err) {
2525
2534
  if (err instanceof CursorRaceLostError) {
2526
- logger5.warn("cursor race lost — block already covered", {
2535
+ logger6.warn("cursor race lost — block already covered", {
2527
2536
  subgraph: subgraphName,
2528
2537
  blockHeight,
2529
2538
  error: err.message
@@ -2547,7 +2556,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
2547
2556
  const { rows } = await sql3.raw(`SELECT n_live_tup AS count FROM pg_stat_user_tables WHERE schemaname = '${schemaName}' AND relname = '${table}'`).execute(route.dataDb);
2548
2557
  const count = Number(rows[0]?.count ?? 0);
2549
2558
  if (count >= 1e7) {
2550
- logger5.warn("Subgraph table exceeds 10M rows (estimate)", {
2559
+ logger6.warn("Subgraph table exceeds 10M rows (estimate)", {
2551
2560
  subgraph: subgraphName,
2552
2561
  table,
2553
2562
  count
@@ -2555,7 +2564,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
2555
2564
  }
2556
2565
  }
2557
2566
  } catch (err) {
2558
- logger5.debug("Row count sample failed", {
2567
+ logger6.debug("Row count sample failed", {
2559
2568
  subgraph: subgraphName,
2560
2569
  error: err instanceof Error ? err.message : String(err)
2561
2570
  });
@@ -2633,7 +2642,7 @@ class StatsAccumulator {
2633
2642
  // src/runtime/catchup.ts
2634
2643
  import { getTargetDb as getTargetDb2 } from "@secondlayer/shared/db";
2635
2644
  import { getSubgraph } from "@secondlayer/shared/db/queries/subgraphs";
2636
- import { logger as logger6 } from "@secondlayer/shared/logger";
2645
+ import { logger as logger7 } from "@secondlayer/shared/logger";
2637
2646
  var LOG_INTERVAL = 1000;
2638
2647
  var STANDARD_CATCHUP_BATCH_CONFIG = {
2639
2648
  defaultBatchSize: 500,
@@ -2740,7 +2749,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
2740
2749
  const subgraphStart = Number(subgraphRow.start_block) || 1;
2741
2750
  const startBlock = Math.max(lastProcessedBlock + 1, subgraphStart);
2742
2751
  const totalBlocks = chainTip - lastProcessedBlock;
2743
- logger6.info("Subgraph catch-up starting", {
2752
+ logger7.info("Subgraph catch-up starting", {
2744
2753
  subgraph: subgraphName,
2745
2754
  from: startBlock,
2746
2755
  to: chainTip,
@@ -2757,7 +2766,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
2757
2766
  while (currentHeight <= chainTip) {
2758
2767
  const currentRow = await getSubgraph(targetDb, subgraphName);
2759
2768
  if (!currentRow || currentRow.status !== "active") {
2760
- logger6.info("Subgraph status changed, stopping catch-up", {
2769
+ logger7.info("Subgraph status changed, stopping catch-up", {
2761
2770
  subgraph: subgraphName,
2762
2771
  status: currentRow?.status ?? "deleted"
2763
2772
  });
@@ -2786,7 +2795,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
2786
2795
  blockData = (await source.loadBlockRange(height, height)).get(height);
2787
2796
  }
2788
2797
  if (!blockData) {
2789
- logger6.warn("Block missing during catch-up, deferring to next tick", {
2798
+ logger7.warn("Block missing during catch-up, deferring to next tick", {
2790
2799
  subgraph: subgraphName,
2791
2800
  blockHeight: height
2792
2801
  });
@@ -2806,12 +2815,12 @@ async function catchUpSubgraph(subgraph, subgraphName) {
2806
2815
  });
2807
2816
  } catch (err) {
2808
2817
  if (err instanceof ReorgEpochChangedError) {
2809
- logger6.info("Reorg detected mid catch-up, aborting walk without writing further progress", { subgraph: subgraphName, blockHeight: height });
2818
+ logger7.info("Reorg detected mid catch-up, aborting walk without writing further progress", { subgraph: subgraphName, blockHeight: height });
2810
2819
  stopCatchup = true;
2811
2820
  break;
2812
2821
  }
2813
2822
  const errorMsg = err instanceof Error ? err.message : String(err);
2814
- logger6.error("Block processing failed persistently during catch-up", {
2823
+ logger7.error("Block processing failed persistently during catch-up", {
2815
2824
  subgraph: subgraphName,
2816
2825
  blockHeight: height,
2817
2826
  error: errorMsg
@@ -2830,7 +2839,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
2830
2839
  }
2831
2840
  }
2832
2841
  if (processed % LOG_INTERVAL === 0) {
2833
- logger6.info("Subgraph catch-up progress", {
2842
+ logger7.info("Subgraph catch-up progress", {
2834
2843
  subgraph: subgraphName,
2835
2844
  processed,
2836
2845
  total: totalBlocks,
@@ -2846,7 +2855,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
2846
2855
  currentHeight = batchEnd + 1;
2847
2856
  }
2848
2857
  await stats.flush(targetDb);
2849
- logger6.info("Subgraph catch-up complete", {
2858
+ logger7.info("Subgraph catch-up complete", {
2850
2859
  subgraph: subgraphName,
2851
2860
  processed
2852
2861
  });
@@ -2863,13 +2872,13 @@ import {
2863
2872
  listSubgraphs,
2864
2873
  resolveSubgraphRawClient
2865
2874
  } from "@secondlayer/shared/db/queries/subgraphs";
2866
- import { logger as logger7 } from "@secondlayer/shared/logger";
2875
+ import { logger as logger8 } from "@secondlayer/shared/logger";
2867
2876
  async function handleSubgraphReorg(blockHeight, loadSubgraphDef) {
2868
2877
  const targetDb = getTargetDb3();
2869
2878
  const activeSubgraphs = (await listSubgraphs(targetDb)).filter((v) => v.status === "active");
2870
2879
  if (activeSubgraphs.length === 0)
2871
2880
  return;
2872
- logger7.info("Propagating reorg to subgraphs", {
2881
+ logger8.info("Propagating reorg to subgraphs", {
2873
2882
  blockHeight,
2874
2883
  subgraphCount: activeSubgraphs.length
2875
2884
  });
@@ -2908,7 +2917,7 @@ async function handleSubgraphReorg(blockHeight, loadSubgraphDef) {
2908
2917
  await tx.unsafe(`DELETE FROM "${schemaName}"."_journal" WHERE block_height >= $1`, [blockHeight]);
2909
2918
  });
2910
2919
  } else {
2911
- logger7.warn("Subgraph has no revert journal — falling back to height delete (accumulator rows may lose history)", { subgraph: sg.name, blockHeight });
2920
+ logger8.warn("Subgraph has no revert journal — falling back to height delete (accumulator rows may lose history)", { subgraph: sg.name, blockHeight });
2912
2921
  for (const tableName of tableNames) {
2913
2922
  await client.unsafe(`DELETE FROM "${schemaName}"."${tableName}" WHERE "_block_height" >= $1`, [blockHeight]);
2914
2923
  }
@@ -2951,7 +2960,7 @@ async function handleSubgraphReorg(blockHeight, loadSubgraphDef) {
2951
2960
  }).as("row_pk"),
2952
2961
  eb2.val("pending").as("status")
2953
2962
  ]).where("subgraph_name", "=", sg.name).where("table_name", "=", tableName).where("status", "=", "active")).onConflict((oc) => oc.columns(["subscription_id", "dedup_key"]).doNothing()).execute().catch((err) => {
2954
- logger7.error("Failed to emit revert event for subscriptions", {
2963
+ logger8.error("Failed to emit revert event for subscriptions", {
2955
2964
  event: "reorg_revert_emit_dropped",
2956
2965
  subgraph: sg.name,
2957
2966
  table: tableName,
@@ -2960,20 +2969,20 @@ async function handleSubgraphReorg(blockHeight, loadSubgraphDef) {
2960
2969
  });
2961
2970
  });
2962
2971
  }
2963
- logger7.info("Subgraph reorg cleanup done", {
2972
+ logger8.info("Subgraph reorg cleanup done", {
2964
2973
  subgraph: sg.name,
2965
2974
  blockHeight,
2966
2975
  tablesAffected: Object.keys(revertedByTable).length
2967
2976
  });
2968
2977
  const def = await loadSubgraphDef(sg);
2969
2978
  await processBlock(def, sg.name, blockHeight);
2970
- logger7.info("Subgraph reorg reprocessed", {
2979
+ logger8.info("Subgraph reorg reprocessed", {
2971
2980
  subgraph: sg.name,
2972
2981
  blockHeight
2973
2982
  });
2974
2983
  });
2975
2984
  } catch (err) {
2976
- logger7.error("Subgraph reorg handling failed", {
2985
+ logger8.error("Subgraph reorg handling failed", {
2977
2986
  subgraph: sg.name,
2978
2987
  blockHeight,
2979
2988
  error: getErrorMessage2(err)
@@ -2985,5 +2994,5 @@ export {
2985
2994
  handleSubgraphReorg
2986
2995
  };
2987
2996
 
2988
- //# debugId=05228F28851AE4EE64756E2164756E21
2997
+ //# debugId=33E820804CBE066864756E2164756E21
2989
2998
  //# sourceMappingURL=reorg.js.map