@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/index.js
CHANGED
|
@@ -782,8 +782,476 @@ function createChainReadClient(ctx) {
|
|
|
782
782
|
}
|
|
783
783
|
};
|
|
784
784
|
}
|
|
785
|
-
// src/
|
|
785
|
+
// src/verification.ts
|
|
786
786
|
import { createHash } from "node:crypto";
|
|
787
|
+
// package.json
|
|
788
|
+
var package_default = {
|
|
789
|
+
name: "@secondlayer/subgraphs",
|
|
790
|
+
version: "6.4.0",
|
|
791
|
+
description: "Describe the Stacks data you want as one TypeScript file; it fills itself and survives reorgs.",
|
|
792
|
+
license: "MIT",
|
|
793
|
+
repository: {
|
|
794
|
+
type: "git",
|
|
795
|
+
url: "git+https://github.com/ryanwaits/secondlayer.git",
|
|
796
|
+
directory: "packages/subgraphs"
|
|
797
|
+
},
|
|
798
|
+
homepage: "https://github.com/ryanwaits/secondlayer/tree/main/packages/subgraphs#readme",
|
|
799
|
+
type: "module",
|
|
800
|
+
main: "./dist/index.js",
|
|
801
|
+
types: "./dist/index.d.ts",
|
|
802
|
+
exports: {
|
|
803
|
+
".": {
|
|
804
|
+
types: "./dist/index.d.ts",
|
|
805
|
+
import: "./dist/index.js"
|
|
806
|
+
},
|
|
807
|
+
"./types": {
|
|
808
|
+
types: "./dist/types.d.ts",
|
|
809
|
+
import: "./dist/types.js"
|
|
810
|
+
},
|
|
811
|
+
"./schema": {
|
|
812
|
+
types: "./dist/schema/index.d.ts",
|
|
813
|
+
import: "./dist/schema/index.js"
|
|
814
|
+
},
|
|
815
|
+
"./validate": {
|
|
816
|
+
types: "./dist/validate.d.ts",
|
|
817
|
+
import: "./dist/validate.js"
|
|
818
|
+
},
|
|
819
|
+
"./verification": {
|
|
820
|
+
types: "./dist/verification.d.ts",
|
|
821
|
+
import: "./dist/verification.js"
|
|
822
|
+
},
|
|
823
|
+
"./runtime/replay": {
|
|
824
|
+
types: "./dist/runtime/replay.d.ts",
|
|
825
|
+
import: "./dist/runtime/replay.js"
|
|
826
|
+
},
|
|
827
|
+
"./runtime/emitter": {
|
|
828
|
+
types: "./dist/runtime/emitter.d.ts",
|
|
829
|
+
import: "./dist/runtime/emitter.js"
|
|
830
|
+
},
|
|
831
|
+
"./testing": {
|
|
832
|
+
types: "./dist/testing/index.d.ts",
|
|
833
|
+
import: "./dist/testing/index.js"
|
|
834
|
+
}
|
|
835
|
+
},
|
|
836
|
+
files: ["dist"],
|
|
837
|
+
scripts: {
|
|
838
|
+
build: "bunup",
|
|
839
|
+
dev: "bunup --watch",
|
|
840
|
+
test: "bun test",
|
|
841
|
+
typecheck: "tsc --noEmit",
|
|
842
|
+
prepublishOnly: "bun run build"
|
|
843
|
+
},
|
|
844
|
+
dependencies: {
|
|
845
|
+
"@secondlayer/shared": "^11.22.0",
|
|
846
|
+
"@secondlayer/stacks": "^6.1.0",
|
|
847
|
+
esbuild: "^0.28.2",
|
|
848
|
+
kysely: "0.28.17",
|
|
849
|
+
zod: "^4.3.6"
|
|
850
|
+
},
|
|
851
|
+
devDependencies: {
|
|
852
|
+
"@types/bun": "latest",
|
|
853
|
+
typescript: "^5"
|
|
854
|
+
},
|
|
855
|
+
publishConfig: {
|
|
856
|
+
access: "public"
|
|
857
|
+
}
|
|
858
|
+
};
|
|
859
|
+
|
|
860
|
+
// src/verification.ts
|
|
861
|
+
var SUBGRAPHS_RUNTIME = `@secondlayer/subgraphs@${package_default.version}`;
|
|
862
|
+
var FORBIDDEN_GLOBALS = {
|
|
863
|
+
Date: "wall-clock time differs per run",
|
|
864
|
+
performance: "timers differ per run",
|
|
865
|
+
process: "host environment differs per run",
|
|
866
|
+
fetch: "network reads are not chain inputs",
|
|
867
|
+
crypto: "randomness differs per run",
|
|
868
|
+
setTimeout: "scheduling order differs per run",
|
|
869
|
+
setInterval: "scheduling order differs per run",
|
|
870
|
+
setImmediate: "scheduling order differs per run",
|
|
871
|
+
clearTimeout: "scheduling order differs per run",
|
|
872
|
+
clearInterval: "scheduling order differs per run",
|
|
873
|
+
queueMicrotask: "scheduling order differs per run",
|
|
874
|
+
WeakRef: "garbage collection timing differs per run",
|
|
875
|
+
FinalizationRegistry: "garbage collection timing differs per run",
|
|
876
|
+
Atomics: "shared memory is not single-threaded",
|
|
877
|
+
SharedArrayBuffer: "shared memory is not single-threaded",
|
|
878
|
+
Intl: "locale and time zone data differ per host",
|
|
879
|
+
Temporal: "locale and time zone data differ per host",
|
|
880
|
+
eval: "generated code hides inputs from the scan",
|
|
881
|
+
WebAssembly: "compiled code hides inputs from the scan",
|
|
882
|
+
ShadowRealm: "a nested realm escapes the allow-list",
|
|
883
|
+
require: "imports must be bundled into the handler"
|
|
884
|
+
};
|
|
885
|
+
var ALLOWED_GLOBALS = [
|
|
886
|
+
"undefined",
|
|
887
|
+
"NaN",
|
|
888
|
+
"Infinity",
|
|
889
|
+
"globalThis",
|
|
890
|
+
"isNaN",
|
|
891
|
+
"isFinite",
|
|
892
|
+
"parseInt",
|
|
893
|
+
"parseFloat",
|
|
894
|
+
"decodeURI",
|
|
895
|
+
"decodeURIComponent",
|
|
896
|
+
"encodeURI",
|
|
897
|
+
"encodeURIComponent",
|
|
898
|
+
"escape",
|
|
899
|
+
"unescape",
|
|
900
|
+
"Object",
|
|
901
|
+
"Function",
|
|
902
|
+
"Array",
|
|
903
|
+
"Number",
|
|
904
|
+
"Boolean",
|
|
905
|
+
"String",
|
|
906
|
+
"Symbol",
|
|
907
|
+
"BigInt",
|
|
908
|
+
"Math",
|
|
909
|
+
"JSON",
|
|
910
|
+
"Reflect",
|
|
911
|
+
"Proxy",
|
|
912
|
+
"Promise",
|
|
913
|
+
"Map",
|
|
914
|
+
"Set",
|
|
915
|
+
"WeakMap",
|
|
916
|
+
"WeakSet",
|
|
917
|
+
"RegExp",
|
|
918
|
+
"Iterator",
|
|
919
|
+
"DisposableStack",
|
|
920
|
+
"AsyncDisposableStack",
|
|
921
|
+
"Error",
|
|
922
|
+
"AggregateError",
|
|
923
|
+
"SuppressedError",
|
|
924
|
+
"EvalError",
|
|
925
|
+
"RangeError",
|
|
926
|
+
"ReferenceError",
|
|
927
|
+
"SyntaxError",
|
|
928
|
+
"TypeError",
|
|
929
|
+
"URIError",
|
|
930
|
+
"ArrayBuffer",
|
|
931
|
+
"DataView",
|
|
932
|
+
"Int8Array",
|
|
933
|
+
"Uint8Array",
|
|
934
|
+
"Uint8ClampedArray",
|
|
935
|
+
"Int16Array",
|
|
936
|
+
"Uint16Array",
|
|
937
|
+
"Int32Array",
|
|
938
|
+
"Uint32Array",
|
|
939
|
+
"Float16Array",
|
|
940
|
+
"Float32Array",
|
|
941
|
+
"Float64Array",
|
|
942
|
+
"BigInt64Array",
|
|
943
|
+
"BigUint64Array",
|
|
944
|
+
"TextEncoder",
|
|
945
|
+
"TextDecoder",
|
|
946
|
+
"atob",
|
|
947
|
+
"btoa",
|
|
948
|
+
"console"
|
|
949
|
+
];
|
|
950
|
+
var ALLOWED_MATH = [
|
|
951
|
+
"abs",
|
|
952
|
+
"min",
|
|
953
|
+
"max",
|
|
954
|
+
"floor",
|
|
955
|
+
"ceil",
|
|
956
|
+
"trunc",
|
|
957
|
+
"sign",
|
|
958
|
+
"round",
|
|
959
|
+
"E",
|
|
960
|
+
"LN10",
|
|
961
|
+
"LN2",
|
|
962
|
+
"LOG10E",
|
|
963
|
+
"LOG2E",
|
|
964
|
+
"PI",
|
|
965
|
+
"SQRT1_2",
|
|
966
|
+
"SQRT2"
|
|
967
|
+
];
|
|
968
|
+
var LOCALE_METHODS = [
|
|
969
|
+
"toLocaleString",
|
|
970
|
+
"toLocaleDateString",
|
|
971
|
+
"toLocaleTimeString",
|
|
972
|
+
"toLocaleUpperCase",
|
|
973
|
+
"toLocaleLowerCase",
|
|
974
|
+
"localeCompare"
|
|
975
|
+
];
|
|
976
|
+
function formatFinding(f) {
|
|
977
|
+
return `${f.file}:${f.line}:${f.column} ${f.name}: ${f.reason}`;
|
|
978
|
+
}
|
|
979
|
+
var STATE_SOURCE_TYPES = new Set([
|
|
980
|
+
"var_set",
|
|
981
|
+
"map_set",
|
|
982
|
+
"map_insert",
|
|
983
|
+
"map_delete"
|
|
984
|
+
]);
|
|
985
|
+
var LEVEL_RANK = { state: 0, events: 1, none: 2 };
|
|
986
|
+
var MAX_LISTED_FINDINGS = 10;
|
|
987
|
+
function deriveVerification(def, findings = []) {
|
|
988
|
+
let level = "state";
|
|
989
|
+
const raise = (to) => {
|
|
990
|
+
if (LEVEL_RANK[to] > LEVEL_RANK[level])
|
|
991
|
+
level = to;
|
|
992
|
+
};
|
|
993
|
+
const reasons = [];
|
|
994
|
+
for (const [name, source] of Object.entries(def.sources ?? {})) {
|
|
995
|
+
const filter = source;
|
|
996
|
+
if (filter.trait) {
|
|
997
|
+
raise("none");
|
|
998
|
+
reasons.push(`source "${name}": trait scope needs a proven contract registry`);
|
|
999
|
+
} else if (!STATE_SOURCE_TYPES.has(filter.type)) {
|
|
1000
|
+
raise("events");
|
|
1001
|
+
reasons.push(`${filter.type} source "${name}" needs event proofs`);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
if (def.backfillMode === "concurrent") {
|
|
1005
|
+
raise("none");
|
|
1006
|
+
reasons.push("backfillMode concurrent: tip-first order is not chain order");
|
|
1007
|
+
}
|
|
1008
|
+
if (findings.some((f) => f.kind === "needs-events"))
|
|
1009
|
+
raise("events");
|
|
1010
|
+
for (const f of findings.slice(0, MAX_LISTED_FINDINGS)) {
|
|
1011
|
+
reasons.push(f.kind === "needs-events" ? `${formatFinding(f)} (needs event proofs)` : formatFinding(f));
|
|
1012
|
+
}
|
|
1013
|
+
if (findings.length > MAX_LISTED_FINDINGS) {
|
|
1014
|
+
reasons.push(`…and ${findings.length - MAX_LISTED_FINDINGS} more handler findings`);
|
|
1015
|
+
}
|
|
1016
|
+
const writesMapInsert = Object.values(def.sources ?? {}).some((s) => s.type === "map_insert");
|
|
1017
|
+
return {
|
|
1018
|
+
level,
|
|
1019
|
+
reasons,
|
|
1020
|
+
unproven: reasons.length === 0 ? [
|
|
1021
|
+
"tx attribution of writes: event.tx, _tx_id (needs event proofs)",
|
|
1022
|
+
"intermediate writes within a block (needs event proofs)",
|
|
1023
|
+
...writesMapInsert ? ["map_insert vs map_set (needs event proofs)"] : []
|
|
1024
|
+
] : []
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
function isDeterminismViolation(v) {
|
|
1028
|
+
return v.level === "state" && v.reasons.length > 0;
|
|
1029
|
+
}
|
|
1030
|
+
function sha256(input) {
|
|
1031
|
+
return createHash("sha256").update(input).digest("hex");
|
|
1032
|
+
}
|
|
1033
|
+
function computePin(input) {
|
|
1034
|
+
const canonical = JSON.stringify({
|
|
1035
|
+
handlerHash: sha256(input.handlerCode),
|
|
1036
|
+
network: input.network,
|
|
1037
|
+
runtime: input.runtime ?? SUBGRAPHS_RUNTIME,
|
|
1038
|
+
schemaHash: input.schemaHash,
|
|
1039
|
+
startBlock: input.startBlock ?? null
|
|
1040
|
+
});
|
|
1041
|
+
return sha256(canonical);
|
|
1042
|
+
}
|
|
1043
|
+
// src/runtime/realm.ts
|
|
1044
|
+
import vm from "node:vm";
|
|
1045
|
+
class NondeterminismError extends Error {
|
|
1046
|
+
name = "NondeterminismError";
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
class HandlerReadError extends Error {
|
|
1050
|
+
name = "HandlerReadError";
|
|
1051
|
+
}
|
|
1052
|
+
var HOST_GLOBALS = [
|
|
1053
|
+
"ArrayBuffer",
|
|
1054
|
+
"DataView",
|
|
1055
|
+
"Int8Array",
|
|
1056
|
+
"Uint8Array",
|
|
1057
|
+
"Uint8ClampedArray",
|
|
1058
|
+
"Int16Array",
|
|
1059
|
+
"Uint16Array",
|
|
1060
|
+
"Int32Array",
|
|
1061
|
+
"Uint32Array",
|
|
1062
|
+
"Float16Array",
|
|
1063
|
+
"Float32Array",
|
|
1064
|
+
"Float64Array",
|
|
1065
|
+
"BigInt64Array",
|
|
1066
|
+
"BigUint64Array",
|
|
1067
|
+
"TextEncoder",
|
|
1068
|
+
"TextDecoder",
|
|
1069
|
+
"atob",
|
|
1070
|
+
"btoa"
|
|
1071
|
+
];
|
|
1072
|
+
function forbid(name, reason) {
|
|
1073
|
+
return () => {
|
|
1074
|
+
throw new NondeterminismError(`${name}: ${reason}`);
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
function createRealm() {
|
|
1078
|
+
const host = globalThis;
|
|
1079
|
+
const sandbox = Object.create(null);
|
|
1080
|
+
for (const name of HOST_GLOBALS) {
|
|
1081
|
+
if (host[name] !== undefined)
|
|
1082
|
+
sandbox[name] = host[name];
|
|
1083
|
+
}
|
|
1084
|
+
const context = vm.createContext(sandbox, {
|
|
1085
|
+
name: "subgraph-handler",
|
|
1086
|
+
codeGeneration: { strings: false, wasm: false }
|
|
1087
|
+
});
|
|
1088
|
+
const g = vm.runInContext("globalThis", context);
|
|
1089
|
+
const allowed = new Set(ALLOWED_GLOBALS);
|
|
1090
|
+
for (const name of Object.getOwnPropertyNames(g)) {
|
|
1091
|
+
if (!allowed.has(name))
|
|
1092
|
+
delete g[name];
|
|
1093
|
+
}
|
|
1094
|
+
for (const [name, reason] of Object.entries(FORBIDDEN_GLOBALS)) {
|
|
1095
|
+
Object.defineProperty(g, name, { get: forbid(name, reason) });
|
|
1096
|
+
}
|
|
1097
|
+
const realmMath = g.Math;
|
|
1098
|
+
const safeMath = {};
|
|
1099
|
+
const allowedMath = new Set(ALLOWED_MATH);
|
|
1100
|
+
for (const key of Object.getOwnPropertyNames(realmMath)) {
|
|
1101
|
+
if (allowedMath.has(key))
|
|
1102
|
+
safeMath[key] = realmMath[key];
|
|
1103
|
+
else
|
|
1104
|
+
Object.defineProperty(safeMath, key, {
|
|
1105
|
+
get: forbid(`Math.${key}`, key === "random" ? "randomness differs per run" : "result is engine-approximated")
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
Object.defineProperty(g, "Math", { value: Object.freeze(safeMath) });
|
|
1109
|
+
for (const ctor of ["String", "Number", "BigInt", "Array", "Object"]) {
|
|
1110
|
+
const proto = g[ctor].prototype;
|
|
1111
|
+
for (const method of LOCALE_METHODS) {
|
|
1112
|
+
if (Object.prototype.hasOwnProperty.call(proto, method)) {
|
|
1113
|
+
Object.defineProperty(proto, method, {
|
|
1114
|
+
value: forbid(method, "output depends on the host's locale data")
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
Object.preventExtensions(g);
|
|
1120
|
+
return context;
|
|
1121
|
+
}
|
|
1122
|
+
function checkRowValues(where, values) {
|
|
1123
|
+
for (const [col, value] of Object.entries(values)) {
|
|
1124
|
+
checkValue(value, `${where}.${col}`);
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
function checkValue(value, path) {
|
|
1128
|
+
switch (typeof value) {
|
|
1129
|
+
case "number":
|
|
1130
|
+
if (!Number.isSafeInteger(value)) {
|
|
1131
|
+
throw new TypeError(`${path}: ${value} is not a safe integer; verifiable subgraphs store bigint or integers only`);
|
|
1132
|
+
}
|
|
1133
|
+
return;
|
|
1134
|
+
case "function":
|
|
1135
|
+
case "symbol":
|
|
1136
|
+
throw new TypeError(`${path}: a ${typeof value} is not a row value`);
|
|
1137
|
+
case "object":
|
|
1138
|
+
if (value === null || ArrayBuffer.isView(value))
|
|
1139
|
+
return;
|
|
1140
|
+
if (Array.isArray(value)) {
|
|
1141
|
+
value.forEach((v, i) => checkValue(v, `${path}[${i}]`));
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
for (const [k, v] of Object.entries(value))
|
|
1145
|
+
checkValue(v, `${path}.${k}`);
|
|
1146
|
+
return;
|
|
1147
|
+
default:
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
function deterministicContext(ctx, schema) {
|
|
1152
|
+
let pendingReads = 0;
|
|
1153
|
+
const knownTable = (table) => {
|
|
1154
|
+
if (!Object.prototype.hasOwnProperty.call(schema, table)) {
|
|
1155
|
+
throw new Error(`Table "${table}" not found in subgraph schema`);
|
|
1156
|
+
}
|
|
1157
|
+
};
|
|
1158
|
+
const guard = (op) => {
|
|
1159
|
+
if (pendingReads > 0) {
|
|
1160
|
+
throw new NondeterminismError(`ctx.${op} while a ctx read is pending: concurrent ctx calls order writes by database latency; await each read before the next call`);
|
|
1161
|
+
}
|
|
1162
|
+
};
|
|
1163
|
+
const read = async (op, run) => {
|
|
1164
|
+
guard(op);
|
|
1165
|
+
pendingReads++;
|
|
1166
|
+
try {
|
|
1167
|
+
return await run();
|
|
1168
|
+
} catch (err) {
|
|
1169
|
+
if (err instanceof NondeterminismError)
|
|
1170
|
+
throw err;
|
|
1171
|
+
throw new HandlerReadError(`ctx.${op} failed: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
1172
|
+
} finally {
|
|
1173
|
+
pendingReads--;
|
|
1174
|
+
}
|
|
1175
|
+
};
|
|
1176
|
+
return {
|
|
1177
|
+
get block() {
|
|
1178
|
+
return ctx.block;
|
|
1179
|
+
},
|
|
1180
|
+
get tx() {
|
|
1181
|
+
return ctx.tx;
|
|
1182
|
+
},
|
|
1183
|
+
get client() {
|
|
1184
|
+
throw new NondeterminismError("ctx.client: contract reads need re-execution proofs");
|
|
1185
|
+
},
|
|
1186
|
+
insert(table, row) {
|
|
1187
|
+
guard("insert");
|
|
1188
|
+
checkRowValues(`insert ${table}`, row);
|
|
1189
|
+
ctx.insert(table, row);
|
|
1190
|
+
},
|
|
1191
|
+
update(table, where, set) {
|
|
1192
|
+
guard("update");
|
|
1193
|
+
checkRowValues(`update ${table}`, where);
|
|
1194
|
+
checkRowValues(`update ${table}`, set);
|
|
1195
|
+
ctx.update(table, where, set);
|
|
1196
|
+
},
|
|
1197
|
+
upsert(table, key, row) {
|
|
1198
|
+
guard("upsert");
|
|
1199
|
+
checkRowValues(`upsert ${table}`, key);
|
|
1200
|
+
checkRowValues(`upsert ${table}`, row);
|
|
1201
|
+
ctx.upsert(table, key, row);
|
|
1202
|
+
},
|
|
1203
|
+
delete(table, where) {
|
|
1204
|
+
guard("delete");
|
|
1205
|
+
ctx.delete(table, where);
|
|
1206
|
+
},
|
|
1207
|
+
increment(table, key, deltas) {
|
|
1208
|
+
guard("increment");
|
|
1209
|
+
checkRowValues(`increment ${table}`, key);
|
|
1210
|
+
checkRowValues(`increment ${table}`, deltas);
|
|
1211
|
+
ctx.increment(table, key, deltas);
|
|
1212
|
+
},
|
|
1213
|
+
findOne: (table, where) => {
|
|
1214
|
+
knownTable(table);
|
|
1215
|
+
return read("findOne", () => ctx.findOne(table, where));
|
|
1216
|
+
},
|
|
1217
|
+
findMany: (table, ...rest) => {
|
|
1218
|
+
knownTable(table);
|
|
1219
|
+
return read("findMany", () => ctx.findMany(table, ...rest));
|
|
1220
|
+
}
|
|
1221
|
+
};
|
|
1222
|
+
}
|
|
1223
|
+
async function loadDeterministicDefinition(handlerCode) {
|
|
1224
|
+
const { transform } = await import("esbuild");
|
|
1225
|
+
const { code } = await transform(handlerCode, {
|
|
1226
|
+
format: "cjs",
|
|
1227
|
+
loader: "js",
|
|
1228
|
+
logLevel: "silent"
|
|
1229
|
+
});
|
|
1230
|
+
const context = createRealm();
|
|
1231
|
+
const evaluate = vm.runInContext(`(function (module, exports) {
|
|
1232
|
+
${code}
|
|
1233
|
+
})`, context, { filename: "handler.js" });
|
|
1234
|
+
const mod = { exports: {} };
|
|
1235
|
+
evaluate(mod, mod.exports);
|
|
1236
|
+
const def = mod.exports.default ?? mod.exports;
|
|
1237
|
+
const sources = structuredClone(def.sources);
|
|
1238
|
+
const schema = structuredClone(def.schema);
|
|
1239
|
+
const facades = new WeakMap;
|
|
1240
|
+
const handlers = {};
|
|
1241
|
+
for (const [name, handler] of Object.entries(def.handlers ?? {})) {
|
|
1242
|
+
handlers[name] = (event, ctx) => {
|
|
1243
|
+
let facade = facades.get(ctx);
|
|
1244
|
+
if (!facade) {
|
|
1245
|
+
facade = deterministicContext(ctx, schema);
|
|
1246
|
+
facades.set(ctx, facade);
|
|
1247
|
+
}
|
|
1248
|
+
return handler(event, facade);
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
1251
|
+
return { ...def, sources, schema, handlers };
|
|
1252
|
+
}
|
|
1253
|
+
// src/schema/generator.ts
|
|
1254
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
787
1255
|
|
|
788
1256
|
// src/schema/utils.ts
|
|
789
1257
|
import { pgSchemaName } from "@secondlayer/shared/db/queries/subgraphs";
|
|
@@ -814,7 +1282,7 @@ var MAX_IDENT_BYTES = 63;
|
|
|
814
1282
|
function safeIndexName(name) {
|
|
815
1283
|
if (name.length <= MAX_IDENT_BYTES)
|
|
816
1284
|
return name;
|
|
817
|
-
const hash =
|
|
1285
|
+
const hash = createHash2("sha256").update(name).digest("hex").slice(0, 8);
|
|
818
1286
|
return `${name.slice(0, MAX_IDENT_BYTES - hash.length - 1)}_${hash}`;
|
|
819
1287
|
}
|
|
820
1288
|
function emitIndexedColumnIndexDDL(schemaName, tableName, colName, opts) {
|
|
@@ -945,7 +1413,7 @@ function generateSubgraphSQL(def, schemaNameOverride) {
|
|
|
945
1413
|
schema: def.schema,
|
|
946
1414
|
sources: def.sources
|
|
947
1415
|
}, (_key, value) => typeof value === "bigint" ? value.toString() : value);
|
|
948
|
-
const hash =
|
|
1416
|
+
const hash = createHash2("sha256").update(hashInput).digest("hex");
|
|
949
1417
|
return { statements, hash };
|
|
950
1418
|
}
|
|
951
1419
|
// src/schema/prisma.ts
|
|
@@ -1532,6 +2000,13 @@ function renderDeployPlan(def, schemaName) {
|
|
|
1532
2000
|
async function deploySchema(db, def, handlerPath, opts) {
|
|
1533
2001
|
validateSubgraphDefinition(def);
|
|
1534
2002
|
const { statements, hash } = generateSubgraphSQL(def, opts?.schemaName);
|
|
2003
|
+
const pin = opts?.handlerCode != null ? computePin({
|
|
2004
|
+
schemaHash: hash,
|
|
2005
|
+
handlerCode: opts.handlerCode,
|
|
2006
|
+
startBlock: def.startBlock,
|
|
2007
|
+
network: opts.network ?? process.env.NETWORK ?? "mainnet"
|
|
2008
|
+
}) : null;
|
|
2009
|
+
const identity = pin ? { pin } : {};
|
|
1535
2010
|
const { getSubgraph, registerSubgraph } = await import("@secondlayer/shared/db/queries/subgraphs");
|
|
1536
2011
|
const ddlDb = db;
|
|
1537
2012
|
const existing = await getSubgraph(db, def.name, opts?.accountId);
|
|
@@ -1554,7 +2029,9 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
1554
2029
|
handlerCode: opts?.handlerCode,
|
|
1555
2030
|
sourceCode: opts?.sourceCode,
|
|
1556
2031
|
schemaName,
|
|
1557
|
-
startBlock: def.startBlock
|
|
2032
|
+
startBlock: def.startBlock,
|
|
2033
|
+
pin,
|
|
2034
|
+
verification: opts?.verification ?? null
|
|
1558
2035
|
};
|
|
1559
2036
|
if (existing) {
|
|
1560
2037
|
const schemaExists = await sql`
|
|
@@ -1568,19 +2045,27 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
1568
2045
|
await sql.raw(stmt).execute(ddlDb);
|
|
1569
2046
|
}
|
|
1570
2047
|
const sg = await registerSubgraph(db, regData);
|
|
1571
|
-
return {
|
|
2048
|
+
return {
|
|
2049
|
+
action: "reindexed",
|
|
2050
|
+
subgraphId: sg.id,
|
|
2051
|
+
version: newVersion,
|
|
2052
|
+
...identity
|
|
2053
|
+
};
|
|
1572
2054
|
}
|
|
1573
2055
|
if (existing.schema_hash === hash && !opts?.forceReindex) {
|
|
1574
2056
|
const handlerChanged = opts?.handlerCode != null && opts.handlerCode !== existing.handler_code;
|
|
1575
2057
|
const { updateSubgraphHandlerPath } = await import("@secondlayer/shared/db/queries/subgraphs");
|
|
1576
2058
|
await updateSubgraphHandlerPath(db, def.name, handlerPath, {
|
|
1577
2059
|
handlerCode: opts?.handlerCode,
|
|
1578
|
-
sourceCode: opts?.sourceCode
|
|
2060
|
+
sourceCode: opts?.sourceCode,
|
|
2061
|
+
pin,
|
|
2062
|
+
verification: opts?.verification ?? null
|
|
1579
2063
|
});
|
|
1580
2064
|
return {
|
|
1581
2065
|
action: handlerChanged ? "handler_updated" : "unchanged",
|
|
1582
2066
|
subgraphId: existing.id,
|
|
1583
2067
|
version: existing.version,
|
|
2068
|
+
...identity,
|
|
1584
2069
|
...handlerChanged ? {
|
|
1585
2070
|
warnings: [
|
|
1586
2071
|
"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)."
|
|
@@ -1594,7 +2079,12 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
1594
2079
|
await sql.raw(stmt).execute(ddlDb);
|
|
1595
2080
|
}
|
|
1596
2081
|
const sg = await registerSubgraph(db, regData);
|
|
1597
|
-
return {
|
|
2082
|
+
return {
|
|
2083
|
+
action: "reindexed",
|
|
2084
|
+
subgraphId: sg.id,
|
|
2085
|
+
version: newVersion,
|
|
2086
|
+
...identity
|
|
2087
|
+
};
|
|
1598
2088
|
}
|
|
1599
2089
|
if (existing.definition.schema) {
|
|
1600
2090
|
const diff = diffSchema(existing.definition.schema, def.schema);
|
|
@@ -1610,6 +2100,7 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
1610
2100
|
action: "reindexed",
|
|
1611
2101
|
subgraphId: sg.id,
|
|
1612
2102
|
version: newVersion,
|
|
2103
|
+
...identity,
|
|
1613
2104
|
diff: deployDiff
|
|
1614
2105
|
};
|
|
1615
2106
|
}
|
|
@@ -1729,6 +2220,7 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
1729
2220
|
action: "updated",
|
|
1730
2221
|
subgraphId: sg.id,
|
|
1731
2222
|
version: newVersion,
|
|
2223
|
+
...identity,
|
|
1732
2224
|
diff: deployDiff
|
|
1733
2225
|
};
|
|
1734
2226
|
}
|
|
@@ -1737,7 +2229,12 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
1737
2229
|
await sql.raw(stmt).execute(ddlDb);
|
|
1738
2230
|
}
|
|
1739
2231
|
const sg = await registerSubgraph(db, regData);
|
|
1740
|
-
return {
|
|
2232
|
+
return {
|
|
2233
|
+
action: "created",
|
|
2234
|
+
subgraphId: sg.id,
|
|
2235
|
+
version: newVersion,
|
|
2236
|
+
...identity
|
|
2237
|
+
};
|
|
1741
2238
|
}
|
|
1742
2239
|
function getDefault(type) {
|
|
1743
2240
|
switch (type) {
|
|
@@ -2186,13 +2683,19 @@ function buildHttpClient() {
|
|
|
2186
2683
|
}
|
|
2187
2684
|
export {
|
|
2188
2685
|
ChainReadError,
|
|
2686
|
+
HandlerReadError,
|
|
2687
|
+
NondeterminismError,
|
|
2688
|
+
SUBGRAPHS_RUNTIME,
|
|
2189
2689
|
camelizeKeys,
|
|
2190
2690
|
canSparseScan,
|
|
2691
|
+
computePin,
|
|
2191
2692
|
defineSchema,
|
|
2192
2693
|
defineSubgraph,
|
|
2193
2694
|
deploySchema,
|
|
2695
|
+
deriveVerification,
|
|
2194
2696
|
diffSchema,
|
|
2195
2697
|
filterFieldsByType,
|
|
2698
|
+
formatFinding,
|
|
2196
2699
|
generateDrizzleSchema,
|
|
2197
2700
|
generateIndexSchema,
|
|
2198
2701
|
generateKyselySchema,
|
|
@@ -2200,7 +2703,9 @@ export {
|
|
|
2200
2703
|
generateSubgraphSQL,
|
|
2201
2704
|
hasBreakingChanges,
|
|
2202
2705
|
inferPrintTopics,
|
|
2706
|
+
isDeterminismViolation,
|
|
2203
2707
|
isStreamsIndexEligible,
|
|
2708
|
+
loadDeterministicDefinition,
|
|
2204
2709
|
pgSchemaName,
|
|
2205
2710
|
readContractAt,
|
|
2206
2711
|
renderDeployPlan,
|
|
@@ -2208,5 +2713,5 @@ export {
|
|
|
2208
2713
|
validateSubgraphDefinition
|
|
2209
2714
|
};
|
|
2210
2715
|
|
|
2211
|
-
//# debugId=
|
|
2716
|
+
//# debugId=BDCC478FBE9DE96064756E2164756E21
|
|
2212
2717
|
//# sourceMappingURL=index.js.map
|