@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.
package/dist/index.js CHANGED
@@ -782,8 +782,491 @@ function createChainReadClient(ctx) {
782
782
  }
783
783
  };
784
784
  }
785
- // src/schema/generator.ts
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.5.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
+ "./verify": {
836
+ types: "./dist/verify/index.d.ts",
837
+ import: "./dist/verify/index.js"
838
+ }
839
+ },
840
+ files: ["dist"],
841
+ scripts: {
842
+ build: "bunup",
843
+ dev: "bunup --watch",
844
+ test: "bun test",
845
+ typecheck: "tsc --noEmit",
846
+ prepublishOnly: "bun run build"
847
+ },
848
+ dependencies: {
849
+ "@secondlayer/shared": "^11.23.0",
850
+ "@secondlayer/stacks": "^6.2.0",
851
+ "@secondlayer/verify": "^0.3.0",
852
+ esbuild: "^0.28.2",
853
+ kysely: "0.28.17",
854
+ zod: "^4.3.6"
855
+ },
856
+ devDependencies: {
857
+ "@types/bun": "latest",
858
+ bunup: "0.16.32",
859
+ typescript: "^5"
860
+ },
861
+ publishConfig: {
862
+ access: "public"
863
+ }
864
+ };
865
+
866
+ // src/verification.ts
867
+ var SUBGRAPHS_RUNTIME = `@secondlayer/subgraphs@${package_default.version}`;
868
+ var FORBIDDEN_GLOBALS = {
869
+ Date: "wall-clock time differs per run",
870
+ performance: "timers differ per run",
871
+ process: "host environment differs per run",
872
+ fetch: "network reads are not chain inputs",
873
+ crypto: "randomness differs per run",
874
+ setTimeout: "scheduling order differs per run",
875
+ setInterval: "scheduling order differs per run",
876
+ setImmediate: "scheduling order differs per run",
877
+ clearTimeout: "scheduling order differs per run",
878
+ clearInterval: "scheduling order differs per run",
879
+ queueMicrotask: "scheduling order differs per run",
880
+ WeakRef: "garbage collection timing differs per run",
881
+ FinalizationRegistry: "garbage collection timing differs per run",
882
+ Atomics: "shared memory is not single-threaded",
883
+ SharedArrayBuffer: "shared memory is not single-threaded",
884
+ Intl: "locale and time zone data differ per host",
885
+ Temporal: "locale and time zone data differ per host",
886
+ eval: "generated code hides inputs from the scan",
887
+ WebAssembly: "compiled code hides inputs from the scan",
888
+ ShadowRealm: "a nested realm escapes the allow-list",
889
+ require: "imports must be bundled into the handler"
890
+ };
891
+ var ALLOWED_GLOBALS = [
892
+ "undefined",
893
+ "NaN",
894
+ "Infinity",
895
+ "globalThis",
896
+ "isNaN",
897
+ "isFinite",
898
+ "parseInt",
899
+ "parseFloat",
900
+ "decodeURI",
901
+ "decodeURIComponent",
902
+ "encodeURI",
903
+ "encodeURIComponent",
904
+ "escape",
905
+ "unescape",
906
+ "Object",
907
+ "Function",
908
+ "Array",
909
+ "Number",
910
+ "Boolean",
911
+ "String",
912
+ "Symbol",
913
+ "BigInt",
914
+ "Math",
915
+ "JSON",
916
+ "Reflect",
917
+ "Proxy",
918
+ "Promise",
919
+ "Map",
920
+ "Set",
921
+ "WeakMap",
922
+ "WeakSet",
923
+ "RegExp",
924
+ "Iterator",
925
+ "DisposableStack",
926
+ "AsyncDisposableStack",
927
+ "Error",
928
+ "AggregateError",
929
+ "SuppressedError",
930
+ "EvalError",
931
+ "RangeError",
932
+ "ReferenceError",
933
+ "SyntaxError",
934
+ "TypeError",
935
+ "URIError",
936
+ "ArrayBuffer",
937
+ "DataView",
938
+ "Int8Array",
939
+ "Uint8Array",
940
+ "Uint8ClampedArray",
941
+ "Int16Array",
942
+ "Uint16Array",
943
+ "Int32Array",
944
+ "Uint32Array",
945
+ "Float16Array",
946
+ "Float32Array",
947
+ "Float64Array",
948
+ "BigInt64Array",
949
+ "BigUint64Array",
950
+ "TextEncoder",
951
+ "TextDecoder",
952
+ "atob",
953
+ "btoa",
954
+ "console"
955
+ ];
956
+ var ALLOWED_MATH = [
957
+ "abs",
958
+ "min",
959
+ "max",
960
+ "floor",
961
+ "ceil",
962
+ "trunc",
963
+ "sign",
964
+ "round",
965
+ "E",
966
+ "LN10",
967
+ "LN2",
968
+ "LOG10E",
969
+ "LOG2E",
970
+ "PI",
971
+ "SQRT1_2",
972
+ "SQRT2"
973
+ ];
974
+ var LOCALE_METHODS = [
975
+ "toLocaleString",
976
+ "toLocaleDateString",
977
+ "toLocaleTimeString",
978
+ "toLocaleUpperCase",
979
+ "toLocaleLowerCase",
980
+ "localeCompare"
981
+ ];
982
+ function formatFinding(f) {
983
+ return `${f.file}:${f.line}:${f.column} ${f.name}: ${f.reason}`;
984
+ }
985
+ var STATE_SOURCE_TYPES = new Set([
986
+ "var_set",
987
+ "map_set",
988
+ "map_delete"
989
+ ]);
990
+ var LEVEL_RANK = { state: 0, events: 1, none: 2 };
991
+ var MAX_LISTED_FINDINGS = 10;
992
+ function deriveVerification(def, findings = []) {
993
+ let level = "state";
994
+ const raise = (to) => {
995
+ if (LEVEL_RANK[to] > LEVEL_RANK[level])
996
+ level = to;
997
+ };
998
+ const reasons = [];
999
+ for (const [name, source] of Object.entries(def.sources ?? {})) {
1000
+ const filter = source;
1001
+ if (filter.trait) {
1002
+ raise("none");
1003
+ reasons.push(`source "${name}": trait scope needs a proven contract registry`);
1004
+ } else if (filter.type === "map_insert") {
1005
+ raise("events");
1006
+ reasons.push(`map_insert source "${name}": map_insert vs map_set needs event proofs`);
1007
+ } else if (!STATE_SOURCE_TYPES.has(filter.type)) {
1008
+ raise("events");
1009
+ reasons.push(`${filter.type} source "${name}" needs event proofs`);
1010
+ }
1011
+ }
1012
+ if (def.backfillMode === "concurrent") {
1013
+ raise("none");
1014
+ reasons.push("backfillMode concurrent: tip-first order is not chain order");
1015
+ }
1016
+ if (findings.some((f) => f.kind === "needs-events"))
1017
+ raise("events");
1018
+ for (const f of findings.slice(0, MAX_LISTED_FINDINGS)) {
1019
+ reasons.push(f.kind === "needs-events" ? `${formatFinding(f)} (needs event proofs)` : formatFinding(f));
1020
+ }
1021
+ if (findings.length > MAX_LISTED_FINDINGS) {
1022
+ reasons.push(`…and ${findings.length - MAX_LISTED_FINDINGS} more handler findings`);
1023
+ }
1024
+ return {
1025
+ level,
1026
+ reasons,
1027
+ unproven: reasons.length === 0 ? [
1028
+ "tx attribution of writes: event.tx, _tx_id (needs event proofs)",
1029
+ "intermediate writes within a block (needs event proofs)"
1030
+ ] : []
1031
+ };
1032
+ }
1033
+ function isDeterminismViolation(v) {
1034
+ return v.level === "state" && v.reasons.length > 0;
1035
+ }
1036
+ function sha256(input) {
1037
+ return createHash("sha256").update(input).digest("hex");
1038
+ }
1039
+ function pinPreimage(input) {
1040
+ const fields = {
1041
+ handlerHash: sha256(input.handlerCode),
1042
+ network: input.network,
1043
+ runtime: input.runtime ?? SUBGRAPHS_RUNTIME,
1044
+ schemaHash: input.schemaHash,
1045
+ startBlock: input.startBlock ?? null
1046
+ };
1047
+ return JSON.stringify(fields);
1048
+ }
1049
+ function computePin(input) {
1050
+ return sha256(pinPreimage(input));
1051
+ }
1052
+ // src/runtime/realm.ts
1053
+ import vm from "node:vm";
1054
+ class NondeterminismError extends Error {
1055
+ name = "NondeterminismError";
1056
+ }
1057
+
1058
+ class HandlerReadError extends Error {
1059
+ name = "HandlerReadError";
1060
+ }
1061
+ function abortsBlock(err) {
1062
+ return err instanceof NondeterminismError || err instanceof HandlerReadError;
1063
+ }
1064
+ var HOST_GLOBALS = [
1065
+ "ArrayBuffer",
1066
+ "DataView",
1067
+ "Int8Array",
1068
+ "Uint8Array",
1069
+ "Uint8ClampedArray",
1070
+ "Int16Array",
1071
+ "Uint16Array",
1072
+ "Int32Array",
1073
+ "Uint32Array",
1074
+ "Float16Array",
1075
+ "Float32Array",
1076
+ "Float64Array",
1077
+ "BigInt64Array",
1078
+ "BigUint64Array",
1079
+ "TextEncoder",
1080
+ "TextDecoder",
1081
+ "atob",
1082
+ "btoa"
1083
+ ];
1084
+ function forbid(name, reason) {
1085
+ return () => {
1086
+ throw new NondeterminismError(`${name}: ${reason}`);
1087
+ };
1088
+ }
1089
+ function createRealm() {
1090
+ const host = globalThis;
1091
+ const sandbox = Object.create(null);
1092
+ for (const name of HOST_GLOBALS) {
1093
+ if (host[name] !== undefined)
1094
+ sandbox[name] = host[name];
1095
+ }
1096
+ const context = vm.createContext(sandbox, {
1097
+ name: "subgraph-handler",
1098
+ codeGeneration: { strings: false, wasm: false }
1099
+ });
1100
+ const g = vm.runInContext("globalThis", context);
1101
+ const allowed = new Set(ALLOWED_GLOBALS);
1102
+ for (const name of Object.getOwnPropertyNames(g)) {
1103
+ if (!allowed.has(name))
1104
+ delete g[name];
1105
+ }
1106
+ for (const [name, reason] of Object.entries(FORBIDDEN_GLOBALS)) {
1107
+ Object.defineProperty(g, name, { get: forbid(name, reason) });
1108
+ }
1109
+ const realmMath = g.Math;
1110
+ const safeMath = {};
1111
+ const allowedMath = new Set(ALLOWED_MATH);
1112
+ for (const key of Object.getOwnPropertyNames(realmMath)) {
1113
+ if (allowedMath.has(key))
1114
+ safeMath[key] = realmMath[key];
1115
+ else
1116
+ Object.defineProperty(safeMath, key, {
1117
+ get: forbid(`Math.${key}`, key === "random" ? "randomness differs per run" : "result is engine-approximated")
1118
+ });
1119
+ }
1120
+ Object.defineProperty(g, "Math", { value: Object.freeze(safeMath) });
1121
+ for (const ctor of ["String", "Number", "BigInt", "Array", "Object"]) {
1122
+ const proto = g[ctor].prototype;
1123
+ for (const method of LOCALE_METHODS) {
1124
+ if (Object.prototype.hasOwnProperty.call(proto, method)) {
1125
+ Object.defineProperty(proto, method, {
1126
+ value: forbid(method, "output depends on the host's locale data")
1127
+ });
1128
+ }
1129
+ }
1130
+ }
1131
+ Object.preventExtensions(g);
1132
+ return context;
1133
+ }
1134
+ function checkRowValues(where, values) {
1135
+ for (const [col, value] of Object.entries(values)) {
1136
+ checkValue(value, `${where}.${col}`);
1137
+ }
1138
+ }
1139
+ function checkValue(value, path) {
1140
+ switch (typeof value) {
1141
+ case "number":
1142
+ if (!Number.isSafeInteger(value)) {
1143
+ throw new TypeError(`${path}: ${value} is not a safe integer; verifiable subgraphs store bigint or integers only`);
1144
+ }
1145
+ return;
1146
+ case "function":
1147
+ case "symbol":
1148
+ throw new TypeError(`${path}: a ${typeof value} is not a row value`);
1149
+ case "object":
1150
+ if (value === null || ArrayBuffer.isView(value))
1151
+ return;
1152
+ if (Array.isArray(value)) {
1153
+ value.forEach((v, i) => checkValue(v, `${path}[${i}]`));
1154
+ return;
1155
+ }
1156
+ for (const [k, v] of Object.entries(value))
1157
+ checkValue(v, `${path}.${k}`);
1158
+ return;
1159
+ default:
1160
+ return;
1161
+ }
1162
+ }
1163
+ function deterministicContext(ctx, schema) {
1164
+ let pendingReads = 0;
1165
+ const knownTable = (table) => {
1166
+ if (!Object.prototype.hasOwnProperty.call(schema, table)) {
1167
+ throw new Error(`Table "${table}" not found in subgraph schema`);
1168
+ }
1169
+ };
1170
+ const guard = (op) => {
1171
+ if (pendingReads > 0) {
1172
+ 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`);
1173
+ }
1174
+ };
1175
+ const read = async (op, run) => {
1176
+ guard(op);
1177
+ pendingReads++;
1178
+ try {
1179
+ return await run();
1180
+ } catch (err) {
1181
+ if (err instanceof NondeterminismError)
1182
+ throw err;
1183
+ throw new HandlerReadError(`ctx.${op} failed: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
1184
+ } finally {
1185
+ pendingReads--;
1186
+ }
1187
+ };
1188
+ return {
1189
+ get block() {
1190
+ return ctx.block;
1191
+ },
1192
+ get tx() {
1193
+ return ctx.tx;
1194
+ },
1195
+ get client() {
1196
+ throw new NondeterminismError("ctx.client: contract reads need re-execution proofs");
1197
+ },
1198
+ insert(table, row) {
1199
+ guard("insert");
1200
+ checkRowValues(`insert ${table}`, row);
1201
+ ctx.insert(table, row);
1202
+ },
1203
+ update(table, where, set) {
1204
+ guard("update");
1205
+ checkRowValues(`update ${table}`, where);
1206
+ checkRowValues(`update ${table}`, set);
1207
+ ctx.update(table, where, set);
1208
+ },
1209
+ upsert(table, key, row) {
1210
+ guard("upsert");
1211
+ checkRowValues(`upsert ${table}`, key);
1212
+ checkRowValues(`upsert ${table}`, row);
1213
+ ctx.upsert(table, key, row);
1214
+ },
1215
+ delete(table, where) {
1216
+ guard("delete");
1217
+ ctx.delete(table, where);
1218
+ },
1219
+ increment(table, key, deltas) {
1220
+ guard("increment");
1221
+ checkRowValues(`increment ${table}`, key);
1222
+ checkRowValues(`increment ${table}`, deltas);
1223
+ ctx.increment(table, key, deltas);
1224
+ },
1225
+ findOne: (table, where) => {
1226
+ knownTable(table);
1227
+ return read("findOne", () => ctx.findOne(table, where));
1228
+ },
1229
+ findMany: (table, ...rest) => {
1230
+ knownTable(table);
1231
+ return read("findMany", () => ctx.findMany(table, ...rest));
1232
+ }
1233
+ };
1234
+ }
1235
+ var realmDefinitions = new WeakSet;
1236
+ async function loadDeterministicDefinition(handlerCode) {
1237
+ const { transform } = await import("esbuild");
1238
+ const { code } = await transform(handlerCode, {
1239
+ format: "cjs",
1240
+ loader: "js",
1241
+ logLevel: "silent"
1242
+ });
1243
+ const context = createRealm();
1244
+ const evaluate = vm.runInContext(`(function (module, exports) {
1245
+ ${code}
1246
+ })`, context, { filename: "handler.js" });
1247
+ const mod = { exports: {} };
1248
+ evaluate(mod, mod.exports);
1249
+ const def = mod.exports.default ?? mod.exports;
1250
+ const sources = structuredClone(def.sources);
1251
+ const schema = structuredClone(def.schema);
1252
+ const facades = new WeakMap;
1253
+ const handlers = {};
1254
+ for (const [name, handler] of Object.entries(def.handlers ?? {})) {
1255
+ handlers[name] = (event, ctx) => {
1256
+ let facade = facades.get(ctx);
1257
+ if (!facade) {
1258
+ facade = deterministicContext(ctx, schema);
1259
+ facades.set(ctx, facade);
1260
+ }
1261
+ return handler(event, facade);
1262
+ };
1263
+ }
1264
+ const loaded = { ...def, sources, schema, handlers };
1265
+ realmDefinitions.add(loaded);
1266
+ return loaded;
1267
+ }
1268
+ // src/schema/generator.ts
1269
+ import { createHash as createHash2 } from "node:crypto";
787
1270
 
