@secondlayer/subgraphs 6.3.6 → 6.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.
- package/dist/index.d.ts +82 -1
- package/dist/index.js +514 -9
- package/dist/index.js.map +6 -4
- package/dist/schema/index.d.ts +7 -0
- package/dist/schema/index.js +294 -6
- package/dist/schema/index.js.map +6 -5
- package/dist/testing/index.js +474 -2
- package/dist/testing/index.js.map +7 -5
- package/dist/verification.d.ts +426 -0
- package/dist/verification.js +272 -0
- package/dist/verification.js.map +10 -0
- package/package.json +6 -2
package/dist/schema/index.d.ts
CHANGED
|
@@ -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 */
|
|
@@ -428,10 +429,16 @@ declare function deploySchema(db: AnyDb, def: SubgraphDefinition, handlerPath: s
|
|
|
428
429
|
schemaName?: string
|
|
429
430
|
handlerCode?: string
|
|
430
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
437
|
action: "created" | "unchanged" | "handler_updated" | "updated" | "reindexed"
|
|
433
438
|
subgraphId: string
|
|
434
439
|
version: string
|
|
440
|
+
/** Present when the deploy carried a bundled handler. */
|
|
441
|
+
pin?: string
|
|
435
442
|
diff?: DeployDiff
|
|
436
443
|
/** Non-fatal caveats the caller should surface (e.g. handler grafts at tip). */
|
|
437
444
|
warnings?: string[]
|
package/dist/schema/index.js
CHANGED
|
@@ -461,6 +461,265 @@ 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.4.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
|
+
},
|
|
515
|
+
files: ["dist"],
|
|
516
|
+
scripts: {
|
|
517
|
+
build: "bunup",
|
|
518
|
+
dev: "bunup --watch",
|
|
519
|
+
test: "bun test",
|
|
520
|
+
typecheck: "tsc --noEmit",
|
|
521
|
+
prepublishOnly: "bun run build"
|
|
522
|
+
},
|
|
523
|
+
dependencies: {
|
|
524
|
+
"@secondlayer/shared": "^11.22.0",
|
|
525
|
+
"@secondlayer/stacks": "^6.1.0",
|
|
526
|
+
esbuild: "^0.28.2",
|
|
527
|
+
kysely: "0.28.17",
|
|
528
|
+
zod: "^4.3.6"
|
|
529
|
+
},
|
|
530
|
+
devDependencies: {
|
|
531
|
+
"@types/bun": "latest",
|
|
532
|
+
typescript: "^5"
|
|
533
|
+
},
|
|
534
|
+
publishConfig: {
|
|
535
|
+
access: "public"
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
// src/verification.ts
|
|
540
|
+
var SUBGRAPHS_RUNTIME = `@secondlayer/subgraphs@${package_default.version}`;
|
|
541
|
+
var FORBIDDEN_GLOBALS = {
|
|
542
|
+
Date: "wall-clock time differs per run",
|
|
543
|
+
performance: "timers differ per run",
|
|
544
|
+
process: "host environment differs per run",
|
|
545
|
+
fetch: "network reads are not chain inputs",
|
|
546
|
+
crypto: "randomness differs per run",
|
|
547
|
+
setTimeout: "scheduling order differs per run",
|
|
548
|
+
setInterval: "scheduling order differs per run",
|
|
549
|
+
setImmediate: "scheduling order differs per run",
|
|
550
|
+
clearTimeout: "scheduling order differs per run",
|
|
551
|
+
clearInterval: "scheduling order differs per run",
|
|
552
|
+
queueMicrotask: "scheduling order differs per run",
|
|
553
|
+
WeakRef: "garbage collection timing differs per run",
|
|
554
|
+
FinalizationRegistry: "garbage collection timing differs per run",
|
|
555
|
+
Atomics: "shared memory is not single-threaded",
|
|
556
|
+
SharedArrayBuffer: "shared memory is not single-threaded",
|
|
557
|
+
Intl: "locale and time zone data differ per host",
|
|
558
|
+
Temporal: "locale and time zone data differ per host",
|
|
559
|
+
eval: "generated code hides inputs from the scan",
|
|
560
|
+
WebAssembly: "compiled code hides inputs from the scan",
|
|
561
|
+
ShadowRealm: "a nested realm escapes the allow-list",
|
|
562
|
+
require: "imports must be bundled into the handler"
|
|
563
|
+
};
|
|
564
|
+
var ALLOWED_GLOBALS = [
|
|
565
|
+
"undefined",
|
|
566
|
+
"NaN",
|
|
567
|
+
"Infinity",
|
|
568
|
+
"globalThis",
|
|
569
|
+
"isNaN",
|
|
570
|
+
"isFinite",
|
|
571
|
+
"parseInt",
|
|
572
|
+
"parseFloat",
|
|
573
|
+
"decodeURI",
|
|
574
|
+
"decodeURIComponent",
|
|
575
|
+
"encodeURI",
|
|
576
|
+
"encodeURIComponent",
|
|
577
|
+
"escape",
|
|
578
|
+
"unescape",
|
|
579
|
+
"Object",
|
|
580
|
+
"Function",
|
|
581
|
+
"Array",
|
|
582
|
+
"Number",
|
|
583
|
+
"Boolean",
|
|
584
|
+
"String",
|
|
585
|
+
"Symbol",
|
|
586
|
+
"BigInt",
|
|
587
|
+
"Math",
|
|
588
|
+
"JSON",
|
|
589
|
+
"Reflect",
|
|
590
|
+
"Proxy",
|
|
591
|
+
"Promise",
|
|
592
|
+
"Map",
|
|
593
|
+
"Set",
|
|
594
|
+
"WeakMap",
|
|
595
|
+
"WeakSet",
|
|
596
|
+
"RegExp",
|
|
597
|
+
"Iterator",
|
|
598
|
+
"DisposableStack",
|
|
599
|
+
"AsyncDisposableStack",
|
|
600
|
+
"Error",
|
|
601
|
+
"AggregateError",
|
|
602
|
+
"SuppressedError",
|
|
603
|
+
"EvalError",
|
|
604
|
+
"RangeError",
|
|
605
|
+
"ReferenceError",
|
|
606
|
+
"SyntaxError",
|
|
607
|
+
"TypeError",
|
|
608
|
+
"URIError",
|
|
609
|
+
"ArrayBuffer",
|
|
610
|
+
"DataView",
|
|
611
|
+
"Int8Array",
|
|
612
|
+
"Uint8Array",
|
|
613
|
+
"Uint8ClampedArray",
|
|
614
|
+
"Int16Array",
|
|
615
|
+
"Uint16Array",
|
|
616
|
+
"Int32Array",
|
|
617
|
+
"Uint32Array",
|
|
618
|
+
"Float16Array",
|
|
619
|
+
"Float32Array",
|
|
620
|
+
"Float64Array",
|
|
621
|
+
"BigInt64Array",
|
|
622
|
+
"BigUint64Array",
|
|
623
|
+
"TextEncoder",
|
|
624
|
+
"TextDecoder",
|
|
625
|
+
"atob",
|
|
626
|
+
"btoa",
|
|
627
|
+
"console"
|
|
628
|
+
];
|
|
629
|
+
var ALLOWED_MATH = [
|
|
630
|
+
"abs",
|
|
631
|
+
"min",
|
|
632
|
+
"max",
|
|
633
|
+
"floor",
|
|
634
|
+
"ceil",
|
|
635
|
+
"trunc",
|
|
636
|
+
"sign",
|
|
637
|
+
"round",
|
|
638
|
+
"E",
|
|
639
|
+
"LN10",
|
|
640
|
+
"LN2",
|
|
641
|
+
"LOG10E",
|
|
642
|
+
"LOG2E",
|
|
643
|
+
"PI",
|
|
644
|
+
"SQRT1_2",
|
|
645
|
+
"SQRT2"
|
|
646
|
+
];
|
|
647
|
+
var LOCALE_METHODS = [
|
|
648
|
+
"toLocaleString",
|
|
649
|
+
"toLocaleDateString",
|
|
650
|
+
"toLocaleTimeString",
|
|
651
|
+
"toLocaleUpperCase",
|
|
652
|
+
"toLocaleLowerCase",
|
|
653
|
+
"localeCompare"
|
|
654
|
+
];
|
|
655
|
+
function formatFinding(f) {
|
|
656
|
+
return `${f.file}:${f.line}:${f.column} ${f.name}: ${f.reason}`;
|
|
657
|
+
}
|
|
658
|
+
var STATE_SOURCE_TYPES = new Set([
|
|
659
|
+
"var_set",
|
|
660
|
+
"map_set",
|
|
661
|
+
"map_insert",
|
|
662
|
+
"map_delete"
|
|
663
|
+
]);
|
|
664
|
+
var LEVEL_RANK = { state: 0, events: 1, none: 2 };
|
|
665
|
+
var MAX_LISTED_FINDINGS = 10;
|
|
666
|
+
function deriveVerification(def, findings = []) {
|
|
667
|
+
let level = "state";
|
|
668
|
+
const raise = (to) => {
|
|
669
|
+
if (LEVEL_RANK[to] > LEVEL_RANK[level])
|
|
670
|
+
level = to;
|
|
671
|
+
};
|
|
672
|
+
const reasons = [];
|
|
673
|
+
for (const [name, source] of Object.entries(def.sources ?? {})) {
|
|
674
|
+
const filter = source;
|
|
675
|
+
if (filter.trait) {
|
|
676
|
+
raise("none");
|
|
677
|
+
reasons.push(`source "${name}": trait scope needs a proven contract registry`);
|
|
678
|
+
} else if (!STATE_SOURCE_TYPES.has(filter.type)) {
|
|
679
|
+
raise("events");
|
|
680
|
+
reasons.push(`${filter.type} source "${name}" needs event proofs`);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
if (def.backfillMode === "concurrent") {
|
|
684
|
+
raise("none");
|
|
685
|
+
reasons.push("backfillMode concurrent: tip-first order is not chain order");
|
|
686
|
+
}
|
|
687
|
+
if (findings.some((f) => f.kind === "needs-events"))
|
|
688
|
+
raise("events");
|
|
689
|
+
for (const f of findings.slice(0, MAX_LISTED_FINDINGS)) {
|
|
690
|
+
reasons.push(f.kind === "needs-events" ? `${formatFinding(f)} (needs event proofs)` : formatFinding(f));
|
|
691
|
+
}
|
|
692
|
+
if (findings.length > MAX_LISTED_FINDINGS) {
|
|
693
|
+
reasons.push(`…and ${findings.length - MAX_LISTED_FINDINGS} more handler findings`);
|
|
694
|
+
}
|
|
695
|
+
const writesMapInsert = Object.values(def.sources ?? {}).some((s) => s.type === "map_insert");
|
|
696
|
+
return {
|
|
697
|
+
level,
|
|
698
|
+
reasons,
|
|
699
|
+
unproven: reasons.length === 0 ? [
|
|
700
|
+
"tx attribution of writes: event.tx, _tx_id (needs event proofs)",
|
|
701
|
+
"intermediate writes within a block (needs event proofs)",
|
|
702
|
+
...writesMapInsert ? ["map_insert vs map_set (needs event proofs)"] : []
|
|
703
|
+
] : []
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
function isDeterminismViolation(v) {
|
|
707
|
+
return v.level === "state" && v.reasons.length > 0;
|
|
708
|
+
}
|
|
709
|
+
function sha256(input) {
|
|
710
|
+
return createHash2("sha256").update(input).digest("hex");
|
|
711
|
+
}
|
|
712
|
+
function computePin(input) {
|
|
713
|
+
const canonical = JSON.stringify({
|
|
714
|
+
handlerHash: sha256(input.handlerCode),
|
|
715
|
+
network: input.network,
|
|
716
|
+
runtime: input.runtime ?? SUBGRAPHS_RUNTIME,
|
|
717
|
+
schemaHash: input.schemaHash,
|
|
718
|
+
startBlock: input.startBlock ?? null
|
|
719
|
+
});
|
|
720
|
+
return sha256(canonical);
|
|
721
|
+
}
|
|
722
|
+
|
|
464
723
|
// src/schema/deployer.ts
|
|
465
724
|
function toJsonSafe(obj) {
|
|
466
725
|
return JSON.parse(JSON.stringify(obj, (_key, value) => typeof value === "bigint" ? value.toString() : value));
|
|
@@ -558,6 +817,13 @@ function renderDeployPlan(def, schemaName) {
|
|
|
558
817
|
async function deploySchema(db, def, handlerPath, opts) {
|
|
559
818
|
validateSubgraphDefinition(def);
|
|
560
819
|
const { statements, hash } = generateSubgraphSQL(def, opts?.schemaName);
|
|
820
|
+
const pin = opts?.handlerCode != null ? computePin({
|
|
821
|
+
schemaHash: hash,
|
|
822
|
+
handlerCode: opts.handlerCode,
|
|
823
|
+
startBlock: def.startBlock,
|
|
824
|
+
network: opts.network ?? process.env.NETWORK ?? "mainnet"
|
|
825
|
+
}) : null;
|
|
826
|
+
const identity = pin ? { pin } : {};
|
|
561
827
|
const { getSubgraph, registerSubgraph } = await import("@secondlayer/shared/db/queries/subgraphs");
|
|
562
828
|
const ddlDb = db;
|
|
563
829
|
const existing = await getSubgraph(db, def.name, opts?.accountId);
|
|
@@ -580,7 +846,9 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
580
846
|
handlerCode: opts?.handlerCode,
|
|
581
847
|
sourceCode: opts?.sourceCode,
|
|
582
848
|
schemaName,
|
|
583
|
-
startBlock: def.startBlock
|
|
849
|
+
startBlock: def.startBlock,
|
|
850
|
+
pin,
|
|
851
|
+
verification: opts?.verification ?? null
|
|
584
852
|
};
|
|
585
853
|
if (existing) {
|
|
586
854
|
const schemaExists = await sql`
|
|
@@ -594,19 +862,27 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
594
862
|
await sql.raw(stmt).execute(ddlDb);
|
|
595
863
|
}
|
|
596
864
|
const sg = await registerSubgraph(db, regData);
|
|
597
|
-
return {
|
|
865
|
+
return {
|
|
866
|
+
action: "reindexed",
|
|
867
|
+
subgraphId: sg.id,
|
|
868
|
+
version: newVersion,
|
|
869
|
+
...identity
|
|
870
|
+
};
|
|
598
871
|
}
|
|
599
872
|
if (existing.schema_hash === hash && !opts?.forceReindex) {
|
|
600
873
|
const handlerChanged = opts?.handlerCode != null && opts.handlerCode !== existing.handler_code;
|
|
601
874
|
const { updateSubgraphHandlerPath } = await import("@secondlayer/shared/db/queries/subgraphs");
|
|
602
875
|
await updateSubgraphHandlerPath(db, def.name, handlerPath, {
|
|
603
876
|
handlerCode: opts?.handlerCode,
|
|
604
|
-
sourceCode: opts?.sourceCode
|
|
877
|
+
sourceCode: opts?.sourceCode,
|
|
878
|
+
pin,
|
|
879
|
+
verification: opts?.verification ?? null
|
|
605
880
|
});
|
|
606
881
|
return {
|
|
607
882
|
action: handlerChanged ? "handler_updated" : "unchanged",
|
|
608
883
|
subgraphId: existing.id,
|
|
609
884
|
version: existing.version,
|
|
885
|
+
...identity,
|
|
610
886
|
...handlerChanged ? {
|
|
611
887
|
warnings: [
|
|
612
888
|
"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 +896,12 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
620
896
|
await sql.raw(stmt).execute(ddlDb);
|
|
621
897
|
}
|
|
622
898
|
const sg = await registerSubgraph(db, regData);
|
|
623
|
-
return {
|
|
899
|
+
return {
|
|
900
|
+
action: "reindexed",
|
|
901
|
+
subgraphId: sg.id,
|
|
902
|
+
version: newVersion,
|
|
903
|
+
...identity
|
|
904
|
+
};
|
|
624
905
|
}
|
|
625
906
|
if (existing.definition.schema) {
|
|
626
907
|
const diff = diffSchema(existing.definition.schema, def.schema);
|
|
@@ -636,6 +917,7 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
636
917
|
action: "reindexed",
|
|
637
918
|
subgraphId: sg.id,
|
|
638
919
|
version: newVersion,
|
|
920
|
+
...identity,
|
|
639
921
|
diff: deployDiff
|
|
640
922
|
};
|
|
641
923
|
}
|
|
@@ -755,6 +1037,7 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
755
1037
|
action: "updated",
|
|
756
1038
|
subgraphId: sg.id,
|
|
757
1039
|
version: newVersion,
|
|
1040
|
+
...identity,
|
|
758
1041
|
diff: deployDiff
|
|
759
1042
|
};
|
|
760
1043
|
}
|
|
@@ -763,7 +1046,12 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
763
1046
|
await sql.raw(stmt).execute(ddlDb);
|
|
764
1047
|
}
|
|
765
1048
|
const sg = await registerSubgraph(db, regData);
|
|
766
|
-
return {
|
|
1049
|
+
return {
|
|
1050
|
+
action: "created",
|
|
1051
|
+
subgraphId: sg.id,
|
|
1052
|
+
version: newVersion,
|
|
1053
|
+
...identity
|
|
1054
|
+
};
|
|
767
1055
|
}
|
|
768
1056
|
function getDefault(type) {
|
|
769
1057
|
switch (type) {
|
|
@@ -790,5 +1078,5 @@ export {
|
|
|
790
1078
|
generateSubgraphSQL
|
|
791
1079
|
};
|
|
792
1080
|
|
|
793
|
-
//# debugId=
|
|
1081
|
+
//# debugId=437B69A70A1DC76C64756E2164756E21
|
|
794
1082
|
//# sourceMappingURL=index.js.map
|