@secondlayer/subgraphs 6.3.6 → 6.5.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.
@@ -16,7 +16,7 @@ import { AssetIdentifier } from "@secondlayer/stacks/filters";
16
16
  */
17
17
  type ChainReadCacheMode = "per-block" | "contract-constant";
18
18
  type ChainReadOptions = {
19
- cache?: ChainReadCacheMode
19
+ cache?: ChainReadCacheMode;
20
20
  };
21
21
  /**
22
22
  * `ChainReadClient` with the ABI generic erased. Structurally cheap, so the
@@ -25,7 +25,7 @@ type ChainReadOptions = {
25
25
  */
26
26
  interface ErasedChainReadClient {
27
27
  contract(contractId: string, abi: unknown, options?: ChainReadOptions): {
28
- read: Record<string, (args?: Record<string, unknown>) => Promise<unknown>>
28
+ read: Record<string, (args?: Record<string, unknown>) => Promise<unknown>>;
29
29
  };
30
30
  }
31
31
  /** Supported column types for subgraph schemas */
@@ -86,7 +86,7 @@ interface StxLockFilter {
86
86
  * as-of each processed block. Lets a source index "all SIP-010 tokens" etc.
87
87
  */
88
88
  type TraitScope = {
89
- trait?: string
89
+ trait?: string;
90
90
  };
91
91
  /**
92
92
  * Scope a source to a DYNAMIC address set discovered from another source's
@@ -126,10 +126,10 @@ type TraitScope = {
126
126
  interface FactoryScope {
127
127
  factory?: {
128
128
  /** Source name whose events reveal the addresses. */
129
- from: string
129
+ from: string;
130
130
  /** Dotted path to the address on that source's payload
131
131
  * (e.g. `"data.pool"`, `"contractId"`, `"data.token.id"`). */
132
- field: string
132
+ field: string;
133
133
  };
134
134
  }
135
135
  /** FT event filters */
