@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/README.md +1 -0
- package/dist/index.d.ts +200 -119
- package/dist/index.js +761 -21
- package/dist/index.js.map +9 -6
- package/dist/runtime/emitter.js +1 -1
- package/dist/runtime/emitter.js.map +2 -2
- package/dist/runtime/replay.d.ts +32 -13
- package/dist/runtime/replay.js +723 -22
- package/dist/runtime/replay.js.map +8 -5
- package/dist/schema/index.d.ts +43 -36
- package/dist/schema/index.js +307 -6
- package/dist/schema/index.js.map +6 -5
- package/dist/testing/index.d.ts +105 -105
- package/dist/testing/index.js +867 -84
- package/dist/testing/index.js.map +9 -6
- package/dist/types.d.ts +31 -31
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/dist/validate.d.ts +27 -27
- package/dist/validate.js +1 -1
- package/dist/validate.js.map +2 -2
- package/dist/verification.d.ts +441 -0
- package/dist/verification.js +282 -0
- package/dist/verification.js.map +10 -0
- package/dist/verify/index.d.ts +475 -0
- package/dist/verify/index.js +3341 -0
- package/dist/verify/index.js.map +27 -0
- package/package.json +13 -3
package/dist/index.js
CHANGED
|
@@ -782,8 +782,491 @@ 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.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 =
|
|
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 =
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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=
|
|
2951
|
+
//# debugId=411D5E1C7A6D7DEB64756E2164756E21
|
|
2212
2952
|
//# sourceMappingURL=index.js.map
|