788
1271
  // src/schema/utils.ts
789
1272
  import { pgSchemaName } from "@secondlayer/shared/db/queries/subgraphs";
@@ -814,7 +1297,7 @@ var MAX_IDENT_BYTES = 63;
814
1297
  function safeIndexName(name) {
815
1298
  if (name.length <= MAX_IDENT_BYTES)
816
1299
  return name;
817
- const hash = createHash("sha256").update(name).digest("hex").slice(0, 8);
1300
+ const hash = createHash2("sha256").update(name).digest("hex").slice(0, 8);
818
1301
  return `${name.slice(0, MAX_IDENT_BYTES - hash.length - 1)}_${hash}`;
819
1302
  }
820
1303
  function emitIndexedColumnIndexDDL(schemaName, tableName, colName, opts) {
@@ -945,7 +1428,7 @@ function generateSubgraphSQL(def, schemaNameOverride) {
945
1428
  schema: def.schema,
946
1429
  sources: def.sources
947
1430
  }, (_key, value) => typeof value === "bigint" ? value.toString() : value);
948
- const hash = createHash("sha256").update(hashInput).digest("hex");
1431
+ const hash = createHash2("sha256").update(hashInput).digest("hex");
949
1432
  return { statements, hash };
950
1433
  }
951
1434
  // src/schema/prisma.ts