@@ -235,12 +235,12 @@ interface MapWriteFilter extends TraitScope, FactoryScope {
235
235
  * - `{ list: F }` — a list of `F`.
236
236
  */
237
237
  type PrintField = ColumnType | {
238
- type: PrintField
239
- optional: true
238
+ type: PrintField;
239
+ optional: true;
240
240
  } | {
241
- tuple: Record<string, PrintField>
241
+ tuple: Record<string, PrintField>;
242
242
  } | {
243
- list: PrintField
243
+ list: PrintField;
244
244
  };
245
245
  /**
246
246
  * Per-topic field schema for a print_event source. When declared, the handler's
@@ -252,34 +252,34 @@ type PrintField = ColumnType | {
252
252
  */
253
253
  type PrintEventPrints = Record<string, Record<string, PrintField>>;
254
254
  type PrintEventFields = FactoryScope & {
255
- type: "print_event"
256
- topic?: string
255
+ type: "print_event";
256
+ topic?: string;
257
257
  };
258
258
  /**
259
259
  * Pinned to one or more contract ids with no trait — `prints` is required so
260
260
  * `event.data` is typed and empty-table handlers cannot typecheck.
261
261
  */
262
262
  type PinnedPrintEventFilter = PrintEventFields & {
263
- contractId: string | readonly string[]
264
- trait?: undefined
265
- prints: PrintEventPrints
263
+ contractId: string | readonly string[];
264
+ trait?: undefined;
265
+ prints: PrintEventPrints;
266
266
  };
267
267
  /**
268
268
  * Trait-scoped print source (optionally narrowed by contractId). No single
269
269
  * schema — `prints` stays optional.
270
270
  */
271
271
  type TraitPrintEventFilter = PrintEventFields & {
272
- contractId?: string | readonly string[]
273
- trait: string
274
- prints?: PrintEventPrints
272
+ contractId?: string | readonly string[];
273
+ trait: string;
274
+ prints?: PrintEventPrints;
275
275
  };
276
276
  /**
277
277
  * Unpinned / factory-only print source (no contractId). `prints` optional.
278
278
  */
279
279
  type UnpinnedPrintEventFilter = PrintEventFields & {
280
- contractId?: undefined
281
- trait?: string
282
- prints?: PrintEventPrints
280
+ contractId?: undefined;
281
+ trait?: string;
282
+ prints?: PrintEventPrints;
283
283
  };
284
284
  /**
285
285
  * Print-event filter. Pinned sources (contractId set, no trait) require
@@ -300,12 +300,12 @@ interface TxMeta {
300
300
  /** Context passed to subgraph handlers during event processing */
301
301
  interface SubgraphContext {
302
302
  block: {
303
- height: number
304
- hash: string
305
- timestamp: number
306
- burnBlockHeight: number
303
+ height: number;
304
+ hash: string;
305
+ timestamp: number;
306
+ burnBlockHeight: number;
307
307
  /** Nakamoto StacksBlockId, when the block row carries one. */
308
- indexBlockHash?: string | null
308
+ indexBlockHash?: string | null;
309
309
  };
310
310
  tx: TxMeta;
311
311
  /**
@@ -366,6 +366,7 @@ interface GeneratedSQL {
366
366
  declare function generateSubgraphSQL(def: SubgraphDefinition, schemaNameOverride?: string): GeneratedSQL;
367
367
  import { Database as Database2 } from "@secondlayer/shared/db";
368
368
  import { Kysely as Kysely2 } from "kysely";
369
+ import { SubgraphVerification } from "@secondlayer/shared/schemas/subgraphs";
369
370
  type AnyDb = Kysely2<Database2>;
370
371
  interface TableDiff {
371
372
  /** Tables added to the schema */
@@ -422,18 +423,24 @@ interface DeployDiff {
422
423
  * - Breaking change → auto-reindex (drop + recreate)
423
424
  */
424
425
  declare function deploySchema(db: AnyDb, def: SubgraphDefinition, handlerPath: string, opts?: {
425
- forceReindex?: boolean
426
- apiKeyId?: string
427
- accountId?: string
428
- schemaName?: string
429
- handlerCode?: string
430
- sourceCode?: string
426
+ forceReindex?: boolean;
427
+ apiKeyId?: string;
428
+ accountId?: string;
429
+ schemaName?: string;
430
+ handlerCode?: string;
431
+ sourceCode?: string;
432
+ /** Derived level to store (see `deriveVerification`). */
433
+ verification?: SubgraphVerification;
434
+ /** Network baked into the pin. Defaults to `NETWORK`, then mainnet. */
435
+ network?: string;
431
436
  }): Promise<{
432
- action: "created" | "unchanged" | "handler_updated" | "updated" | "reindexed"
433
- subgraphId: string
434
- version: string
435
- diff?: DeployDiff
437
+ action: "created" | "unchanged" | "handler_updated" | "updated" | "reindexed";
438
+ subgraphId: string;
439
+ version: string;
440
+ /** Present when the deploy carried a bundled handler. */
441
+ pin?: string;
442
+ diff?: DeployDiff;
436
443
  /** Non-fatal caveats the caller should surface (e.g. handler grafts at tip). */
437
- warnings?: string[]
444
+ warnings?: string[];
438
445
  }>;
439
446
  export { ColumnDiff, GeneratedSQL, TYPE_MAP, TableDiff, deploySchema, diffSchema, generateSubgraphSQL };
@@ -461,6 +461,274 @@ function filterFieldsByType() {
461
461
  return out;
462
462
  }
463
463
 
464
+ // src/verification.ts
465
+ import { createHash as createHash2 } from "node:crypto";
466
+ // package.json
467
+ var package_default = {
468
+ name: "@secondlayer/subgraphs",
469
+ version: "6.5.0",
470
+ description: "Describe the Stacks data you want as one TypeScript file; it fills itself and survives reorgs.",
471
+ license: "MIT",
472
+ repository: {
473
+ type: "git",
474
+ url: "git+https://github.com/ryanwaits/secondlayer.git",
475
+ directory: "packages/subgraphs"
476
+ },
477
+ homepage: "https://github.com/ryanwaits/secondlayer/tree/main/packages/subgraphs#readme",
478
+ type: "module",
479
+ main: "./dist/index.js",
480
+ types: "./dist/index.d.ts",
481
+ exports: {
482
+ ".": {
483
+ types: "./dist/index.d.ts",
484
+ import: "./dist/index.js"
485
+ },
486
+ "./types": {
487
+ types: "./dist/types.d.ts",
488
+ import: "./dist/types.js"
489
+ },
490
+ "./schema": {
491
+ types: "./dist/schema/index.d.ts",
492
+ import: "./dist/schema/index.js"
493
+ },
494
+ "./validate": {
495
+ types: "./dist/validate.d.ts",
496
+ import: "./dist/validate.js"
497
+ },
498
+ "./verification": {
499
+ types: "./dist/verification.d.ts",
500
+ import: "./dist/verification.js"
501
+ },
502
+ "./runtime/replay": {
503
+ types: "./dist/runtime/replay.d.ts",
504
+ import: "./dist/runtime/replay.js"
505
+ },
506
+ "./runtime/emitter": {
507
+ types: "./dist/runtime/emitter.d.ts",
508
+ import: "./dist/runtime/emitter.js"
509
+ },
510
+ "./testing": {
511
+ types: "./dist/testing/index.d.ts",
512
+ import: "./dist/testing/index.js"
513
+ },
514
+ "./verify": {
515
+ types: "./dist/verify/index.d.ts",
516
+ import: "./dist/verify/index.js"
517
+ }
518
+ },
519
+ files: ["dist"],
520
+ scripts: {
521
+ build: "bunup",
522
+ dev: "bunup --watch",
523
+ test: "bun test",
524
+ typecheck: "tsc --noEmit",
525
+ prepublishOnly: "bun run build"
526
+ },
527
+ dependencies: {
528
+ "@secondlayer/shared": "^11.23.0",
529
+ "@secondlayer/stacks": "^6.2.0",
530
+ "@secondlayer/verify": "^0.3.0",
531
+ esbuild: "^0.28.2",
532
+ kysely: "0.28.17",
533
+ zod: "^4.3.6"
534
+ },
535
+ devDependencies: {
536
+ "@types/bun": "latest",
537
+ bunup: "0.16.32",
538
+ typescript: "^5"
539
+ },
540
+ publishConfig: {
541
+ access: "public"
542
+ }
543
+ };
544
+
545
+ // src/verification.ts
546
+ var SUBGRAPHS_RUNTIME = `@secondlayer/subgraphs@${package_default.version}`;
547
+ var FORBIDDEN_GLOBALS = {
548
+ Date: "wall-clock time differs per run",
549
+ performance: "timers differ per run",
550
+ process: "host environment differs per run",
551
+ fetch: "network reads are not chain inputs",
552
+ crypto: "randomness differs per run",
553
+ setTimeout: "scheduling order differs per run",
554
+ setInterval: "scheduling order differs per run",
555
+ setImmediate: "scheduling order differs per run",
556
+ clearTimeout: "scheduling order differs per run",
557
+ clearInterval: "scheduling order differs per run",
558
+ queueMicrotask: "scheduling order differs per run",
559
+ WeakRef: "garbage collection timing differs per run",
560
+ FinalizationRegistry: "garbage collection timing differs per run",
561
+ Atomics: "shared memory is not single-threaded",
562
+ SharedArrayBuffer: "shared memory is not single-threaded",
563
+ Intl: "locale and time zone data differ per host",
564
+ Temporal: "locale and time zone data differ per host",
565
+ eval: "generated code hides inputs from the scan",
566
+ WebAssembly: "compiled code hides inputs from the scan",
567
+ ShadowRealm: "a nested realm escapes the allow-list",
568
+ require: "imports must be bundled into the handler"
569
+ };
570
+ var ALLOWED_GLOBALS = [
571
+ "undefined",
572
+ "NaN",
573
+ "Infinity",
574
+ "globalThis",
575
+ "isNaN",
576
+ "isFinite",
577
+ "parseInt",
578
+ "parseFloat",
579
+ "decodeURI",
580
+ "decodeURIComponent",
581
+ "encodeURI",
582
+ "encodeURIComponent",
583
+ "escape",
584
+ "unescape",
585
+ "Object",
586
+ "Function",
587
+ "Array",
588
+ "Number",
589
+ "Boolean",
590
+ "String",
591
+ "Symbol",
592
+ "BigInt",
593
+ "Math",
594
+ "JSON",
595
+ "Reflect",
596
+ "Proxy",
597
+ "Promise",
598
+ "Map",
599
+ "Set",
600
+ "WeakMap",
601
+ "WeakSet",
602
+ "RegExp",
603
+ "Iterator",
604
+ "DisposableStack",
605
+ "AsyncDisposableStack",
606
+ "Error",
607
+ "AggregateError",
608
+ "SuppressedError",
609
+ "EvalError",
610
+ "RangeError",
611
+ "ReferenceError",
612
+ "SyntaxError",
613
+ "TypeError",
614
+ "URIError",
615
+ "ArrayBuffer",
616
+ "DataView",
617
+ "Int8Array",
618
+ "Uint8Array",
619
+ "Uint8ClampedArray",
620
+ "Int16Array",
621
+ "Uint16Array",
622
+ "Int32Array",
623
+ "Uint32Array",
624
+ "Float16Array",
625
+ "Float32Array",
626
+ "Float64Array",
627
+ "BigInt64Array",
628
+ "BigUint64Array",
629
+ "TextEncoder",
630
+ "TextDecoder",
631
+ "atob",
632
+ "btoa",
633
+ "console"
634
+ ];
635
+ var ALLOWED_MATH = [
636
+ "abs",
637
+ "min",
638
+ "max",
639
+ "floor",
640
+ "ceil",
641
+ "trunc",
642
+ "sign",
643
+ "round",
644
+ "E",
645
+ "LN10",
646
+ "LN2",
647
+ "LOG10E",
648
+ "LOG2E",
649
+ "PI",
650
+ "SQRT1_2",
651
+ "SQRT2"
652
+ ];
653
+ var LOCALE_METHODS = [
654
+ "toLocaleString",
655
+ "toLocaleDateString",
656
+ "toLocaleTimeString",
657
+ "toLocaleUpperCase",
658
+ "toLocaleLowerCase",
659
+ "localeCompare"
660
+ ];
661
+ function formatFinding(f) {
662
+ return `${f.file}:${f.line}:${f.column} ${f.name}: ${f.reason}`;
663
+ }
664
+ var STATE_SOURCE_TYPES = new Set([
665
+ "var_set",
666
+ "map_set",
667
+ "map_delete"
668
+ ]);
669
+ var LEVEL_RANK = { state: 0, events: 1, none: 2 };
670
+ var MAX_LISTED_FINDINGS = 10;
671
+ function deriveVerification(def, findings = []) {
672
+ let level = "state";
673
+ const raise = (to) => {
674
+ if (LEVEL_RANK[to] > LEVEL_RANK[level])
675
+ level = to;
676
+ };
677
+ const reasons = [];
678
+ for (const [name, source] of Object.entries(def.sources ?? {})) {
679
+ const filter = source;
680
+ if (filter.trait) {
681
+ raise("none");
682
+ reasons.push(`source "${name}": trait scope needs a proven contract registry`);
683
+ } else if (filter.type === "map_insert") {
684
+ raise("events");
685
+ reasons.push(`map_insert source "${name}": map_insert vs map_set needs event proofs`);
686
+ } else if (!STATE_SOURCE_TYPES.has(filter.type)) {
687
+ raise("events");
688
+ reasons.push(`${filter.type} source "${name}" needs event proofs`);
689
+ }
690
+ }
691
+ if (def.backfillMode === "concurrent") {
692
+ raise("none");
693
+ reasons.push("backfillMode concurrent: tip-first order is not chain order");
694
+ }
695
+ if (findings.some((f) => f.kind === "needs-events"))
696
+ raise("events");
697
+ for (const f of findings.slice(0, MAX_LISTED_FINDINGS)) {
698
+ reasons.push(f.kind === "needs-events" ? `${formatFinding(f)} (needs event proofs)` : formatFinding(f));
699
+ }
700
+ if (findings.length > MAX_LISTED_FINDINGS) {
701
+ reasons.push(`…and ${findings.length - MAX_LISTED_FINDINGS} more handler findings`);
702
+ }
703
+ return {
704
+ level,
705
+ reasons,
706
+ unproven: reasons.length === 0 ? [
707
+ "tx attribution of writes: event.tx, _tx_id (needs event proofs)",
708
+ "intermediate writes within a block (needs event proofs)"
709
+ ] : []
710
+ };
711
+ }
712
+ function isDeterminismViolation(v) {
713
+ return v.level === "state" && v.reasons.length > 0;
714
+ }
715
+ function sha256(input) {
716
+ return createHash2("sha256").update(input).digest("hex");
717
+ }
718
+ function pinPreimage(input) {
719
+ const fields = {
720
+ handlerHash: sha256(input.handlerCode),
721
+ network: input.network,
722
+ runtime: input.runtime ?? SUBGRAPHS_RUNTIME,
723
+ schemaHash: input.schemaHash,
724
+ startBlock: input.startBlock ?? null
725
+ };
726
+ return JSON.stringify(fields);
727
+ }
728
+ function computePin(input) {
729
+ return sha256(pinPreimage(input));
730
+ }
731
+
464
732
  // src/schema/deployer.ts
465
733
  function toJsonSafe(obj) {
466
734
  return JSON.parse(JSON.stringify(obj, (_key, value) => typeof value === "bigint" ? value.toString() : value));
@@ -558,6 +826,15 @@ function renderDeployPlan(def, schemaName) {
558
826
  async function deploySchema(db, def, handlerPath, opts) {
559
827
  validateSubgraphDefinition(def);
560
828
  const { statements, hash } = generateSubgraphSQL(def, opts?.schemaName);
829
+ const pinInput = opts?.handlerCode != null ? {
830
+ schemaHash: hash,
831
+ handlerCode: opts.handlerCode,
832
+ startBlock: def.startBlock,
833
+ network: opts.network ?? process.env.NETWORK ?? "mainnet"
834
+ } : null;
835
+ const pin = pinInput ? computePin(pinInput) : null;
836
+ const preimage = pinInput ? pinPreimage(pinInput) : null;
837
+ const identity = pin ? { pin } : {};
561
838
  const { getSubgraph, registerSubgraph } = await import("@secondlayer/shared/db/queries/subgraphs");
562
839
  const ddlDb = db;
563
840
  const existing = await getSubgraph(db, def.name, opts?.accountId);
@@ -580,7 +857,10 @@ async function deploySchema(db, def, handlerPath, opts) {
580
857
  handlerCode: opts?.handlerCode,
581
858
  sourceCode: opts?.sourceCode,
582
859
  schemaName,
583
- startBlock: def.startBlock
860
+ startBlock: def.startBlock,
861
+ pin,
862
+ pinPreimage: preimage,
863
+ verification: opts?.verification ?? null
584
864
  };
585
865
  if (existing) {
586
866
  const schemaExists = await sql`
@@ -594,19 +874,28 @@ async function deploySchema(db, def, handlerPath, opts) {
594
874
  await sql.raw(stmt).execute(ddlDb);
595
875
  }
596
876
  const sg = await registerSubgraph(db, regData);
597
- return { action: "reindexed", subgraphId: sg.id, version: newVersion };
877
+ return {
878
+ action: "reindexed",
879
+ subgraphId: sg.id,
880
+ version: newVersion,
881
+ ...identity
882
+ };
598
883
  }
599
884
  if (existing.schema_hash === hash && !opts?.forceReindex) {
600
885
  const handlerChanged = opts?.handlerCode != null && opts.handlerCode !== existing.handler_code;
601
886
  const { updateSubgraphHandlerPath } = await import("@secondlayer/shared/db/queries/subgraphs");
602
887
  await updateSubgraphHandlerPath(db, def.name, handlerPath, {
603
888
  handlerCode: opts?.handlerCode,
604
- sourceCode: opts?.sourceCode
889
+ sourceCode: opts?.sourceCode,
890
+ pin,
891
+ pinPreimage: preimage,
892
+ verification: opts?.verification ?? null
605
893
  });
606
894
  return {
607
895
  action: handlerChanged ? "handler_updated" : "unchanged",
608
896
  subgraphId: existing.id,
609
897
  version: existing.version,
898
+ ...identity,
610
899
  ...handlerChanged ? {
611
900
  warnings: [
612
901
  "handler changed but schema did not: new logic applies from the current tip only. Rows already indexed were computed by the previous handler. To recompute history, redeploy with --reindex (or backfill a range)."
@@ -620,7 +909,12 @@ async function deploySchema(db, def, handlerPath, opts) {
620
909
  await sql.raw(stmt).execute(ddlDb);
621
910
  }
622
911
  const sg = await registerSubgraph(db, regData);
623
- return { action: "reindexed", subgraphId: sg.id, version: newVersion };
912
+ return {
913
+ action: "reindexed",
914
+ subgraphId: sg.id,
915
+ version: newVersion,
916
+ ...identity
917
+ };
624
918
  }
625
919
  if (existing.definition.schema) {
626
920
  const diff = diffSchema(existing.definition.schema, def.schema);
@@ -636,6 +930,7 @@ async function deploySchema(db, def, handlerPath, opts) {
636
930
  action: "reindexed",
637
931
  subgraphId: sg.id,
638
932
  version: newVersion,
933
+ ...identity,
639
934
  diff: deployDiff
640
935
  };
641
936
  }
@@ -755,6 +1050,7 @@ async function deploySchema(db, def, handlerPath, opts) {
755
1050
  action: "updated",
756
1051
  subgraphId: sg.id,
757
1052
  version: newVersion,
1053
+ ...identity,
758
1054
  diff: deployDiff
759
1055
  };
760
1056
  }
@@ -763,7 +1059,12 @@ async function deploySchema(db, def, handlerPath, opts) {
763
1059
  await sql.raw(stmt).execute(ddlDb);
764
1060
  }
765
1061
  const sg = await registerSubgraph(db, regData);
766
- return { action: "created", subgraphId: sg.id, version: newVersion };
1062
+ return {
1063
+ action: "created",
1064
+ subgraphId: sg.id,
1065
+ version: newVersion,
1066
+ ...identity
1067
+ };
767
1068
  }
768
1069
  function getDefault(type) {
769
1070
  switch (type) {
@@ -790,5 +1091,5 @@ export {
790
1091
  generateSubgraphSQL
791
1092
  };
792
1093
 
793
- //# debugId=054E6A0FD91586DA64756E2164756E21
1094
+ //# debugId=858E528FDE59D30764756E2164756E21
794
1095
  //# sourceMappingURL=index.js.map