@@ -1532,6 +2015,15 @@ function renderDeployPlan(def, schemaName) {
1532
2015
  async function deploySchema(db, def, handlerPath, opts) {
1533
2016
  validateSubgraphDefinition(def);
1534
2017
  const { statements, hash } = generateSubgraphSQL(def, opts?.schemaName);
2018
+ const pinInput = opts?.handlerCode != null ? {
2019
+ schemaHash: hash,
2020
+ handlerCode: opts.handlerCode,
2021
+ startBlock: def.startBlock,
2022
+ network: opts.network ?? process.env.NETWORK ?? "mainnet"
2023
+ } : null;
2024
+ const pin = pinInput ? computePin(pinInput) : null;
2025
+ const preimage = pinInput ? pinPreimage(pinInput) : null;
2026
+ const identity = pin ? { pin } : {};
1535
2027
  const { getSubgraph, registerSubgraph } = await import("@secondlayer/shared/db/queries/subgraphs");
1536
2028
  const ddlDb = db;
1537
2029
  const existing = await getSubgraph(db, def.name, opts?.accountId);
@@ -1554,7 +2046,10 @@ async function deploySchema(db, def, handlerPath, opts) {
1554
2046
  handlerCode: opts?.handlerCode,
1555
2047
  sourceCode: opts?.sourceCode,
1556
2048
  schemaName,
1557
- startBlock: def.startBlock
2049
+ startBlock: def.startBlock,
2050
+ pin,
2051
+ pinPreimage: preimage,
2052
+ verification: opts?.verification ?? null
1558
2053
  };
1559
2054
  if (existing) {
1560
2055
  const schemaExists = await sql`
@@ -1568,19 +2063,28 @@ async function deploySchema(db, def, handlerPath, opts) {
1568
2063
  await sql.raw(stmt).execute(ddlDb);
1569
2064
  }
1570
2065
  const sg = await registerSubgraph(db, regData);
1571
- return { action: "reindexed", subgraphId: sg.id, version: newVersion };
2066
+ return {
2067
+ action: "reindexed",
2068
+ subgraphId: sg.id,
2069
+ version: newVersion,
2070
+ ...identity
2071
+ };
1572
2072
  }
1573
2073
  if (existing.schema_hash === hash && !opts?.forceReindex) {
1574
2074
  const handlerChanged = opts?.handlerCode != null && opts.handlerCode !== existing.handler_code;
1575
2075
  const { updateSubgraphHandlerPath } = await import("@secondlayer/shared/db/queries/subgraphs");
1576
2076
  await updateSubgraphHandlerPath(db, def.name, handlerPath, {
1577
2077
  handlerCode: opts?.handlerCode,
1578
- sourceCode: opts?.sourceCode
2078
+ sourceCode: opts?.sourceCode,
2079
+ pin,
2080
+ pinPreimage: preimage,
2081
+ verification: opts?.verification ?? null
1579
2082
  });
1580
2083
  return {
1581
2084
  action: handlerChanged ? "handler_updated" : "unchanged",
1582
2085
  subgraphId: existing.id,
1583
2086
  version: existing.version,
2087
+ ...identity,
1584
2088
  ...handlerChanged ? {
1585
2089
  warnings: [
1586
2090
  "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 +2098,12 @@ async function deploySchema(db, def, handlerPath, opts) {
1594
2098
  await sql.raw(stmt).execute(ddlDb);
1595
2099
  }
1596
2100
  const sg = await registerSubgraph(db, regData);
1597
- return { action: "reindexed", subgraphId: sg.id, version: newVersion };
2101
+ return {
2102
+ action: "reindexed",
2103
+ subgraphId: sg.id,
2104
+ version: newVersion,
2105
+ ...identity
2106
+ };
1598
2107
  }
1599
2108
  if (existing.definition.schema) {
1600
2109
  const diff = diffSchema(existing.definition.schema, def.schema);
@@ -1610,6 +2119,7 @@ async function deploySchema(db, def, handlerPath, opts) {
1610
2119
  action: "reindexed",
1611
2120
  subgraphId: sg.id,
1612
2121
  version: newVersion,
2122
+ ...identity,
1613
2123
  diff: deployDiff
1614
2124
  };
1615
2125
  }
@@ -1729,6 +2239,7 @@ async function deploySchema(db, def, handlerPath, opts) {
1729
2239
  action: "updated",
1730
2240
  subgraphId: sg.id,
1731
2241
  version: newVersion,
2242
+ ...identity,
1732
2243
  diff: deployDiff
1733
2244
  };
1734
2245
  }
@@ -1737,7 +2248,12 @@ async function deploySchema(db, def, handlerPath, opts) {
1737
2248
  await sql.raw(stmt).execute(ddlDb);
1738
2249
  }
1739
2250
  const sg = await registerSubgraph(db, regData);
1740
- return { action: "created", subgraphId: sg.id, version: newVersion };
2251
+ return {
2252
+ action: "created",
2253
+ subgraphId: sg.id,
2254
+ version: newVersion,
2255
+ ...identity
2256
+ };
1741
2257
  }
1742
2258
  function getDefault(type) {
1743
2259
  switch (type) {
@@ -1766,15 +2282,136 @@ import {
1766
2282
  import { logger as logger2 } from "@secondlayer/shared/logger";
1767
2283
 
1768
2284
  // src/runtime/batch-loader.ts
2285
+ import { sql as sql2 } from "kysely";
2286
+
2287
+ // src/runtime/state-writes.ts
2288
+ var DATA_MAP = "0";
2289
+ var VARIABLE = "1";
2290
+ var HEX = /^(?:[0-9a-f]{2})+$/;
2291
+ function parseStateWriteKey(key) {
2292
+ if (!key.startsWith("vm::"))
2293
+ return null;
2294
+ const parts = key.slice(4).split("::");
2295
+ const [contractId, store] = parts;
2296
+ if (!contractId?.includes("."))
2297
+ return null;
2298
+ if (store === DATA_MAP && parts.length === 4) {
2299
+ const [, , map, rawKey] = parts;
2300
+ if (!map || !rawKey || !HEX.test(rawKey))
2301
+ return null;
2302
+ return { contractId, kind: "map", map, rawKey };
2303
+ }
2304
+ if (store === VARIABLE && parts.length === 3 && parts[2]) {
2305
+ return { contractId, kind: "var", varName: parts[2] };
2306
+ }
2307
+ return null;
2308
+ }
2309
+ var utf8 = new TextDecoder("utf-8", { fatal: true });
2310
+ function storedValue(w) {
2311
+ const h = w.value_hex.startsWith("0x") ? w.value_hex.slice(2) : w.value_hex;
2312
+ const bytes = new Uint8Array(h.length / 2);
2313
+ for (let i = 0;i < bytes.length; i++) {
2314
+ bytes[i] = Number.parseInt(h.slice(i * 2, i * 2 + 2), 16);
2315
+ }
2316
+ let value;
2317
+ try {
2318
+ value = utf8.decode(bytes);
2319
+ } catch {
2320
+ throw new Error(`state write ${w.ordinal} value is not UTF-8 hex`);
2321
+ }
2322
+ value = value.toLowerCase();
2323
+ if (!HEX.test(value)) {
2324
+ throw new Error(`state write ${w.ordinal} value is not a Clarity value`);
2325
+ }
2326
+ return value;
2327
+ }
2328
+ function writeEvent(w) {
2329
+ const key = parseStateWriteKey(w.key);
2330
+ if (!key)
2331
+ return null;
2332
+ const value = storedValue(w);
2333
+ if (key.kind === "var") {
2334
+ return {
2335
+ type: "var_set",
2336
+ data: {
2337
+ contract_identifier: key.contractId,
2338
+ var_name: key.varName,
2339
+ raw_value: `0x${value}`
2340
+ }
2341
+ };
2342
+ }
2343
+ const base = {
2344
+ contract_identifier: key.contractId,
2345
+ map_name: key.map,
2346
+ raw_key: `0x${key.rawKey}`
2347
+ };
2348
+ if (value === "09")
2349
+ return { type: "map_delete", data: base };
2350
+ if (value.startsWith("0a")) {
2351
+ return {
2352
+ type: "map_set",
2353
+ data: { ...base, raw_value: `0x${value.slice(2)}` }
2354
+ };
2355
+ }
2356
+ throw new Error(`state write ${w.ordinal} map value is neither (some v) nor none`);
2357
+ }
2358
+ function stateWriteEvents(writes, txByIndex) {
2359
+ const txs = new Map;
2360
+ const vmEvents = [];
2361
+ for (const w of [...writes].sort((a, b) => a.ordinal - b.ordinal)) {
2362
+ const event = writeEvent(w);
2363
+ if (!event)
2364
+ continue;
2365
+ let tx = txs.get(w.tx_index);
2366
+ if (!tx) {
2367
+ if (w.tx_index === null) {
2368
+ tx = { tx_id: "", type: "block", sender: "", status: "success" };
2369
+ } else {
2370
+ const real = txByIndex.get(w.tx_index);
2371
+ if (!real) {
2372
+ throw new Error(`state write ${w.ordinal} names tx_index ${w.tx_index} the block does not have`);
2373
+ }
2374
+ tx = real;
2375
+ }
2376
+ tx = { ...tx, tx_index: 0 };
2377
+ txs.set(w.tx_index, tx);
2378
+ }
2379
+ vmEvents.push({
2380
+ id: vmEventId(tx.tx_id || "block", w.ordinal),
2381
+ tx_id: tx.tx_id,
2382
+ type: event.type,
2383
+ event_index: w.ordinal,
2384
+ data: event.data,
2385
+ clock: "vm"
2386
+ });
2387
+ }
2388
+ return { txs: [...txs.values()], vmEvents };
2389
+ }
2390
+
2391
+ // src/runtime/batch-loader.ts
2392
+ function stateWriteBlocks(writesByHeight, txsByHeight) {
2393
+ const out = new Map;
2394
+ for (const [height, writes] of writesByHeight) {
2395
+ const txByIndex = new Map;
2396
+ for (const tx of txsByHeight.get(height) ?? []) {
2397
+ if (tx.tx_index !== undefined)
2398
+ txByIndex.set(tx.tx_index, tx);
2399
+ }
2400
+ out.set(height, stateWriteEvents(writes, txByIndex));
2401
+ }
2402
+ return out;
2403
+ }
1769
2404
  function vmEventId(txId, vmEventIndex) {
1770
2405
  return `${txId}#vm:${vmEventIndex}`;
1771
2406
  }
1772
- async function loadBlockRange(db, fromHeight, toHeight) {
1773
- const [blocks, txs, events, vmRows] = await Promise.all([
2407
+ async function loadBlockRange(db, fromHeight, toHeight, opts = {}) {
2408
+ const feed = opts.stateWrites;
2409
+ const [blocks, txs, events, vmRows, writeRows] = await Promise.all([
1774
2410
  db.selectFrom("blocks").selectAll().where("height", ">=", fromHeight).where("height", "<=", toHeight).where("canonical", "=", true).execute(),
1775
2411
  db.selectFrom("transactions").selectAll().where("block_height", ">=", fromHeight).where("block_height", "<=", toHeight).execute(),
1776
2412
  db.selectFrom("events").selectAll().where("block_height", ">=", fromHeight).where("block_height", "<=", toHeight).execute(),
1777
- db.selectFrom("vm_events").selectAll().where("block_height", ">=", fromHeight).where("block_height", "<=", toHeight).orderBy("ordinal", "asc").execute()
2413
+ feed ? Promise.resolve([]) : db.selectFrom("vm_events").selectAll().where("block_height", ">=", fromHeight).where("block_height", "<=", toHeight).orderBy("ordinal", "asc").execute(),
2414
+ feed ? loadStateWrites(db, fromHeight, toHeight, feed) : Promise.resolve([])
1778
2415
  ]);
1779
2416
  const txsByHeight = new Map;
1780
2417
  for (const tx of txs) {
@@ -1806,18 +2443,42 @@ async function loadBlockRange(db, fromHeight, toHeight) {
1806
2443
  });
1807
2444
  vmByHeight.set(h, list);
1808
2445
  }
2446
+ const writesByHeight = new Map;
2447
+ for (const row of writeRows) {
2448
+ const h = Number(row.block_height);
2449
+ const list = writesByHeight.get(h) ?? [];
2450
+ list.push({
2451
+ ordinal: Number(row.ordinal),
2452
+ tx_index: row.tx_index === null ? null : Number(row.tx_index),
2453
+ key: row.key,
2454
+ value_hex: row.value_hex
2455
+ });
2456
+ writesByHeight.set(h, list);
2457
+ }
2458
+ const named = stateWriteBlocks(writesByHeight, txsByHeight);
1809
2459
  const result = new Map;
1810
2460
  for (const block of blocks) {
1811
2461
  const h = Number(block.height);
2462
+ const fed = named.get(h);
1812
2463
  result.set(h, {
1813
2464
  block,
1814
- txs: txsByHeight.get(h) ?? [],
2465
+ txs: feed ? fed?.txs ?? [] : txsByHeight.get(h) ?? [],
1815
2466
  events: eventsByHeight.get(h) ?? [],
1816
- vmEvents: vmByHeight.get(h) ?? []
2467
+ vmEvents: feed ? fed?.vmEvents ?? [] : vmByHeight.get(h) ?? []
1817
2468
  });
1818
2469
  }
1819
2470
  return result;
1820
2471
  }
2472
+ async function loadStateWrites(db, fromHeight, toHeight, feed) {
2473
+ const scope = feed.contracts ? sql2`AND (${sql2.join(feed.contracts.map((c) => sql2`starts_with(key, ${`vm::${c}::`})`), sql2` OR `)})` : sql2``;
2474
+ const { rows } = await sql2`
2475
+ SELECT block_height, ordinal, tx_index, key, value_hex
2476
+ FROM state_writes
2477
+ WHERE block_height >= ${fromHeight} AND block_height <= ${toHeight} ${scope}
2478
+ ORDER BY block_height, ordinal
2479
+ `.execute(db);
2480
+ return rows;
2481
+ }
1821
2482
 
1822
2483
  // src/runtime/observer-http-source.ts
1823
2484
  import {
@@ -2019,14 +2680,39 @@ function sparseProbeTargets(subgraph) {
2019
2680
  }
2020
2681
  return targets;
2021
2682
  }
2022
-
2683
+ var COVERAGE_TTL_MS = 60000;
2684
+ function cachedCoverage(read, ttlMs = COVERAGE_TTL_MS) {
2685
+ let cached;
2686
+ const coverage = () => {
2687
+ const now = Date.now();
2688
+ if (!cached || now - cached.at > ttlMs) {
2689
+ cached = { at: now, value: read().catch(() => null) };
2690
+ }
2691
+ return cached.value;
2692
+ };
2693
+ return Object.assign(coverage, {
2694
+ forget: () => {
2695
+ cached = undefined;
2696
+ }
2697
+ });
2698
+ }
2699
+ var dbStateWritesCoverage = cachedCoverage(async () => {
2700
+ const row = await getSourceDb2().selectFrom("state_writes").select("block_height").orderBy("block_height", "asc").limit(1).executeTakeFirst();
2701
+ return row ? Number(row.block_height) : null;
2702
+ });
2023
2703
  class PostgresBlockSource {
2704
+ feed;
2705
+ constructor(feed) {
2706
+ this.feed = feed;
2707
+ }
2024
2708
  async getTip() {
2025
2709
  const progress = await getSourceDb2().selectFrom("index_progress").selectAll().where("network", "=", process.env.NETWORK ?? "mainnet").executeTakeFirst();
2026
2710
  return progress ? Number(progress.highest_seen_block) : 0;
2027
2711
  }
2028
2712
  loadBlockRange(fromHeight, toHeight) {
2029
- return loadBlockRange(getSourceDb2(), fromHeight, toHeight);
2713
+ return loadBlockRange(getSourceDb2(), fromHeight, toHeight, {
2714
+ stateWrites: this.feed
2715
+ });
2030
2716
  }
2031
2717
  }
2032
2718
  var EVENT_FILTER_TO_INDEX_TYPE = {
@@ -2112,15 +2798,18 @@ class PublicApiBlockSource {
2112
2798
  eventTypes;
2113
2799
  probeTargets;
2114
2800
  needsTransactions;
2115
- constructor(http, eventTypes, probeTargets, needsTransactions = true) {
2801
+ feed;
2802
+ constructor(http, eventTypes, probeTargets, needsTransactions = true, feed) {
2116
2803
  this.http = http;
2117
2804
  this.eventTypes = eventTypes;
2118
2805
  this.probeTargets = probeTargets;
2119
2806
  this.needsTransactions = needsTransactions;
2807
+ this.feed = feed;
2120
2808
  }
2121
2809
  walkTargets() {
2122
2810
  const scoped = this.needsTransactions ? undefined : this.probeTargets;
2123
- return this.eventTypes.flatMap((eventType) => {
2811
+ const types = this.feed ? this.eventTypes.filter((t) => !VM_INDEX_EVENT_TYPES.has(t)) : this.eventTypes;
2812
+ return types.flatMap((eventType) => {
2124
2813
  const own = scoped?.filter((t) => t.eventType === eventType);
2125
2814
  return own?.length ? own : [{ eventType }];
2126
2815
  });
@@ -2143,10 +2832,11 @@ class PublicApiBlockSource {
2143
2832
  }
2144
2833
  async loadBlockRange(fromHeight, toHeight) {
2145
2834
  const withTx = !this.needsTransactions;
2146
- const [blocks, txRows, eventLists] = await Promise.all([
2835
+ const [blocks, txRows, eventLists, writes] = await Promise.all([
2147
2836
  this.http.walkBlocks(fromHeight, toHeight),
2148
2837
  this.needsTransactions ? this.http.walkTransactions(fromHeight, toHeight) : Promise.resolve([]),
2149
- Promise.all(this.walkTargets().map((t) => this.http.walkEvents(t.eventType, fromHeight, toHeight, withTx, t.contractId)))
2838
+ Promise.all(this.walkTargets().map((t) => this.http.walkEvents(t.eventType, fromHeight, toHeight, withTx, t.contractId))),
2839
+ this.feed ? this.walkWrites(fromHeight, toHeight, this.feed) : null
2150
2840
  ]);
2151
2841
  const events = dedupeEvents(eventLists.flat());
2152
2842
  const map = new Map;
@@ -2172,13 +2862,55 @@ class PublicApiBlockSource {
2172
2862
  bd.events.push(reconstructEvent(e));
2173
2863
  }
2174
2864
  }
2865
+ if (writes) {
2866
+ for (const [height, fed] of writes) {
2867
+ const bd = map.get(height);
2868
+ if (!bd)
2869
+ continue;
2870
+ bd.txs.push(...fed.txs);
2871
+ bd.vmEvents = fed.vmEvents;
2872
+ }
2873
+ }
2175
2874
  for (const bd of map.values()) {
2176
- bd.txs.sort((a, b) => a.tx_index - b.tx_index);
2875
+ bd.txs.sort((a, b) => (a.tx_index ?? 0) - (b.tx_index ?? 0));
2177
2876
  bd.events.sort((a, b) => a.event_index - b.event_index);
2178
2877
  bd.vmEvents?.sort((a, b) => a.event_index - b.event_index);
2179
2878
  }
2180
2879
  return map;
2181
2880
  }
2881
+ async walkWrites(fromHeight, toHeight, feed) {
2882
+ const walks = await Promise.all((feed.contracts ?? [undefined]).map((c) => this.http.walkStateWrites(fromHeight, toHeight, c)));
2883
+ const writesByHeight = new Map;
2884
+ const txsByHeight = new Map;
2885
+ for (const row of walks.flat()) {
2886
+ const writes = writesByHeight.get(row.block_height) ?? new Map;
2887
+ writes.set(row.ordinal, row);
2888
+ writesByHeight.set(row.block_height, writes);
2889
+ const tx = writeTx(row);
2890
+ if (tx) {
2891
+ const txs = txsByHeight.get(row.block_height) ?? new Map;
2892
+ txs.set(row.tx_index, tx);
2893
+ txsByHeight.set(row.block_height, txs);
2894
+ }
2895
+ }
2896
+ return stateWriteBlocks(new Map([...writesByHeight].map(([h, w]) => [
2897
+ h,
2898
+ [...w.values()].sort((a, b) => a.ordinal - b.ordinal)
2899
+ ])), new Map([...txsByHeight].map(([h, t]) => [h, [...t.values()]])));
2900
+ }
2901
+ }
2902
+ function writeTx(row) {
2903
+ if (row.tx_index === null || !row.tx_id)
2904
+ return null;
2905
+ return {
2906
+ tx_id: row.tx_id,
2907
+ tx_index: row.tx_index,
2908
+ type: row.tx_type ?? "",
2909
+ sender: row.tx_sender ?? "",
2910
+ status: row.tx_status ?? "",
2911
+ contract_id: row.tx_contract_id ?? null,
2912
+ function_name: row.tx_function_name ?? null
2913
+ };
2182
2914
  }
2183
2915
  var postgresBlockSource = new PostgresBlockSource;
2184
2916
  function buildHttpClient() {
@@ -2186,13 +2918,19 @@ function buildHttpClient() {
2186
2918
  }
2187
2919
  export {
2188
2920
  ChainReadError,
2921
+ HandlerReadError,
2922
+ NondeterminismError,
2923
+ SUBGRAPHS_RUNTIME,
2189
2924
  camelizeKeys,
2190
2925
  canSparseScan,
2926
+ computePin,
2191
2927
  defineSchema,
2192
2928
  defineSubgraph,
2193
2929
  deploySchema,
2930
+ deriveVerification,
2194
2931
  diffSchema,
2195
2932
  filterFieldsByType,
2933
+ formatFinding,
2196
2934
  generateDrizzleSchema,
2197
2935
  generateIndexSchema,
2198
2936
  generateKyselySchema,
@@ -2200,7 +2938,9 @@ export {
2200
2938
  generateSubgraphSQL,
2201
2939
  hasBreakingChanges,
2202
2940
  inferPrintTopics,
2941
+ isDeterminismViolation,
2203
2942
  isStreamsIndexEligible,
2943
+ loadDeterministicDefinition,
2204
2944
  pgSchemaName,
2205
2945
  readContractAt,
2206
2946
  renderDeployPlan,
@@ -2208,5 +2948,5 @@ export {
2208
2948
  validateSubgraphDefinition
2209
2949
  };
2210
2950
 
2211
- //# debugId=94E9A1B5DDFFF67E64756E2164756E21
2951
+ //# debugId=411D5E1C7A6D7DEB64756E2164756E21
2212
2952
  //# sourceMappingURL=index.js.map