@secondlayer/subgraphs 4.1.4 → 4.2.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/src/index.d.ts +91 -31
- package/dist/src/index.js +3319 -3067
- package/dist/src/index.js.map +11 -9
- package/dist/src/runtime/block-processor.d.ts +72 -23
- package/dist/src/runtime/block-processor.js +867 -400
- package/dist/src/runtime/block-processor.js.map +9 -6
- package/dist/src/runtime/catchup.d.ts +72 -23
- package/dist/src/runtime/catchup.js +878 -411
- package/dist/src/runtime/catchup.js.map +9 -6
- package/dist/src/runtime/processor.js +943 -475
- package/dist/src/runtime/processor.js.map +10 -7
- package/dist/src/runtime/reindex.d.ts +72 -23
- package/dist/src/runtime/reindex.js +911 -444
- package/dist/src/runtime/reindex.js.map +9 -6
- package/dist/src/runtime/reorg.d.ts +72 -23
- package/dist/src/runtime/reorg.js +890 -422
- package/dist/src/runtime/reorg.js.map +10 -7
- package/dist/src/runtime/replay.js +12 -6
- package/dist/src/runtime/replay.js.map +4 -4
- package/dist/src/runtime/runner.d.ts +72 -23
- package/dist/src/runtime/runner.js +757 -28
- package/dist/src/runtime/runner.js.map +9 -5
- package/dist/src/runtime/source-matcher.d.ts +80 -24
- package/dist/src/runtime/source-matcher.js +12 -5
- package/dist/src/runtime/source-matcher.js.map +3 -3
- package/dist/src/schema/index.d.ts +72 -23
- package/dist/src/schema/index.js +115 -1
- package/dist/src/schema/index.js.map +3 -3
- package/dist/src/service.js +983 -515
- package/dist/src/service.js.map +10 -7
- package/dist/src/testing/index.d.ts +195 -24
- package/dist/src/testing/index.js +1049 -30
- package/dist/src/testing/index.js.map +10 -5
- package/dist/src/triggers/index.d.ts +83 -24
- package/dist/src/triggers/index.js +5 -2
- package/dist/src/triggers/index.js.map +3 -3
- package/dist/src/types.d.ts +73 -24
- package/dist/src/validate.d.ts +72 -23
- package/dist/src/validate.js +115 -1
- package/dist/src/validate.js.map +3 -3
- package/package.json +2 -2
|
@@ -942,15 +942,679 @@ function decodeFunctionArgs(args) {
|
|
|
942
942
|
return args.map(decodeClarityValue);
|
|
943
943
|
}
|
|
944
944
|
|
|
945
|
+
// src/runtime/source-matcher.ts
|
|
946
|
+
var patternCache = new Map;
|
|
947
|
+
function matchAny(value, pattern) {
|
|
948
|
+
return Array.isArray(pattern) ? pattern.some((p) => matchPattern(value, p)) : matchPattern(value, pattern);
|
|
949
|
+
}
|
|
950
|
+
function matchPattern(value, pattern) {
|
|
951
|
+
if (!pattern.includes("*"))
|
|
952
|
+
return value === pattern;
|
|
953
|
+
let re = patternCache.get(pattern);
|
|
954
|
+
if (!re) {
|
|
955
|
+
const regex = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
956
|
+
re = new RegExp(`^${regex}$`);
|
|
957
|
+
patternCache.set(pattern, re);
|
|
958
|
+
}
|
|
959
|
+
return re.test(value);
|
|
960
|
+
}
|
|
961
|
+
var EMPTY_SET = new Set;
|
|
962
|
+
function factoryAllows(filter, contractId, factoryContracts) {
|
|
963
|
+
if (!filter.factory)
|
|
964
|
+
return true;
|
|
965
|
+
if (!contractId)
|
|
966
|
+
return false;
|
|
967
|
+
return (factoryContracts.get(filter.factory.from) ?? EMPTY_SET).has(contractId);
|
|
968
|
+
}
|
|
969
|
+
function readPath(payload, path) {
|
|
970
|
+
let cur = payload;
|
|
971
|
+
for (const key of path.split(".")) {
|
|
972
|
+
if (cur === null || typeof cur !== "object")
|
|
973
|
+
return;
|
|
974
|
+
cur = cur[key];
|
|
975
|
+
}
|
|
976
|
+
return cur;
|
|
977
|
+
}
|
|
978
|
+
function traitAllows(filter, contractId, traitContracts) {
|
|
979
|
+
const trait = filter.trait;
|
|
980
|
+
if (!trait)
|
|
981
|
+
return true;
|
|
982
|
+
if (!contractId)
|
|
983
|
+
return false;
|
|
984
|
+
return (traitContracts.get(trait) ?? EMPTY_SET).has(contractId);
|
|
985
|
+
}
|
|
986
|
+
function assetContract(assetId) {
|
|
987
|
+
return assetId?.split("::")[0];
|
|
988
|
+
}
|
|
989
|
+
function printContractId(data) {
|
|
990
|
+
if (!data)
|
|
991
|
+
return;
|
|
992
|
+
const id = (typeof data.contract_identifier === "string" ? data.contract_identifier : undefined) ?? (typeof data.contract_id === "string" ? data.contract_id : undefined);
|
|
993
|
+
return id || undefined;
|
|
994
|
+
}
|
|
995
|
+
function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts = new Map) {
|
|
996
|
+
const results = [];
|
|
997
|
+
switch (filter.type) {
|
|
998
|
+
case "stx_transfer":
|
|
999
|
+
case "stx_mint":
|
|
1000
|
+
case "stx_burn":
|
|
1001
|
+
case "stx_lock": {
|
|
1002
|
+
const eventType = `${filter.type}_event`;
|
|
1003
|
+
for (const tx of transactions) {
|
|
1004
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
1005
|
+
const matched = txEvents.filter((e) => e.type === eventType);
|
|
1006
|
+
if (matched.length === 0)
|
|
1007
|
+
continue;
|
|
1008
|
+
const filtered = matched.filter((e) => {
|
|
1009
|
+
const data = e.data;
|
|
1010
|
+
if (!data)
|
|
1011
|
+
return false;
|
|
1012
|
+
if ("sender" in filter && filter.sender) {
|
|
1013
|
+
if (!matchPattern(data.sender, filter.sender))
|
|
1014
|
+
return false;
|
|
1015
|
+
}
|
|
1016
|
+
if ("recipient" in filter && filter.recipient) {
|
|
1017
|
+
if (!matchPattern(data.recipient, filter.recipient))
|
|
1018
|
+
return false;
|
|
1019
|
+
}
|
|
1020
|
+
if ("lockedAddress" in filter && filter.lockedAddress) {
|
|
1021
|
+
if (!matchPattern(data.locked_address, filter.lockedAddress))
|
|
1022
|
+
return false;
|
|
1023
|
+
}
|
|
1024
|
+
if ("minAmount" in filter && filter.minAmount !== undefined) {
|
|
1025
|
+
const amount = BigInt(data.amount ?? data.locked_amount ?? "0");
|
|
1026
|
+
if (amount < filter.minAmount)
|
|
1027
|
+
return false;
|
|
1028
|
+
}
|
|
1029
|
+
if ("maxAmount" in filter && filter.maxAmount !== undefined) {
|
|
1030
|
+
const amount = BigInt(data.amount ?? "0");
|
|
1031
|
+
if (amount > filter.maxAmount)
|
|
1032
|
+
return false;
|
|
1033
|
+
}
|
|
1034
|
+
return true;
|
|
1035
|
+
});
|
|
1036
|
+
if (filtered.length > 0) {
|
|
1037
|
+
results.push({ tx, events: filtered });
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
break;
|
|
1041
|
+
}
|
|
1042
|
+
case "ft_transfer":
|
|
1043
|
+
case "ft_mint":
|
|
1044
|
+
case "ft_burn": {
|
|
1045
|
+
const eventType = `${filter.type}_event`;
|
|
1046
|
+
for (const tx of transactions) {
|
|
1047
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
1048
|
+
const matched = txEvents.filter((e) => {
|
|
1049
|
+
if (e.type !== eventType)
|
|
1050
|
+
return false;
|
|
1051
|
+
const data = e.data;
|
|
1052
|
+
if (!data)
|
|
1053
|
+
return false;
|
|
1054
|
+
if (filter.assetIdentifier) {
|
|
1055
|
+
const assetId = data.asset_identifier;
|
|
1056
|
+
if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
|
|
1057
|
+
return false;
|
|
1058
|
+
}
|
|
1059
|
+
if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
|
|
1060
|
+
return false;
|
|
1061
|
+
if ("sender" in filter && filter.sender) {
|
|
1062
|
+
if (!matchPattern(data.sender, filter.sender))
|
|
1063
|
+
return false;
|
|
1064
|
+
}
|
|
1065
|
+
if ("recipient" in filter && filter.recipient) {
|
|
1066
|
+
if (!matchPattern(data.recipient, filter.recipient))
|
|
1067
|
+
return false;
|
|
1068
|
+
}
|
|
1069
|
+
if (filter.minAmount !== undefined) {
|
|
1070
|
+
const amount = BigInt(data.amount ?? "0");
|
|
1071
|
+
if (amount < filter.minAmount)
|
|
1072
|
+
return false;
|
|
1073
|
+
}
|
|
1074
|
+
return true;
|
|
1075
|
+
});
|
|
1076
|
+
if (matched.length > 0) {
|
|
1077
|
+
results.push({ tx, events: matched });
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
break;
|
|
1081
|
+
}
|
|
1082
|
+
case "nft_transfer":
|
|
1083
|
+
case "nft_mint":
|
|
1084
|
+
case "nft_burn": {
|
|
1085
|
+
const eventType = `${filter.type}_event`;
|
|
1086
|
+
for (const tx of transactions) {
|
|
1087
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
1088
|
+
const matched = txEvents.filter((e) => {
|
|
1089
|
+
if (e.type !== eventType)
|
|
1090
|
+
return false;
|
|
1091
|
+
const data = e.data;
|
|
1092
|
+
if (!data)
|
|
1093
|
+
return false;
|
|
1094
|
+
if (filter.assetIdentifier) {
|
|
1095
|
+
const assetId = data.asset_identifier;
|
|
1096
|
+
if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
|
|
1097
|
+
return false;
|
|
1098
|
+
}
|
|
1099
|
+
if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
|
|
1100
|
+
return false;
|
|
1101
|
+
if ("sender" in filter && filter.sender) {
|
|
1102
|
+
if (!matchPattern(data.sender, filter.sender))
|
|
1103
|
+
return false;
|
|
1104
|
+
}
|
|
1105
|
+
if ("recipient" in filter && filter.recipient) {
|
|
1106
|
+
if (!matchPattern(data.recipient, filter.recipient))
|
|
1107
|
+
return false;
|
|
1108
|
+
}
|
|
1109
|
+
return true;
|
|
1110
|
+
});
|
|
1111
|
+
if (matched.length > 0) {
|
|
1112
|
+
results.push({ tx, events: matched });
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
break;
|
|
1116
|
+
}
|
|
1117
|
+
case "contract_call": {
|
|
1118
|
+
for (const tx of transactions) {
|
|
1119
|
+
if (tx.type !== "contract_call")
|
|
1120
|
+
continue;
|
|
1121
|
+
if (filter.contractId) {
|
|
1122
|
+
if (!tx.contract_id || !matchAny(tx.contract_id, filter.contractId))
|
|
1123
|
+
continue;
|
|
1124
|
+
}
|
|
1125
|
+
if (!factoryAllows(filter, tx.contract_id, factoryContracts))
|
|
1126
|
+
continue;
|
|
1127
|
+
if (filter.functionName) {
|
|
1128
|
+
if (!tx.function_name || !matchPattern(tx.function_name, filter.functionName))
|
|
1129
|
+
continue;
|
|
1130
|
+
}
|
|
1131
|
+
if (filter.caller) {
|
|
1132
|
+
if (!matchPattern(tx.sender, filter.caller))
|
|
1133
|
+
continue;
|
|
1134
|
+
}
|
|
1135
|
+
if (!traitAllows(filter, tx.contract_id, traitContracts))
|
|
1136
|
+
continue;
|
|
1137
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
1138
|
+
results.push({ tx, events: txEvents });
|
|
1139
|
+
}
|
|
1140
|
+
break;
|
|
1141
|
+
}
|
|
1142
|
+
case "contract_deploy": {
|
|
1143
|
+
for (const tx of transactions) {
|
|
1144
|
+
if (tx.type !== "smart_contract")
|
|
1145
|
+
continue;
|
|
1146
|
+
if (filter.deployer) {
|
|
1147
|
+
if (!matchPattern(tx.sender, filter.deployer))
|
|
1148
|
+
continue;
|
|
1149
|
+
}
|
|
1150
|
+
if (filter.contractName) {
|
|
1151
|
+
const name = tx.contract_id?.split(".")[1] ?? "";
|
|
1152
|
+
if (!matchPattern(name, filter.contractName))
|
|
1153
|
+
continue;
|
|
1154
|
+
}
|
|
1155
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
1156
|
+
results.push({ tx, events: txEvents });
|
|
1157
|
+
}
|
|
1158
|
+
break;
|
|
1159
|
+
}
|
|
1160
|
+
case "print_event": {
|
|
1161
|
+
for (const tx of transactions) {
|
|
1162
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
1163
|
+
const matched = txEvents.filter((e) => {
|
|
1164
|
+
if (e.type !== "smart_contract_event" && e.type !== "contract_event")
|
|
1165
|
+
return false;
|
|
1166
|
+
const data = e.data;
|
|
1167
|
+
if (!data)
|
|
1168
|
+
return false;
|
|
1169
|
+
if (data.topic !== "print")
|
|
1170
|
+
return false;
|
|
1171
|
+
const contractId = printContractId(data);
|
|
1172
|
+
if (filter.contractId) {
|
|
1173
|
+
if (!contractId || !matchAny(contractId, filter.contractId))
|
|
1174
|
+
return false;
|
|
1175
|
+
}
|
|
1176
|
+
if (!traitAllows(filter, contractId, traitContracts))
|
|
1177
|
+
return false;
|
|
1178
|
+
if (!factoryAllows(filter, contractId, factoryContracts))
|
|
1179
|
+
return false;
|
|
1180
|
+
return true;
|
|
1181
|
+
});
|
|
1182
|
+
if (matched.length > 0) {
|
|
1183
|
+
results.push({ tx, events: matched });
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
break;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
return results;
|
|
1190
|
+
}
|
|
1191
|
+
function matchSources(sources, transactions, events, traitContracts = new Map, factoryContracts = new Map) {
|
|
1192
|
+
const eventsByTx = new Map;
|
|
1193
|
+
for (const event of events) {
|
|
1194
|
+
const list = eventsByTx.get(event.tx_id) ?? [];
|
|
1195
|
+
list.push(event);
|
|
1196
|
+
eventsByTx.set(event.tx_id, list);
|
|
1197
|
+
}
|
|
1198
|
+
const seen = new Set;
|
|
1199
|
+
const results = [];
|
|
1200
|
+
for (const [sourceName, filter] of Object.entries(sources)) {
|
|
1201
|
+
const matches = matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts);
|
|
1202
|
+
for (const match of matches) {
|
|
1203
|
+
const dedupeKey = `${match.tx.tx_id}:${sourceName}`;
|
|
1204
|
+
if (!seen.has(dedupeKey)) {
|
|
1205
|
+
seen.add(dedupeKey);
|
|
1206
|
+
results.push({ ...match, sourceName });
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
return results;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
945
1213
|
// src/runtime/runner.ts
|
|
946
1214
|
import { getErrorMessage } from "@secondlayer/shared";
|
|
947
|
-
import { logger as
|
|
1215
|
+
import { logger as logger4 } from "@secondlayer/shared/logger";
|
|
948
1216
|
import {
|
|
949
1217
|
clarityValueToJS,
|
|
950
|
-
deserializeCV as
|
|
1218
|
+
deserializeCV as deserializeCV4,
|
|
951
1219
|
toCamelCase as toCamelCase2
|
|
952
1220
|
} from "@secondlayer/stacks/clarity";
|
|
953
1221
|
|
|
1222
|
+
// src/print-schema.ts
|
|
1223
|
+
import {
|
|
1224
|
+
deserializeCV as deserializeCV3
|
|
1225
|
+
} from "@secondlayer/stacks/clarity";
|
|
1226
|
+
function camelizeDataKey(str) {
|
|
1227
|
+
return str.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
1228
|
+
}
|
|
1229
|
+
function camelizeKeys(obj) {
|
|
1230
|
+
if (obj === null || obj === undefined)
|
|
1231
|
+
return obj;
|
|
1232
|
+
if (typeof obj !== "object")
|
|
1233
|
+
return obj;
|
|
1234
|
+
if (Array.isArray(obj))
|
|
1235
|
+
return obj.map(camelizeKeys);
|
|
1236
|
+
const result = {};
|
|
1237
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
1238
|
+
result[camelizeDataKey(k)] = camelizeKeys(v);
|
|
1239
|
+
}
|
|
1240
|
+
return result;
|
|
1241
|
+
}
|
|
1242
|
+
function cvToTree(cv) {
|
|
1243
|
+
switch (cv.type) {
|
|
1244
|
+
case "uint":
|
|
1245
|
+
case "int":
|
|
1246
|
+
return { kind: cv.type };
|
|
1247
|
+
case "true":
|
|
1248
|
+
case "false":
|
|
1249
|
+
return { kind: "bool" };
|
|
1250
|
+
case "address":
|
|
1251
|
+
case "contract":
|
|
1252
|
+
return { kind: "principal" };
|
|
1253
|
+
case "buffer":
|
|
1254
|
+
return { kind: "buffer", len: cv.value.length / 2 };
|
|
1255
|
+
case "ascii":
|
|
1256
|
+
return { kind: "ascii", len: cv.value.length };
|
|
1257
|
+
case "utf8":
|
|
1258
|
+
return { kind: "utf8", len: new TextEncoder().encode(cv.value).length };
|
|
1259
|
+
case "none":
|
|
1260
|
+
return { kind: "optional", inner: null };
|
|
1261
|
+
case "some":
|
|
1262
|
+
return { kind: "optional", inner: cvToTree(cv.value) };
|
|
1263
|
+
case "ok":
|
|
1264
|
+
return { kind: "response", ok: cvToTree(cv.value), err: null };
|
|
1265
|
+
case "err":
|
|
1266
|
+
return { kind: "response", ok: null, err: cvToTree(cv.value) };
|
|
1267
|
+
case "list": {
|
|
1268
|
+
let inner = null;
|
|
1269
|
+
for (const el of cv.value) {
|
|
1270
|
+
const t = cvToTree(el);
|
|
1271
|
+
inner = inner ? unify(inner, t) : t;
|
|
1272
|
+
}
|
|
1273
|
+
return { kind: "list", inner };
|
|
1274
|
+
}
|
|
1275
|
+
case "tuple": {
|
|
1276
|
+
const entries = new Map;
|
|
1277
|
+
for (const [k, v] of Object.entries(cv.value)) {
|
|
1278
|
+
entries.set(k, { tree: cvToTree(v), present: 1 });
|
|
1279
|
+
}
|
|
1280
|
+
return { kind: "tuple", count: 1, entries };
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
function unifyNullable(a, b) {
|
|
1285
|
+
if (!a)
|
|
1286
|
+
return b;
|
|
1287
|
+
if (!b)
|
|
1288
|
+
return a;
|
|
1289
|
+
return unify(a, b);
|
|
1290
|
+
}
|
|
1291
|
+
var UNION_KIND_ORDER = {
|
|
1292
|
+
uint: 0,
|
|
1293
|
+
int: 1,
|
|
1294
|
+
bool: 2,
|
|
1295
|
+
principal: 3,
|
|
1296
|
+
buffer: 4,
|
|
1297
|
+
ascii: 5,
|
|
1298
|
+
utf8: 6,
|
|
1299
|
+
list: 7,
|
|
1300
|
+
tuple: 8,
|
|
1301
|
+
response: 9
|
|
1302
|
+
};
|
|
1303
|
+
function makeUnion(members) {
|
|
1304
|
+
const sorted = [...members].sort((a, b) => (UNION_KIND_ORDER[a.kind] ?? 99) - (UNION_KIND_ORDER[b.kind] ?? 99));
|
|
1305
|
+
return { kind: "union", members: sorted };
|
|
1306
|
+
}
|
|
1307
|
+
function unify(a, b) {
|
|
1308
|
+
if (a.kind === "optional" || b.kind === "optional") {
|
|
1309
|
+
const ai = a.kind === "optional" ? a.inner : a;
|
|
1310
|
+
const bi = b.kind === "optional" ? b.inner : b;
|
|
1311
|
+
return { kind: "optional", inner: unifyNullable(ai, bi) };
|
|
1312
|
+
}
|
|
1313
|
+
if (a.kind === "union")
|
|
1314
|
+
return unionAdd(a.members, b);
|
|
1315
|
+
if (b.kind === "union")
|
|
1316
|
+
return unionAdd(b.members, a);
|
|
1317
|
+
if (a.kind !== b.kind)
|
|
1318
|
+
return makeUnion([a, b]);
|
|
1319
|
+
switch (a.kind) {
|
|
1320
|
+
case "uint":
|
|
1321
|
+
case "int":
|
|
1322
|
+
case "bool":
|
|
1323
|
+
case "principal":
|
|
1324
|
+
return a;
|
|
1325
|
+
case "buffer":
|
|
1326
|
+
case "ascii":
|
|
1327
|
+
case "utf8":
|
|
1328
|
+
return { kind: a.kind, len: Math.max(a.len, b.len) };
|
|
1329
|
+
case "list":
|
|
1330
|
+
return {
|
|
1331
|
+
kind: "list",
|
|
1332
|
+
inner: unifyNullable(a.inner, b.inner)
|
|
1333
|
+
};
|
|
1334
|
+
case "response": {
|
|
1335
|
+
const rb = b;
|
|
1336
|
+
return {
|
|
1337
|
+
kind: "response",
|
|
1338
|
+
ok: unifyNullable(a.ok, rb.ok),
|
|
1339
|
+
err: unifyNullable(a.err, rb.err)
|
|
1340
|
+
};
|
|
1341
|
+
}
|
|
1342
|
+
case "tuple": {
|
|
1343
|
+
const tb = b;
|
|
1344
|
+
const entries = new Map([...a.entries].map(([k, e]) => [k, { ...e }]));
|
|
1345
|
+
for (const [k, e] of tb.entries) {
|
|
1346
|
+
const existing = entries.get(k);
|
|
1347
|
+
entries.set(k, existing ? {
|
|
1348
|
+
tree: unify(existing.tree, e.tree),
|
|
1349
|
+
present: existing.present + e.present
|
|
1350
|
+
} : { ...e });
|
|
1351
|
+
}
|
|
1352
|
+
return { kind: "tuple", count: a.count + tb.count, entries };
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
function unionAdd(members, t) {
|
|
1357
|
+
if (t.kind === "union") {
|
|
1358
|
+
let acc = { kind: "union", members };
|
|
1359
|
+
for (const m of t.members)
|
|
1360
|
+
acc = unify(acc, m);
|
|
1361
|
+
return acc;
|
|
1362
|
+
}
|
|
1363
|
+
const next = [...members];
|
|
1364
|
+
for (let i = 0;i < next.length; i++) {
|
|
1365
|
+
const member = next[i];
|
|
1366
|
+
if (!member)
|
|
1367
|
+
continue;
|
|
1368
|
+
const merged = unify(member, t);
|
|
1369
|
+
if (merged.kind !== "union") {
|
|
1370
|
+
next[i] = merged;
|
|
1371
|
+
return makeUnion(next);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
next.push(t);
|
|
1375
|
+
return makeUnion(next);
|
|
1376
|
+
}
|
|
1377
|
+
function wrapOptional(t) {
|
|
1378
|
+
return t.kind === "optional" ? t : { kind: "optional", inner: t };
|
|
1379
|
+
}
|
|
1380
|
+
function renderClarity(t) {
|
|
1381
|
+
if (!t)
|
|
1382
|
+
return "?";
|
|
1383
|
+
switch (t.kind) {
|
|
1384
|
+
case "uint":
|
|
1385
|
+
case "int":
|
|
1386
|
+
case "bool":
|
|
1387
|
+
case "principal":
|
|
1388
|
+
return t.kind;
|
|
1389
|
+
case "buffer":
|
|
1390
|
+
return `(buff ${t.len})`;
|
|
1391
|
+
case "ascii":
|
|
1392
|
+
return `(string-ascii ${t.len})`;
|
|
1393
|
+
case "utf8":
|
|
1394
|
+
return `(string-utf8 ${t.len})`;
|
|
1395
|
+
case "optional":
|
|
1396
|
+
return `(optional ${renderClarity(t.inner)})`;
|
|
1397
|
+
case "list":
|
|
1398
|
+
return `(list ${renderClarity(t.inner)})`;
|
|
1399
|
+
case "response":
|
|
1400
|
+
return `(response ${renderClarity(t.ok)} ${renderClarity(t.err)})`;
|
|
1401
|
+
case "tuple": {
|
|
1402
|
+
const parts = [...t.entries].map(([k, e]) => {
|
|
1403
|
+
const tree = e.present < t.count ? wrapOptional(e.tree) : e.tree;
|
|
1404
|
+
return `(${k} ${renderClarity(tree)})`;
|
|
1405
|
+
});
|
|
1406
|
+
return `(tuple ${parts.join(" ")})`;
|
|
1407
|
+
}
|
|
1408
|
+
case "union":
|
|
1409
|
+
return t.members.map(renderClarity).join(" | ");
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
function renderTs(t) {
|
|
1413
|
+
if (!t)
|
|
1414
|
+
return "unknown";
|
|
1415
|
+
switch (t.kind) {
|
|
1416
|
+
case "uint":
|
|
1417
|
+
case "int":
|
|
1418
|
+
return "bigint";
|
|
1419
|
+
case "bool":
|
|
1420
|
+
return "boolean";
|
|
1421
|
+
case "principal":
|
|
1422
|
+
case "buffer":
|
|
1423
|
+
case "ascii":
|
|
1424
|
+
case "utf8":
|
|
1425
|
+
return "string";
|
|
1426
|
+
case "optional":
|
|
1427
|
+
return t.inner ? `${renderTs(t.inner)} | null` : "unknown | null";
|
|
1428
|
+
case "list": {
|
|
1429
|
+
const inner = renderTs(t.inner);
|
|
1430
|
+
return inner.includes(" | ") ? `(${inner})[]` : `${inner}[]`;
|
|
1431
|
+
}
|
|
1432
|
+
case "response": {
|
|
1433
|
+
const sides = [...new Set([renderTs(t.ok), renderTs(t.err)])];
|
|
1434
|
+
return sides.join(" | ");
|
|
1435
|
+
}
|
|
1436
|
+
case "tuple": {
|
|
1437
|
+
const parts = [...t.entries].map(([k, e]) => {
|
|
1438
|
+
const opt = e.present < t.count ? "?" : "";
|
|
1439
|
+
return `${camelizeDataKey(k)}${opt}: ${renderTs(e.tree)}`;
|
|
1440
|
+
});
|
|
1441
|
+
return `{ ${parts.join("; ")} }`;
|
|
1442
|
+
}
|
|
1443
|
+
case "union":
|
|
1444
|
+
return [...new Set(t.members.map((m) => renderTs(m)))].join(" | ");
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
function toColumnType(t) {
|
|
1448
|
+
if (!t)
|
|
1449
|
+
return "jsonb";
|
|
1450
|
+
switch (t.kind) {
|
|
1451
|
+
case "uint":
|
|
1452
|
+
return "uint";
|
|
1453
|
+
case "int":
|
|
1454
|
+
return "int";
|
|
1455
|
+
case "bool":
|
|
1456
|
+
return "boolean";
|
|
1457
|
+
case "principal":
|
|
1458
|
+
return "principal";
|
|
1459
|
+
case "buffer":
|
|
1460
|
+
case "ascii":
|
|
1461
|
+
case "utf8":
|
|
1462
|
+
return "text";
|
|
1463
|
+
case "list":
|
|
1464
|
+
case "tuple":
|
|
1465
|
+
return "jsonb";
|
|
1466
|
+
case "optional":
|
|
1467
|
+
return toColumnType(t.inner);
|
|
1468
|
+
case "response":
|
|
1469
|
+
return t.ok ? toColumnType(t.ok) : "jsonb";
|
|
1470
|
+
case "union":
|
|
1471
|
+
return "jsonb";
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
var MAX_DECODED_PER_TOPIC_NEWEST = 75;
|
|
1475
|
+
var MAX_DECODED_PER_TOPIC_OLDEST = 25;
|
|
1476
|
+
function inferPrintTopics(samples) {
|
|
1477
|
+
const groups = new Map;
|
|
1478
|
+
for (const s of samples) {
|
|
1479
|
+
const group = groups.get(s.topic);
|
|
1480
|
+
if (group)
|
|
1481
|
+
group.push(s);
|
|
1482
|
+
else
|
|
1483
|
+
groups.set(s.topic, [s]);
|
|
1484
|
+
}
|
|
1485
|
+
const out = [];
|
|
1486
|
+
for (const [topic, rows] of groups) {
|
|
1487
|
+
let first = Number.POSITIVE_INFINITY;
|
|
1488
|
+
let last = Number.NEGATIVE_INFINITY;
|
|
1489
|
+
for (const r of rows) {
|
|
1490
|
+
if (r.blockHeight < first)
|
|
1491
|
+
first = r.blockHeight;
|
|
1492
|
+
if (r.blockHeight > last)
|
|
1493
|
+
last = r.blockHeight;
|
|
1494
|
+
}
|
|
1495
|
+
const withHex = [...rows].filter((r) => r.rawHex !== null).sort((a, b) => b.blockHeight - a.blockHeight);
|
|
1496
|
+
const budget = MAX_DECODED_PER_TOPIC_NEWEST + MAX_DECODED_PER_TOPIC_OLDEST;
|
|
1497
|
+
const picked = withHex.length <= budget ? withHex : [
|
|
1498
|
+
...withHex.slice(0, MAX_DECODED_PER_TOPIC_NEWEST),
|
|
1499
|
+
...withHex.slice(-MAX_DECODED_PER_TOPIC_OLDEST)
|
|
1500
|
+
];
|
|
1501
|
+
const tuples = [];
|
|
1502
|
+
let decoded = 0;
|
|
1503
|
+
for (const p of picked) {
|
|
1504
|
+
try {
|
|
1505
|
+
const cv = deserializeCV3(p.rawHex);
|
|
1506
|
+
decoded++;
|
|
1507
|
+
if (cv.type === "tuple")
|
|
1508
|
+
tuples.push(cv);
|
|
1509
|
+
} catch {}
|
|
1510
|
+
}
|
|
1511
|
+
const nonTuple = decoded > 0 && tuples.length === 0;
|
|
1512
|
+
const fields = [];
|
|
1513
|
+
if (!nonTuple) {
|
|
1514
|
+
const stats = new Map;
|
|
1515
|
+
for (const t of tuples) {
|
|
1516
|
+
for (const [key, value] of Object.entries(t.value)) {
|
|
1517
|
+
if (key === "topic")
|
|
1518
|
+
continue;
|
|
1519
|
+
const tree = cvToTree(value);
|
|
1520
|
+
const existing = stats.get(key);
|
|
1521
|
+
if (existing) {
|
|
1522
|
+
existing.present++;
|
|
1523
|
+
existing.tree = unify(existing.tree, tree);
|
|
1524
|
+
if (value.type === "none")
|
|
1525
|
+
existing.noneCount++;
|
|
1526
|
+
if (value.type === "none" || value.type === "some") {
|
|
1527
|
+
existing.optionalSeen = true;
|
|
1528
|
+
}
|
|
1529
|
+
} else {
|
|
1530
|
+
stats.set(key, {
|
|
1531
|
+
tree,
|
|
1532
|
+
present: 1,
|
|
1533
|
+
noneCount: value.type === "none" ? 1 : 0,
|
|
1534
|
+
optionalSeen: value.type === "none" || value.type === "some"
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
for (const [name, st] of [...stats].sort(([a], [b]) => a.localeCompare(b))) {
|
|
1540
|
+
const field = {
|
|
1541
|
+
name,
|
|
1542
|
+
camel_name: camelizeDataKey(name),
|
|
1543
|
+
clarity_type: renderClarity(st.tree),
|
|
1544
|
+
ts_type: renderTs(st.tree),
|
|
1545
|
+
column_type: toColumnType(st.tree),
|
|
1546
|
+
always_present: st.present === tuples.length
|
|
1547
|
+
};
|
|
1548
|
+
if (st.optionalSeen) {
|
|
1549
|
+
field.optional_some_rate = (st.present - st.noneCount) / st.present;
|
|
1550
|
+
}
|
|
1551
|
+
fields.push(field);
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
out.push({
|
|
1555
|
+
topic,
|
|
1556
|
+
count: rows.length,
|
|
1557
|
+
first_height: first,
|
|
1558
|
+
last_height: last,
|
|
1559
|
+
non_tuple: nonTuple,
|
|
1560
|
+
fields
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1563
|
+
return out.sort((a, b) => b.count - a.count);
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
// src/runtime/materialize.ts
|
|
1567
|
+
function resolveColumn(col, event, tx, block) {
|
|
1568
|
+
if ("fromTx" in col) {
|
|
1569
|
+
return { ok: true, value: tx[col.fromTx] };
|
|
1570
|
+
}
|
|
1571
|
+
if ("fromBlock" in col) {
|
|
1572
|
+
return { ok: true, value: block[col.fromBlock] };
|
|
1573
|
+
}
|
|
1574
|
+
const data = event.data;
|
|
1575
|
+
const dataObj = data !== null && typeof data === "object" && !Array.isArray(data) ? data : undefined;
|
|
1576
|
+
if (dataObj && Object.prototype.hasOwnProperty.call(dataObj, col.from)) {
|
|
1577
|
+
return { ok: true, value: dataObj[col.from] };
|
|
1578
|
+
}
|
|
1579
|
+
if (col.from === "topic" && "topic" in event) {
|
|
1580
|
+
return { ok: true, value: event.topic };
|
|
1581
|
+
}
|
|
1582
|
+
if (Object.prototype.hasOwnProperty.call(event, col.from)) {
|
|
1583
|
+
return { ok: true, value: event[col.from] };
|
|
1584
|
+
}
|
|
1585
|
+
return { ok: false, missing: col.from };
|
|
1586
|
+
}
|
|
1587
|
+
function buildMaterializeRow(spec, event, ctx, tableColumns) {
|
|
1588
|
+
const row = {};
|
|
1589
|
+
for (const [colName, mapping] of Object.entries(spec.columns)) {
|
|
1590
|
+
const resolved = resolveColumn(mapping, event, ctx.tx, ctx.block);
|
|
1591
|
+
if (!resolved.ok) {
|
|
1592
|
+
const colDef = tableColumns[colName];
|
|
1593
|
+
if (colDef?.nullable) {
|
|
1594
|
+
row[colName] = null;
|
|
1595
|
+
continue;
|
|
1596
|
+
}
|
|
1597
|
+
return {
|
|
1598
|
+
ok: false,
|
|
1599
|
+
reason: `required materialize field "${resolved.missing}" missing for column "${colName}"`
|
|
1600
|
+
};
|
|
1601
|
+
}
|
|
1602
|
+
row[colName] = resolved.value ?? null;
|
|
1603
|
+
}
|
|
1604
|
+
return { ok: true, row };
|
|
1605
|
+
}
|
|
1606
|
+
function applyMaterializeInsert(spec, event, ctx, schema) {
|
|
1607
|
+
const table = schema[spec.table];
|
|
1608
|
+
if (!table) {
|
|
1609
|
+
return { ok: false, reason: `materialize table "${spec.table}" missing` };
|
|
1610
|
+
}
|
|
1611
|
+
const built = buildMaterializeRow(spec, event, ctx, table.columns);
|
|
1612
|
+
if (!built.ok)
|
|
1613
|
+
return built;
|
|
1614
|
+
ctx.insert(spec.table, built.row);
|
|
1615
|
+
return { ok: true };
|
|
1616
|
+
}
|
|
1617
|
+
|
|
954
1618
|
// src/runtime/print-validate.ts
|
|
955
1619
|
var COLUMN_TYPES = new Set([
|
|
956
1620
|
"text",
|
|
@@ -1030,24 +1694,62 @@ function validatePrintPayload(prints, topic, data) {
|
|
|
1030
1694
|
return matchesTopic(declared, data);
|
|
1031
1695
|
}
|
|
1032
1696
|
|
|
1033
|
-
// src/runtime/
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1697
|
+
// src/runtime/violations.ts
|
|
1698
|
+
import { getTargetDb } from "@secondlayer/shared/db";
|
|
1699
|
+
import { logger as logger3 } from "@secondlayer/shared/logger";
|
|
1700
|
+
import { sql as sql2 } from "kysely";
|
|
1701
|
+
var VIOLATIONS_CAP_PER_SUBGRAPH = 100;
|
|
1702
|
+
function jsonSafe2(value) {
|
|
1703
|
+
if (typeof value === "bigint")
|
|
1704
|
+
return value.toString();
|
|
1705
|
+
if (value === null || value === undefined)
|
|
1706
|
+
return value;
|
|
1707
|
+
if (Array.isArray(value))
|
|
1708
|
+
return value.map(jsonSafe2);
|
|
1709
|
+
if (typeof value === "object") {
|
|
1710
|
+
const out = {};
|
|
1711
|
+
for (const [k, v] of Object.entries(value)) {
|
|
1712
|
+
out[k] = jsonSafe2(v);
|
|
1713
|
+
}
|
|
1714
|
+
return out;
|
|
1715
|
+
}
|
|
1716
|
+
return value;
|
|
1037
1717
|
}
|
|
1038
|
-
function
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1718
|
+
async function defaultRecordPrintViolation(input) {
|
|
1719
|
+
try {
|
|
1720
|
+
const db = getTargetDb();
|
|
1721
|
+
const sample = jsonSafe2(input.samplePayload);
|
|
1722
|
+
await db.insertInto("subgraph_violations").values({
|
|
1723
|
+
subgraph_name: input.subgraphName,
|
|
1724
|
+
source_name: input.sourceName,
|
|
1725
|
+
block_height: input.blockHeight,
|
|
1726
|
+
tx_id: input.txId,
|
|
1727
|
+
reason: input.reason,
|
|
1728
|
+
sample_payload: sample
|
|
1729
|
+
}).execute();
|
|
1730
|
+
await sql2`
|
|
1731
|
+
DELETE FROM subgraph_violations
|
|
1732
|
+
WHERE id IN (
|
|
1733
|
+
SELECT id FROM subgraph_violations
|
|
1734
|
+
WHERE subgraph_name = ${input.subgraphName}
|
|
1735
|
+
ORDER BY seen_at DESC
|
|
1736
|
+
OFFSET ${VIOLATIONS_CAP_PER_SUBGRAPH}
|
|
1737
|
+
)
|
|
1738
|
+
`.execute(db);
|
|
1739
|
+
} catch (err) {
|
|
1740
|
+
logger3.warn("Failed to record print violation", {
|
|
1741
|
+
subgraph: input.subgraphName,
|
|
1742
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1743
|
+
});
|
|
1048
1744
|
}
|
|
1049
|
-
return result;
|
|
1050
1745
|
}
|
|
1746
|
+
var recorder = defaultRecordPrintViolation;
|
|
1747
|
+
async function recordPrintViolation(input) {
|
|
1748
|
+
await recorder(input);
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
// src/runtime/runner.ts
|
|
1752
|
+
var DEFAULT_ERROR_THRESHOLD = 50;
|
|
1051
1753
|
function decodeFunctionArgs2(args) {
|
|
1052
1754
|
let parsed = args;
|
|
1053
1755
|
if (typeof parsed === "string") {
|
|
@@ -1111,7 +1813,7 @@ function buildContractCallInput(filter, tx) {
|
|
|
1111
1813
|
return;
|
|
1112
1814
|
try {
|
|
1113
1815
|
const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
1114
|
-
input[toCamelCase2(arg.name)] = decodeArg(arg.type,
|
|
1816
|
+
input[toCamelCase2(arg.name)] = decodeArg(arg.type, deserializeCV4(clean));
|
|
1115
1817
|
} catch {}
|
|
1116
1818
|
});
|
|
1117
1819
|
return input;
|
|
@@ -1234,7 +1936,7 @@ function buildEventPayload(filter, tx, event) {
|
|
|
1234
1936
|
const { topic: _, ...rest } = clarityObj ?? {};
|
|
1235
1937
|
const data = Object.keys(rest).length > 0 ? camelizeKeys(rest) : rawValue && typeof rawValue !== "object" ? rawValue : {};
|
|
1236
1938
|
return {
|
|
1237
|
-
contractId: decoded
|
|
1939
|
+
contractId: printContractId(decoded) ?? tx.contract_id ?? "",
|
|
1238
1940
|
topic,
|
|
1239
1941
|
data: data ?? {},
|
|
1240
1942
|
tx: txMeta
|
|
@@ -1292,366 +1994,131 @@ async function runHandlers(subgraph, matched, ctx, opts) {
|
|
|
1292
1994
|
units.push({ tx, sourceName, event });
|
|
1293
1995
|
}
|
|
1294
1996
|
}
|
|
1295
|
-
units.sort((a, b) => (a.tx.tx_index ?? 0) - (b.tx.tx_index ?? 0) || (a.event?.event_index ?? -1) - (b.event?.event_index ?? -1));
|
|
1296
|
-
for (const { tx, event, sourceName } of units) {
|
|
1297
|
-
if (errors >= threshold) {
|
|
1298
|
-
|
|
1299
|
-
subgraph: subgraph.name,
|
|
1300
|
-
errors,
|
|
1301
|
-
threshold
|
|
1302
|
-
});
|
|
1303
|
-
return { processed, errors, ...skipped > 0 ? { skipped } : {} };
|
|
1304
|
-
}
|
|
1305
|
-
const handler = subgraph.handlers[sourceName] ?? subgraph.handlers["*"] ?? null;
|
|
1306
|
-
if (!handler) {
|
|
1307
|
-
logger3.warn("No handler found for source", {
|
|
1308
|
-
subgraph: subgraph.name,
|
|
1309
|
-
sourceName,
|
|
1310
|
-
txId: tx.tx_id
|
|
1311
|
-
});
|
|
1312
|
-
continue;
|
|
1313
|
-
}
|
|
1314
|
-
ctx.setTx({
|
|
1315
|
-
txId: tx.tx_id,
|
|
1316
|
-
sender: tx.sender,
|
|
1317
|
-
type: tx.type,
|
|
1318
|
-
status: tx.status,
|
|
1319
|
-
contractId: tx.contract_id ?? null,
|
|
1320
|
-
functionName: tx.function_name ?? null
|
|
1321
|
-
});
|
|
1322
|
-
const filter = filterLookup.get(sourceName);
|
|
1323
|
-
const checkpoint = ctx.opsCheckpoint();
|
|
1324
|
-
try {
|
|
1325
|
-
let payload;
|
|
1326
|
-
if (event === null) {
|
|
1327
|
-
payload = filter ? buildEventPayload(filter, tx, null) : {
|
|
1328
|
-
tx: {
|
|
1329
|
-
txId: tx.tx_id,
|
|
1330
|
-
sender: tx.sender,
|
|
1331
|
-
type: tx.type,
|
|
1332
|
-
status: tx.status,
|
|
1333
|
-
contractId: tx.contract_id,
|
|
1334
|
-
functionName: tx.function_name
|
|
1335
|
-
}
|
|
1336
|
-
};
|
|
1337
|
-
} else if (filter) {
|
|
1338
|
-
payload = buildEventPayload(filter, tx, event);
|
|
1339
|
-
} else {
|
|
1340
|
-
const decoded = decodeEventData(event.data);
|
|
1341
|
-
payload = {
|
|
1342
|
-
...decoded,
|
|
1343
|
-
_eventId: event.id,
|
|
1344
|
-
_eventType: event.type,
|
|
1345
|
-
_eventIndex: event.event_index,
|
|
1346
|
-
tx: {
|
|
1347
|
-
txId: tx.tx_id,
|
|
1348
|
-
sender: tx.sender,
|
|
1349
|
-
type: tx.type,
|
|
1350
|
-
status: tx.status,
|
|
1351
|
-
contractId: tx.contract_id,
|
|
1352
|
-
functionName: tx.function_name
|
|
1353
|
-
}
|
|
1354
|
-
};
|
|
1355
|
-
}
|
|
1356
|
-
if (event !== null && filter?.type === "print_event" && filter.topic && payload.topic !== filter.topic) {
|
|
1357
|
-
continue;
|
|
1358
|
-
}
|
|
1359
|
-
if (event !== null && filter?.type === "print_event" && filter.prints) {
|
|
1360
|
-
const printPayload = payload;
|
|
1361
|
-
const verdict = validatePrintPayload(filter.prints, printPayload.topic ?? "", printPayload.data ?? {});
|
|
1362
|
-
if (!verdict.ok) {
|
|
1363
|
-
skipped++;
|
|
1364
|
-
logger3.warn("Print payload does not match declared prints schema", {
|
|
1365
|
-
subgraph: subgraph.name,
|
|
1366
|
-
sourceName,
|
|
1367
|
-
topic: printPayload.topic,
|
|
1368
|
-
txId: tx.tx_id,
|
|
1369
|
-
reason: verdict.reason
|
|
1370
|
-
});
|
|
1371
|
-
continue;
|
|
1372
|
-
}
|
|
1373
|
-
}
|
|
1374
|
-
await handler(payload, ctx);
|
|
1375
|
-
processed++;
|
|
1376
|
-
} catch (err) {
|
|
1377
|
-
ctx.rollbackTo(checkpoint);
|
|
1378
|
-
errors++;
|
|
1379
|
-
logger3.error("Subgraph handler error", {
|
|
1380
|
-
subgraph: subgraph.name,
|
|
1381
|
-
sourceName,
|
|
1382
|
-
txId: tx.tx_id,
|
|
1383
|
-
...event !== null ? { eventId: event.id, eventType: event.type } : {},
|
|
1384
|
-
error: getErrorMessage(err)
|
|
1385
|
-
});
|
|
1386
|
-
}
|
|
1387
|
-
}
|
|
1388
|
-
return { processed, errors, ...skipped > 0 ? { skipped } : {} };
|
|
1389
|
-
}
|
|
1390
|
-
|
|
1391
|
-
// src/runtime/source-matcher.ts
|
|
1392
|
-
var patternCache = new Map;
|
|
1393
|
-
function matchAny(value, pattern) {
|
|
1394
|
-
return Array.isArray(pattern) ? pattern.some((p) => matchPattern(value, p)) : matchPattern(value, pattern);
|
|
1395
|
-
}
|
|
1396
|
-
function matchPattern(value, pattern) {
|
|
1397
|
-
if (!pattern.includes("*"))
|
|
1398
|
-
return value === pattern;
|
|
1399
|
-
let re = patternCache.get(pattern);
|
|
1400
|
-
if (!re) {
|
|
1401
|
-
const regex = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
1402
|
-
re = new RegExp(`^${regex}$`);
|
|
1403
|
-
patternCache.set(pattern, re);
|
|
1404
|
-
}
|
|
1405
|
-
return re.test(value);
|
|
1406
|
-
}
|
|
1407
|
-
var EMPTY_SET = new Set;
|
|
1408
|
-
function factoryAllows(filter, contractId, factoryContracts) {
|
|
1409
|
-
if (!filter.factory)
|
|
1410
|
-
return true;
|
|
1411
|
-
if (!contractId)
|
|
1412
|
-
return false;
|
|
1413
|
-
return (factoryContracts.get(filter.factory.from) ?? EMPTY_SET).has(contractId);
|
|
1414
|
-
}
|
|
1415
|
-
function readPath(payload, path) {
|
|
1416
|
-
let cur = payload;
|
|
1417
|
-
for (const key of path.split(".")) {
|
|
1418
|
-
if (cur === null || typeof cur !== "object")
|
|
1419
|
-
return;
|
|
1420
|
-
cur = cur[key];
|
|
1421
|
-
}
|
|
1422
|
-
return cur;
|
|
1423
|
-
}
|
|
1424
|
-
function traitAllows(filter, contractId, traitContracts) {
|
|
1425
|
-
const trait = filter.trait;
|
|
1426
|
-
if (!trait)
|
|
1427
|
-
return true;
|
|
1428
|
-
if (!contractId)
|
|
1429
|
-
return false;
|
|
1430
|
-
return (traitContracts.get(trait) ?? EMPTY_SET).has(contractId);
|
|
1431
|
-
}
|
|
1432
|
-
function assetContract(assetId) {
|
|
1433
|
-
return assetId?.split("::")[0];
|
|
1434
|
-
}
|
|
1435
|
-
function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts = new Map) {
|
|
1436
|
-
const results = [];
|
|
1437
|
-
switch (filter.type) {
|
|
1438
|
-
case "stx_transfer":
|
|
1439
|
-
case "stx_mint":
|
|
1440
|
-
case "stx_burn":
|
|
1441
|
-
case "stx_lock": {
|
|
1442
|
-
const eventType = `${filter.type}_event`;
|
|
1443
|
-
for (const tx of transactions) {
|
|
1444
|
-
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
1445
|
-
const matched = txEvents.filter((e) => e.type === eventType);
|
|
1446
|
-
if (matched.length === 0)
|
|
1447
|
-
continue;
|
|
1448
|
-
const filtered = matched.filter((e) => {
|
|
1449
|
-
const data = e.data;
|
|
1450
|
-
if (!data)
|
|
1451
|
-
return false;
|
|
1452
|
-
if ("sender" in filter && filter.sender) {
|
|
1453
|
-
if (!matchPattern(data.sender, filter.sender))
|
|
1454
|
-
return false;
|
|
1455
|
-
}
|
|
1456
|
-
if ("recipient" in filter && filter.recipient) {
|
|
1457
|
-
if (!matchPattern(data.recipient, filter.recipient))
|
|
1458
|
-
return false;
|
|
1459
|
-
}
|
|
1460
|
-
if ("lockedAddress" in filter && filter.lockedAddress) {
|
|
1461
|
-
if (!matchPattern(data.locked_address, filter.lockedAddress))
|
|
1462
|
-
return false;
|
|
1463
|
-
}
|
|
1464
|
-
if ("minAmount" in filter && filter.minAmount !== undefined) {
|
|
1465
|
-
const amount = BigInt(data.amount ?? data.locked_amount ?? "0");
|
|
1466
|
-
if (amount < filter.minAmount)
|
|
1467
|
-
return false;
|
|
1468
|
-
}
|
|
1469
|
-
if ("maxAmount" in filter && filter.maxAmount !== undefined) {
|
|
1470
|
-
const amount = BigInt(data.amount ?? "0");
|
|
1471
|
-
if (amount > filter.maxAmount)
|
|
1472
|
-
return false;
|
|
1473
|
-
}
|
|
1474
|
-
return true;
|
|
1475
|
-
});
|
|
1476
|
-
if (filtered.length > 0) {
|
|
1477
|
-
results.push({ tx, events: filtered });
|
|
1478
|
-
}
|
|
1479
|
-
}
|
|
1480
|
-
break;
|
|
1997
|
+
units.sort((a, b) => (a.tx.tx_index ?? 0) - (b.tx.tx_index ?? 0) || (a.event?.event_index ?? -1) - (b.event?.event_index ?? -1));
|
|
1998
|
+
for (const { tx, event, sourceName } of units) {
|
|
1999
|
+
if (errors >= threshold) {
|
|
2000
|
+
logger4.error("Subgraph error threshold reached, skipping remaining events", {
|
|
2001
|
+
subgraph: subgraph.name,
|
|
2002
|
+
errors,
|
|
2003
|
+
threshold
|
|
2004
|
+
});
|
|
2005
|
+
return { processed, errors, ...skipped > 0 ? { skipped } : {} };
|
|
1481
2006
|
}
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
if (!data)
|
|
1493
|
-
return false;
|
|
1494
|
-
if (filter.assetIdentifier) {
|
|
1495
|
-
const assetId = data.asset_identifier;
|
|
1496
|
-
if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
|
|
1497
|
-
return false;
|
|
1498
|
-
}
|
|
1499
|
-
if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
|
|
1500
|
-
return false;
|
|
1501
|
-
if ("sender" in filter && filter.sender) {
|
|
1502
|
-
if (!matchPattern(data.sender, filter.sender))
|
|
1503
|
-
return false;
|
|
1504
|
-
}
|
|
1505
|
-
if ("recipient" in filter && filter.recipient) {
|
|
1506
|
-
if (!matchPattern(data.recipient, filter.recipient))
|
|
1507
|
-
return false;
|
|
1508
|
-
}
|
|
1509
|
-
if (filter.minAmount !== undefined) {
|
|
1510
|
-
const amount = BigInt(data.amount ?? "0");
|
|
1511
|
-
if (amount < filter.minAmount)
|
|
1512
|
-
return false;
|
|
1513
|
-
}
|
|
1514
|
-
return true;
|
|
1515
|
-
});
|
|
1516
|
-
if (matched.length > 0) {
|
|
1517
|
-
results.push({ tx, events: matched });
|
|
1518
|
-
}
|
|
1519
|
-
}
|
|
1520
|
-
break;
|
|
2007
|
+
const filter = filterLookup.get(sourceName);
|
|
2008
|
+
const materialize = filter && "materialize" in filter && filter.materialize !== undefined ? filter.materialize : undefined;
|
|
2009
|
+
const handler = subgraph.handlers[sourceName] ?? subgraph.handlers["*"] ?? null;
|
|
2010
|
+
if (!handler && !materialize) {
|
|
2011
|
+
logger4.warn("No handler found for source", {
|
|
2012
|
+
subgraph: subgraph.name,
|
|
2013
|
+
sourceName,
|
|
2014
|
+
txId: tx.tx_id
|
|
2015
|
+
});
|
|
2016
|
+
continue;
|
|
1521
2017
|
}
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
if (!matchPattern(data.sender, filter.sender))
|
|
1543
|
-
return false;
|
|
2018
|
+
ctx.setTx({
|
|
2019
|
+
txId: tx.tx_id,
|
|
2020
|
+
sender: tx.sender,
|
|
2021
|
+
type: tx.type,
|
|
2022
|
+
status: tx.status,
|
|
2023
|
+
contractId: tx.contract_id ?? null,
|
|
2024
|
+
functionName: tx.function_name ?? null
|
|
2025
|
+
});
|
|
2026
|
+
const checkpoint = ctx.opsCheckpoint();
|
|
2027
|
+
try {
|
|
2028
|
+
let payload;
|
|
2029
|
+
if (event === null) {
|
|
2030
|
+
payload = filter ? buildEventPayload(filter, tx, null) : {
|
|
2031
|
+
tx: {
|
|
2032
|
+
txId: tx.tx_id,
|
|
2033
|
+
sender: tx.sender,
|
|
2034
|
+
type: tx.type,
|
|
2035
|
+
status: tx.status,
|
|
2036
|
+
contractId: tx.contract_id,
|
|
2037
|
+
functionName: tx.function_name
|
|
1544
2038
|
}
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
2039
|
+
};
|
|
2040
|
+
} else if (filter) {
|
|
2041
|
+
payload = buildEventPayload(filter, tx, event);
|
|
2042
|
+
} else {
|
|
2043
|
+
const decoded = decodeEventData(event.data);
|
|
2044
|
+
payload = {
|
|
2045
|
+
...decoded,
|
|
2046
|
+
_eventId: event.id,
|
|
2047
|
+
_eventType: event.type,
|
|
2048
|
+
_eventIndex: event.event_index,
|
|
2049
|
+
tx: {
|
|
2050
|
+
txId: tx.tx_id,
|
|
2051
|
+
sender: tx.sender,
|
|
2052
|
+
type: tx.type,
|
|
2053
|
+
status: tx.status,
|
|
2054
|
+
contractId: tx.contract_id,
|
|
2055
|
+
functionName: tx.function_name
|
|
1548
2056
|
}
|
|
1549
|
-
|
|
1550
|
-
});
|
|
1551
|
-
if (matched.length > 0) {
|
|
1552
|
-
results.push({ tx, events: matched });
|
|
1553
|
-
}
|
|
2057
|
+
};
|
|
1554
2058
|
}
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
case "contract_call": {
|
|
1558
|
-
for (const tx of transactions) {
|
|
1559
|
-
if (tx.type !== "contract_call")
|
|
1560
|
-
continue;
|
|
1561
|
-
if (filter.contractId) {
|
|
1562
|
-
if (!tx.contract_id || !matchAny(tx.contract_id, filter.contractId))
|
|
1563
|
-
continue;
|
|
1564
|
-
}
|
|
1565
|
-
if (!factoryAllows(filter, tx.contract_id, factoryContracts))
|
|
1566
|
-
continue;
|
|
1567
|
-
if (filter.functionName) {
|
|
1568
|
-
if (!tx.function_name || !matchPattern(tx.function_name, filter.functionName))
|
|
1569
|
-
continue;
|
|
1570
|
-
}
|
|
1571
|
-
if (filter.caller) {
|
|
1572
|
-
if (!matchPattern(tx.sender, filter.caller))
|
|
1573
|
-
continue;
|
|
1574
|
-
}
|
|
1575
|
-
if (!traitAllows(filter, tx.contract_id, traitContracts))
|
|
1576
|
-
continue;
|
|
1577
|
-
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
1578
|
-
results.push({ tx, events: txEvents });
|
|
2059
|
+
if (event !== null && filter?.type === "print_event" && filter.topic && payload.topic !== filter.topic) {
|
|
2060
|
+
continue;
|
|
1579
2061
|
}
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
2062
|
+
if (event !== null && filter?.type === "print_event" && filter.prints) {
|
|
2063
|
+
const printPayload = payload;
|
|
2064
|
+
const verdict = validatePrintPayload(filter.prints, printPayload.topic ?? "", printPayload.data ?? {});
|
|
2065
|
+
if (!verdict.ok) {
|
|
2066
|
+
skipped++;
|
|
2067
|
+
logger4.warn("Print payload does not match declared prints schema", {
|
|
2068
|
+
subgraph: subgraph.name,
|
|
2069
|
+
sourceName,
|
|
2070
|
+
topic: printPayload.topic,
|
|
2071
|
+
txId: tx.tx_id,
|
|
2072
|
+
reason: verdict.reason
|
|
2073
|
+
});
|
|
2074
|
+
recordPrintViolation({
|
|
2075
|
+
subgraphName: subgraph.name,
|
|
2076
|
+
sourceName,
|
|
2077
|
+
blockHeight: ctx.block.height,
|
|
2078
|
+
txId: tx.tx_id,
|
|
2079
|
+
reason: verdict.reason,
|
|
2080
|
+
samplePayload: {
|
|
2081
|
+
topic: printPayload.topic ?? "",
|
|
2082
|
+
data: printPayload.data ?? {}
|
|
2083
|
+
}
|
|
2084
|
+
});
|
|
1585
2085
|
continue;
|
|
1586
|
-
if (filter.deployer) {
|
|
1587
|
-
if (!matchPattern(tx.sender, filter.deployer))
|
|
1588
|
-
continue;
|
|
1589
|
-
}
|
|
1590
|
-
if (filter.contractName) {
|
|
1591
|
-
const name = tx.contract_id?.split(".")[1] ?? "";
|
|
1592
|
-
if (!matchPattern(name, filter.contractName))
|
|
1593
|
-
continue;
|
|
1594
2086
|
}
|
|
1595
|
-
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
1596
|
-
results.push({ tx, events: txEvents });
|
|
1597
2087
|
}
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
if (data.topic !== "print")
|
|
1610
|
-
return false;
|
|
1611
|
-
const printContractId = data.contract_identifier ?? data.contract_id;
|
|
1612
|
-
if (filter.contractId) {
|
|
1613
|
-
if (!printContractId || !matchAny(printContractId, filter.contractId))
|
|
1614
|
-
return false;
|
|
1615
|
-
}
|
|
1616
|
-
if (!traitAllows(filter, printContractId, traitContracts))
|
|
1617
|
-
return false;
|
|
1618
|
-
if (!factoryAllows(filter, printContractId, factoryContracts))
|
|
1619
|
-
return false;
|
|
1620
|
-
return true;
|
|
1621
|
-
});
|
|
1622
|
-
if (matched.length > 0) {
|
|
1623
|
-
results.push({ tx, events: matched });
|
|
2088
|
+
if (materialize) {
|
|
2089
|
+
const result = applyMaterializeInsert(materialize, payload, ctx, subgraph.schema);
|
|
2090
|
+
if (!result.ok) {
|
|
2091
|
+
skipped++;
|
|
2092
|
+
logger4.warn("Materialize skipped: required field missing", {
|
|
2093
|
+
subgraph: subgraph.name,
|
|
2094
|
+
sourceName,
|
|
2095
|
+
txId: tx.tx_id,
|
|
2096
|
+
reason: result.reason
|
|
2097
|
+
});
|
|
2098
|
+
continue;
|
|
1624
2099
|
}
|
|
2100
|
+
processed++;
|
|
2101
|
+
} else if (handler) {
|
|
2102
|
+
await handler(payload, ctx);
|
|
2103
|
+
processed++;
|
|
1625
2104
|
}
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
eventsByTx.set(event.tx_id, list);
|
|
1637
|
-
}
|
|
1638
|
-
const seen = new Set;
|
|
1639
|
-
const results = [];
|
|
1640
|
-
for (const [sourceName, filter] of Object.entries(sources)) {
|
|
1641
|
-
const matches = matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts);
|
|
1642
|
-
for (const match of matches) {
|
|
1643
|
-
const dedupeKey = `${match.tx.tx_id}:${sourceName}`;
|
|
1644
|
-
if (!seen.has(dedupeKey)) {
|
|
1645
|
-
seen.add(dedupeKey);
|
|
1646
|
-
results.push({ ...match, sourceName });
|
|
1647
|
-
}
|
|
2105
|
+
} catch (err) {
|
|
2106
|
+
ctx.rollbackTo(checkpoint);
|
|
2107
|
+
errors++;
|
|
2108
|
+
logger4.error("Subgraph handler error", {
|
|
2109
|
+
subgraph: subgraph.name,
|
|
2110
|
+
sourceName,
|
|
2111
|
+
txId: tx.tx_id,
|
|
2112
|
+
...event !== null ? { eventId: event.id, eventType: event.type } : {},
|
|
2113
|
+
error: getErrorMessage(err)
|
|
2114
|
+
});
|
|
1648
2115
|
}
|
|
1649
2116
|
}
|
|
1650
|
-
return
|
|
2117
|
+
return { processed, errors, ...skipped > 0 ? { skipped } : {} };
|
|
1651
2118
|
}
|
|
1652
2119
|
|
|
1653
2120
|
// src/runtime/block-processor.ts
|
|
1654
|
-
import { getTargetDb } from "@secondlayer/shared/db";
|
|
2121
|
+
import { getTargetDb as getTargetDb2 } from "@secondlayer/shared/db";
|
|
1655
2122
|
import { resolveTraitContractIds } from "@secondlayer/shared/db/queries/contracts";
|
|
1656
2123
|
import { recordGapBatch } from "@secondlayer/shared/db/queries/subgraph-gaps";
|
|
1657
2124
|
import { advanceOperationCursor } from "@secondlayer/shared/db/queries/subgraph-operations";
|
|
@@ -1662,15 +2129,15 @@ import {
|
|
|
1662
2129
|
rewindLiveProgress,
|
|
1663
2130
|
updateSubgraphStatus
|
|
1664
2131
|
} from "@secondlayer/shared/db/queries/subgraphs";
|
|
1665
|
-
import { logger as
|
|
1666
|
-
import { sql as
|
|
2132
|
+
import { logger as logger8 } from "@secondlayer/shared/logger";
|
|
2133
|
+
import { sql as sql4 } from "kysely";
|
|
1667
2134
|
|
|
1668
2135
|
// src/runtime/block-source.ts
|
|
1669
2136
|
import { getSourceDb as getSourceDb2 } from "@secondlayer/shared/db";
|
|
1670
2137
|
import {
|
|
1671
2138
|
createInternalIndexHttpClient
|
|
1672
2139
|
} from "@secondlayer/shared/index-http";
|
|
1673
|
-
import { logger as
|
|
2140
|
+
import { logger as logger5 } from "@secondlayer/shared/logger";
|
|
1674
2141
|
|
|
1675
2142
|
// src/runtime/batch-loader.ts
|
|
1676
2143
|
async function loadBlockRange(db, fromHeight, toHeight) {
|
|
@@ -1990,7 +2457,7 @@ function reconstructEvent(e) {
|
|
|
1990
2457
|
type: "contract_event",
|
|
1991
2458
|
data: {
|
|
1992
2459
|
topic: e.payload.topic,
|
|
1993
|
-
|
|
2460
|
+
contract_id: e.contract_id,
|
|
1994
2461
|
value: e.payload.value,
|
|
1995
2462
|
raw_value: e.payload.raw_value
|
|
1996
2463
|
}
|
|
@@ -2157,7 +2624,7 @@ class FallbackBlockSource {
|
|
|
2157
2624
|
try {
|
|
2158
2625
|
return await this.primary.getTip();
|
|
2159
2626
|
} catch (err) {
|
|
2160
|
-
|
|
2627
|
+
logger5.warn("block source primary getTip failed — using DB tap", {
|
|
2161
2628
|
error: err instanceof Error ? err.message : String(err)
|
|
2162
2629
|
});
|
|
2163
2630
|
return this.fallback.getTip();
|
|
@@ -2167,7 +2634,7 @@ class FallbackBlockSource {
|
|
|
2167
2634
|
try {
|
|
2168
2635
|
return await this.primary.loadBlockRange(fromHeight, toHeight);
|
|
2169
2636
|
} catch (err) {
|
|
2170
|
-
|
|
2637
|
+
logger5.warn("block source primary loadBlockRange failed — using DB tap", {
|
|
2171
2638
|
from: fromHeight,
|
|
2172
2639
|
to: toHeight,
|
|
2173
2640
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -2204,7 +2671,7 @@ function resolveBlockSource(subgraph) {
|
|
|
2204
2671
|
return new FallbackBlockSource(new PublicApiBlockSource(buildHttpClient(), referencedIndexEventTypes(subgraph), canSparseScan(subgraph) ? sparseProbeTargets(subgraph) : undefined, needsTransactionData(subgraph)), postgresBlockSource);
|
|
2205
2672
|
}
|
|
2206
2673
|
if (process.env.SUBGRAPH_SOURCE === "streams-index" && subgraph) {
|
|
2207
|
-
|
|
2674
|
+
logger5.debug("Subgraph not streams-index eligible, using DB tap", {
|
|
2208
2675
|
subgraph: subgraph.name
|
|
2209
2676
|
});
|
|
2210
2677
|
}
|
|
@@ -2213,7 +2680,7 @@ function resolveBlockSource(subgraph) {
|
|
|
2213
2680
|
|
|
2214
2681
|
// src/runtime/outbox-emit.ts
|
|
2215
2682
|
import { createHash as createHash2 } from "node:crypto";
|
|
2216
|
-
import { logger as
|
|
2683
|
+
import { logger as logger6 } from "@secondlayer/shared/logger";
|
|
2217
2684
|
var loggedKillSwitch = false;
|
|
2218
2685
|
var OP_VERB = {
|
|
2219
2686
|
insert: "created",
|
|
@@ -2237,7 +2704,7 @@ function stableStringify(obj) {
|
|
|
2237
2704
|
async function emitSubscriptionOutbox(tx, subgraphName, manifest, matcher, blockHeight) {
|
|
2238
2705
|
if (!isEmitOutboxEnabled()) {
|
|
2239
2706
|
if (!loggedKillSwitch) {
|
|
2240
|
-
|
|
2707
|
+
logger6.warn("SECONDLAYER_EMIT_OUTBOX=false — outbox emission bypassed");
|
|
2241
2708
|
loggedKillSwitch = true;
|
|
2242
2709
|
}
|
|
2243
2710
|
return 0;
|
|
@@ -2282,7 +2749,7 @@ function sandboxEnabled(subgraph) {
|
|
|
2282
2749
|
// src/runtime/sandbox/host.ts
|
|
2283
2750
|
import { tmpdir } from "node:os";
|
|
2284
2751
|
import { fileURLToPath } from "node:url";
|
|
2285
|
-
import { logger as
|
|
2752
|
+
import { logger as logger7 } from "@secondlayer/shared/logger";
|
|
2286
2753
|
|
|
2287
2754
|
// src/runtime/sandbox/bundle.ts
|
|
2288
2755
|
import esbuild from "esbuild";
|
|
@@ -2362,7 +2829,7 @@ function spawnSandboxProcess() {
|
|
|
2362
2829
|
break;
|
|
2363
2830
|
const text = decoder.decode(value, { stream: true });
|
|
2364
2831
|
if (text.trim()) {
|
|
2365
|
-
|
|
2832
|
+
logger7.warn("Sandbox subprocess stderr", { stderr: text.trim() });
|
|
2366
2833
|
}
|
|
2367
2834
|
}
|
|
2368
2835
|
} catch {}
|
|
@@ -2420,7 +2887,7 @@ async function ensureSandboxProcess(subgraphName, version, handlerCode) {
|
|
|
2420
2887
|
try {
|
|
2421
2888
|
await initSandboxProcess(existing.proc, subgraphName, version, handlerCode);
|
|
2422
2889
|
existing.version = version;
|
|
2423
|
-
|
|
2890
|
+
logger7.info("Sandbox subprocess handler reloaded", {
|
|
2424
2891
|
subgraph: subgraphName,
|
|
2425
2892
|
version
|
|
2426
2893
|
});
|
|
@@ -2428,7 +2895,7 @@ async function ensureSandboxProcess(subgraphName, version, handlerCode) {
|
|
|
2428
2895
|
} catch (err) {
|
|
2429
2896
|
existing.proc.kill();
|
|
2430
2897
|
pool.delete(subgraphName);
|
|
2431
|
-
|
|
2898
|
+
logger7.warn("Sandbox subprocess re-init failed; respawning", {
|
|
2432
2899
|
subgraph: subgraphName,
|
|
2433
2900
|
error: err instanceof Error ? err.message : String(err)
|
|
2434
2901
|
});
|
|
@@ -2511,7 +2978,7 @@ async function runBlockThroughProcess(entry, subgraphName, hostCtx, block, match
|
|
|
2511
2978
|
return result;
|
|
2512
2979
|
} catch (err) {
|
|
2513
2980
|
evictSandboxWorker(subgraphName);
|
|
2514
|
-
|
|
2981
|
+
logger7.warn("Sandbox subprocess run failed; subprocess evicted", {
|
|
2515
2982
|
subgraph: subgraphName,
|
|
2516
2983
|
blockHeight: block.height,
|
|
2517
2984
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -2582,7 +3049,7 @@ async function answerRead(hostCtx, msg) {
|
|
|
2582
3049
|
|
|
2583
3050
|
// src/runtime/subscription-state.ts
|
|
2584
3051
|
import { listSubscriptions } from "@secondlayer/shared/db/queries/subscriptions";
|
|
2585
|
-
import { sql as
|
|
3052
|
+
import { sql as sql3 } from "kysely";
|
|
2586
3053
|
|
|
2587
3054
|
// src/runtime/emitter-matcher.ts
|
|
2588
3055
|
function isPrimitive(v) {
|
|
@@ -2746,7 +3213,7 @@ class SubscriptionMatcher {
|
|
|
2746
3213
|
// src/runtime/subscription-state.ts
|
|
2747
3214
|
var matcher = new SubscriptionMatcher;
|
|
2748
3215
|
async function refreshMatcher(db) {
|
|
2749
|
-
const rows = await
|
|
3216
|
+
const rows = await sql3`
|
|
2750
3217
|
SELECT * FROM subscriptions WHERE status = 'active'
|
|
2751
3218
|
`.execute(db);
|
|
2752
3219
|
matcher.setAll(rows.rows);
|
|
@@ -2768,7 +3235,7 @@ async function resolveRoute(subgraphName, targetDb) {
|
|
|
2768
3235
|
version: row?.version ?? ""
|
|
2769
3236
|
};
|
|
2770
3237
|
routeCache.set(subgraphName, route);
|
|
2771
|
-
|
|
3238
|
+
logger8.info("subgraph handler dispatch path resolved", {
|
|
2772
3239
|
subgraph: subgraphName,
|
|
2773
3240
|
path: sandboxEnabled({ sandbox_workers: route.sandboxWorkers }) ? "sandbox" : "in-process"
|
|
2774
3241
|
});
|
|
@@ -2792,8 +3259,8 @@ async function resolveFactoryContracts(subgraph, blockHeight, schemaName, db, tx
|
|
|
2792
3259
|
for (const [discoveringSource, factory] of factories) {
|
|
2793
3260
|
const known = new Set;
|
|
2794
3261
|
try {
|
|
2795
|
-
const rows = await
|
|
2796
|
-
SELECT address FROM ${
|
|
3262
|
+
const rows = await sql4`
|
|
3263
|
+
SELECT address FROM ${sql4.raw(`"${schemaName}"."_factory_addresses"`)}
|
|
2797
3264
|
WHERE source_name = ${discoveringSource} AND block_height <= ${blockHeight}
|
|
2798
3265
|
`.execute(db);
|
|
2799
3266
|
for (const row of rows.rows)
|
|
@@ -2821,8 +3288,8 @@ async function persistFactoryDiscoveries(schemaName, db, blockHeight, discovered
|
|
|
2821
3288
|
if (discovered.length === 0)
|
|
2822
3289
|
return;
|
|
2823
3290
|
for (const { sourceName, address } of discovered) {
|
|
2824
|
-
await
|
|
2825
|
-
INSERT INTO ${
|
|
3291
|
+
await sql4`
|
|
3292
|
+
INSERT INTO ${sql4.raw(`"${schemaName}"."_factory_addresses"`)}
|
|
2826
3293
|
(source_name, address, block_height)
|
|
2827
3294
|
VALUES (${sourceName}, ${address}, ${blockHeight})
|
|
2828
3295
|
ON CONFLICT (source_name, address) DO NOTHING
|
|
@@ -2882,7 +3349,7 @@ async function processBlockWithRetry(subgraph, subgraphName, blockHeight, opts,
|
|
|
2882
3349
|
const delay = retryDelaysMs[attempt];
|
|
2883
3350
|
if (delay === undefined)
|
|
2884
3351
|
break;
|
|
2885
|
-
|
|
3352
|
+
logger8.warn("Block processing failed, retrying", {
|
|
2886
3353
|
subgraph: subgraphName,
|
|
2887
3354
|
blockHeight,
|
|
2888
3355
|
attempt: attempt + 1,
|
|
@@ -2895,7 +3362,7 @@ async function processBlockWithRetry(subgraph, subgraphName, blockHeight, opts,
|
|
|
2895
3362
|
throw lastError;
|
|
2896
3363
|
}
|
|
2897
3364
|
async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
2898
|
-
const targetDb =
|
|
3365
|
+
const targetDb = getTargetDb2();
|
|
2899
3366
|
const blockStart = performance.now();
|
|
2900
3367
|
const result = {
|
|
2901
3368
|
blockHeight,
|
|
@@ -2914,7 +3381,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2914
3381
|
} else {
|
|
2915
3382
|
const data = (await resolveBlockSource(subgraph).loadBlockRange(blockHeight, blockHeight)).get(blockHeight);
|
|
2916
3383
|
if (!data) {
|
|
2917
|
-
|
|
3384
|
+
logger8.debug("Block not found or non-canonical for subgraph processing", {
|
|
2918
3385
|
subgraph: subgraphName,
|
|
2919
3386
|
blockHeight
|
|
2920
3387
|
});
|
|
@@ -2980,7 +3447,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2980
3447
|
{ start: blockHeight, end: blockHeight, reason: "processing_error" }
|
|
2981
3448
|
]);
|
|
2982
3449
|
} else {
|
|
2983
|
-
|
|
3450
|
+
logger8.warn("Block errors not recorded as a gap — no subgraphId", {
|
|
2984
3451
|
subgraph: subgraphName,
|
|
2985
3452
|
blockHeight,
|
|
2986
3453
|
errors: rr.errors
|
|
@@ -3053,7 +3520,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
3053
3520
|
});
|
|
3054
3521
|
} catch (err) {
|
|
3055
3522
|
if (err instanceof CursorRaceLostError || err instanceof LiveCursorRaceLostError) {
|
|
3056
|
-
|
|
3523
|
+
logger8.warn("cursor race lost — block already covered", {
|
|
3057
3524
|
subgraph: subgraphName,
|
|
3058
3525
|
blockHeight,
|
|
3059
3526
|
error: err.message
|
|
@@ -3078,10 +3545,10 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
3078
3545
|
try {
|
|
3079
3546
|
const tables = Object.keys(subgraph.schema);
|
|
3080
3547
|
for (const table of tables) {
|
|
3081
|
-
const { rows } = await
|
|
3548
|
+
const { rows } = await sql4.raw(`SELECT n_live_tup AS count FROM pg_stat_user_tables WHERE schemaname = '${schemaName}' AND relname = '${table}'`).execute(route.dataDb);
|
|
3082
3549
|
const count = Number(rows[0]?.count ?? 0);
|
|
3083
3550
|
if (count >= 1e7) {
|
|
3084
|
-
|
|
3551
|
+
logger8.warn("Subgraph table exceeds 10M rows (estimate)", {
|
|
3085
3552
|
subgraph: subgraphName,
|
|
3086
3553
|
table,
|
|
3087
3554
|
count
|
|
@@ -3089,13 +3556,13 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
3089
3556
|
}
|
|
3090
3557
|
}
|
|
3091
3558
|
} catch (err) {
|
|
3092
|
-
|
|
3559
|
+
logger8.debug("Row count sample failed", {
|
|
3093
3560
|
subgraph: subgraphName,
|
|
3094
3561
|
error: err instanceof Error ? err.message : String(err)
|
|
3095
3562
|
});
|
|
3096
3563
|
}
|
|
3097
3564
|
if (journalEnabled(opts)) {
|
|
3098
|
-
await
|
|
3565
|
+
await sql4.raw(`DELETE FROM "${schemaName}"."_journal" WHERE "block_height" < ${blockHeight - JOURNAL_RETENTION_BLOCKS}`).execute(route.dataDb).catch(() => {});
|
|
3099
3566
|
}
|
|
3100
3567
|
}
|
|
3101
3568
|
return result;
|
|
@@ -3165,9 +3632,9 @@ class StatsAccumulator {
|
|
|
3165
3632
|
}
|
|
3166
3633
|
|
|
3167
3634
|
// src/runtime/catchup.ts
|
|
3168
|
-
import { getTargetDb as
|
|
3635
|
+
import { getTargetDb as getTargetDb3, sql as sql5 } from "@secondlayer/shared/db";
|
|
3169
3636
|
import { getSubgraph } from "@secondlayer/shared/db/queries/subgraphs";
|
|
3170
|
-
import { logger as
|
|
3637
|
+
import { logger as logger9 } from "@secondlayer/shared/logger";
|
|
3171
3638
|
|
|
3172
3639
|
// src/runtime/catchup-leader.ts
|
|
3173
3640
|
import {
|
|
@@ -3297,8 +3764,8 @@ async function withSubgraphBlockLock(name, fn) {
|
|
|
3297
3764
|
}
|
|
3298
3765
|
}
|
|
3299
3766
|
async function withSubgraphBlockAdvisoryLock(name, fn) {
|
|
3300
|
-
return
|
|
3301
|
-
await
|
|
3767
|
+
return getTargetDb3().transaction().execute(async (lockTx) => {
|
|
3768
|
+
await sql5`SELECT pg_advisory_xact_lock(hashtext(${`subgraph-block:${name}`}))`.execute(lockTx);
|
|
3302
3769
|
return fn();
|
|
3303
3770
|
});
|
|
3304
3771
|
}
|
|
@@ -3359,7 +3826,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
3359
3826
|
catchingUp.add(subgraphName);
|
|
3360
3827
|
try {
|
|
3361
3828
|
const source = resolveBlockSource(subgraph);
|
|
3362
|
-
const targetDb =
|
|
3829
|
+
const targetDb = getTargetDb3();
|
|
3363
3830
|
const subgraphRow = await getSubgraph(targetDb, subgraphName);
|
|
3364
3831
|
if (!subgraphRow)
|
|
3365
3832
|
return 0;
|
|
@@ -3371,7 +3838,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
3371
3838
|
if (usesDecodedIndexPlane(subgraph)) {
|
|
3372
3839
|
const bound = await boundSourceTip(rawTip, decoderNamesForSubgraph(subgraph));
|
|
3373
3840
|
if (!bound.ok) {
|
|
3374
|
-
|
|
3841
|
+
logger9.warn("Subgraph catch-up stalled: missing decoder checkpoint", {
|
|
3375
3842
|
event: "subgraph_catchup_decoder_stall",
|
|
3376
3843
|
subgraph: subgraphName,
|
|
3377
3844
|
missing: bound.missing
|
|
@@ -3379,7 +3846,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
3379
3846
|
return 0;
|
|
3380
3847
|
}
|
|
3381
3848
|
if (bound.floor !== null && bound.floor < rawTip) {
|
|
3382
|
-
|
|
3849
|
+
logger9.debug("Subgraph catch-up tip bounded by decoder progress", {
|
|
3383
3850
|
event: "subgraph_catchup_decoder_floor",
|
|
3384
3851
|
subgraph: subgraphName,
|
|
3385
3852
|
rawTip,
|
|
@@ -3394,7 +3861,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
3394
3861
|
const subgraphStart = Number(subgraphRow.start_block) || 1;
|
|
3395
3862
|
const startBlock = Math.max(lastProcessedBlock + 1, subgraphStart);
|
|
3396
3863
|
const totalBlocks = chainTip - lastProcessedBlock;
|
|
3397
|
-
|
|
3864
|
+
logger9.info("Subgraph catch-up starting", {
|
|
3398
3865
|
subgraph: subgraphName,
|
|
3399
3866
|
from: startBlock,
|
|
3400
3867
|
to: chainTip,
|
|
@@ -3410,7 +3877,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
3410
3877
|
let nextBatchPromise = batchConfig.prefetch ? source.loadBlockRange(currentHeight, prefetchedBatchEnd) : undefined;
|
|
3411
3878
|
while (currentHeight <= chainTip) {
|
|
3412
3879
|
if (!isCatchUpLeader()) {
|
|
3413
|
-
|
|
3880
|
+
logger9.info("Lost catch-up leadership, stopping catch-up", {
|
|
3414
3881
|
subgraph: subgraphName,
|
|
3415
3882
|
processed
|
|
3416
3883
|
});
|
|
@@ -3418,7 +3885,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
3418
3885
|
}
|
|
3419
3886
|
const currentRow = await getSubgraph(targetDb, subgraphName);
|
|
3420
3887
|
if (!currentRow || currentRow.status !== "active") {
|
|
3421
|
-
|
|
3888
|
+
logger9.info("Subgraph status changed, stopping catch-up", {
|
|
3422
3889
|
subgraph: subgraphName,
|
|
3423
3890
|
status: currentRow?.status ?? "deleted"
|
|
3424
3891
|
});
|
|
@@ -3447,7 +3914,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
3447
3914
|
blockData = (await source.loadBlockRange(height, height)).get(height);
|
|
3448
3915
|
}
|
|
3449
3916
|
if (!blockData) {
|
|
3450
|
-
|
|
3917
|
+
logger9.warn("Block missing during catch-up, deferring to next tick", {
|
|
3451
3918
|
subgraph: subgraphName,
|
|
3452
3919
|
blockHeight: height
|
|
3453
3920
|
});
|
|
@@ -3468,12 +3935,12 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
3468
3935
|
});
|
|
3469
3936
|
} catch (err) {
|
|
3470
3937
|
if (err instanceof ReorgEpochChangedError) {
|
|
3471
|
-
|
|
3938
|
+
logger9.info("Reorg detected mid catch-up, aborting walk without writing further progress", { subgraph: subgraphName, blockHeight: height });
|
|
3472
3939
|
stopCatchup = true;
|
|
3473
3940
|
break;
|
|
3474
3941
|
}
|
|
3475
3942
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
3476
|
-
|
|
3943
|
+
logger9.error("Block processing failed persistently during catch-up", {
|
|
3477
3944
|
subgraph: subgraphName,
|
|
3478
3945
|
blockHeight: height,
|
|
3479
3946
|
error: errorMsg
|
|
@@ -3492,7 +3959,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
3492
3959
|
}
|
|
3493
3960
|
}
|
|
3494
3961
|
if (processed % LOG_INTERVAL === 0) {
|
|
3495
|
-
|
|
3962
|
+
logger9.info("Subgraph catch-up progress", {
|
|
3496
3963
|
subgraph: subgraphName,
|
|
3497
3964
|
processed,
|
|
3498
3965
|
total: totalBlocks,
|
|
@@ -3508,7 +3975,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
3508
3975
|
currentHeight = batchEnd + 1;
|
|
3509
3976
|
}
|
|
3510
3977
|
await stats.flush(targetDb);
|
|
3511
|
-
|
|
3978
|
+
logger9.info("Subgraph catch-up complete", {
|
|
3512
3979
|
subgraph: subgraphName,
|
|
3513
3980
|
processed
|
|
3514
3981
|
});
|
|
@@ -3520,16 +3987,16 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
3520
3987
|
|
|
3521
3988
|
// src/runtime/reorg.ts
|
|
3522
3989
|
import { getErrorMessage as getErrorMessage2 } from "@secondlayer/shared";
|
|
3523
|
-
import { getRawClient, getTargetDb as
|
|
3990
|
+
import { getRawClient, getTargetDb as getTargetDb4 } from "@secondlayer/shared/db";
|
|
3524
3991
|
import { listSubgraphs } from "@secondlayer/shared/db/queries/subgraphs";
|
|
3525
|
-
import { logger as
|
|
3526
|
-
import { sql as
|
|
3992
|
+
import { logger as logger10 } from "@secondlayer/shared/logger";
|
|
3993
|
+
import { sql as sql6 } from "kysely";
|
|
3527
3994
|
async function handleSubgraphReorg(blockHeight, loadSubgraphDef) {
|
|
3528
|
-
const targetDb =
|
|
3995
|
+
const targetDb = getTargetDb4();
|
|
3529
3996
|
const activeSubgraphs = (await listSubgraphs(targetDb)).filter((v) => v.status === "active");
|
|
3530
3997
|
if (activeSubgraphs.length === 0)
|
|
3531
3998
|
return;
|
|
3532
|
-
|
|
3999
|
+
logger10.info("Propagating reorg to subgraphs", {
|
|
3533
4000
|
blockHeight,
|
|
3534
4001
|
subgraphCount: activeSubgraphs.length
|
|
3535
4002
|
});
|
|
@@ -3540,13 +4007,13 @@ async function handleSubgraphReorg(blockHeight, loadSubgraphDef) {
|
|
|
3540
4007
|
continue;
|
|
3541
4008
|
bumpReorgEpoch(sg.name);
|
|
3542
4009
|
await withSubgraphBlockLock(sg.name, async () => {
|
|
3543
|
-
await
|
|
3544
|
-
await
|
|
4010
|
+
await getTargetDb4().transaction().execute(async (lockTx) => {
|
|
4011
|
+
await sql6`SELECT pg_advisory_xact_lock(hashtext(${`subgraph-reorg:${sg.name}`}))`.execute(lockTx);
|
|
3545
4012
|
await reorgOneSubgraph(sg, schema, blockHeight, loadSubgraphDef);
|
|
3546
4013
|
});
|
|
3547
4014
|
});
|
|
3548
4015
|
} catch (err) {
|
|
3549
|
-
|
|
4016
|
+
logger10.error("Subgraph reorg handling failed", {
|
|
3550
4017
|
subgraph: sg.name,
|
|
3551
4018
|
blockHeight,
|
|
3552
4019
|
error: getErrorMessage2(err)
|
|
@@ -3555,7 +4022,7 @@ async function handleSubgraphReorg(blockHeight, loadSubgraphDef) {
|
|
|
3555
4022
|
}
|
|
3556
4023
|
}
|
|
3557
4024
|
async function reorgOneSubgraph(sg, schema, blockHeight, loadSubgraphDef) {
|
|
3558
|
-
const targetDb =
|
|
4025
|
+
const targetDb = getTargetDb4();
|
|
3559
4026
|
const schemaName = sg.schema_name ?? pgSchemaName(sg.name);
|
|
3560
4027
|
const client = getRawClient("target");
|
|
3561
4028
|
const tableNames = Object.keys(schema);
|
|
@@ -3584,7 +4051,7 @@ async function reorgOneSubgraph(sg, schema, blockHeight, loadSubgraphDef) {
|
|
|
3584
4051
|
await tx.unsafe(`DELETE FROM "${schemaName}"."_journal" WHERE block_height >= $1`, [blockHeight]);
|
|
3585
4052
|
});
|
|
3586
4053
|
} else {
|
|
3587
|
-
|
|
4054
|
+
logger10.warn("Subgraph has no revert journal — falling back to height delete (accumulator rows may lose history)", { subgraph: sg.name, blockHeight });
|
|
3588
4055
|
for (const tableName of tableNames) {
|
|
3589
4056
|
await client.unsafe(`DELETE FROM "${schemaName}"."${tableName}" WHERE "_block_height" >= $1`, [blockHeight]);
|
|
3590
4057
|
}
|
|
@@ -3594,6 +4061,7 @@ async function reorgOneSubgraph(sg, schema, blockHeight, loadSubgraphDef) {
|
|
|
3594
4061
|
DELETE FROM "${schemaName}"."_factory_addresses" WHERE block_height >= ${Number(blockHeight)};
|
|
3595
4062
|
END IF;
|
|
3596
4063
|
END $$`);
|
|
4064
|
+
await targetDb.deleteFrom("subgraph_violations").where("subgraph_name", "=", sg.name).where("block_height", ">=", blockHeight).execute();
|
|
3597
4065
|
for (const [tableName, rows] of Object.entries(revertedByTable)) {
|
|
3598
4066
|
if (rows.length === 0)
|
|
3599
4067
|
continue;
|
|
@@ -3627,7 +4095,7 @@ async function reorgOneSubgraph(sg, schema, blockHeight, loadSubgraphDef) {
|
|
|
3627
4095
|
}).as("row_pk"),
|
|
3628
4096
|
eb2.val("pending").as("status")
|
|
3629
4097
|
]).where("subgraph_name", "=", sg.name).where("table_name", "=", tableName).where("status", "=", "active")).onConflict((oc) => oc.columns(["subscription_id", "dedup_key"]).doNothing()).execute().catch((err) => {
|
|
3630
|
-
|
|
4098
|
+
logger10.error("Failed to emit revert event for subscriptions", {
|
|
3631
4099
|
event: "reorg_revert_emit_dropped",
|
|
3632
4100
|
subgraph: sg.name,
|
|
3633
4101
|
table: tableName,
|
|
@@ -3636,14 +4104,14 @@ async function reorgOneSubgraph(sg, schema, blockHeight, loadSubgraphDef) {
|
|
|
3636
4104
|
});
|
|
3637
4105
|
});
|
|
3638
4106
|
}
|
|
3639
|
-
|
|
4107
|
+
logger10.info("Subgraph reorg cleanup done", {
|
|
3640
4108
|
subgraph: sg.name,
|
|
3641
4109
|
blockHeight,
|
|
3642
4110
|
tablesAffected: Object.keys(revertedByTable).length
|
|
3643
4111
|
});
|
|
3644
4112
|
const def = await loadSubgraphDef(sg);
|
|
3645
4113
|
await processBlock(def, sg.name, blockHeight, { reorgRewind: true });
|
|
3646
|
-
|
|
4114
|
+
logger10.info("Subgraph reorg reprocessed", {
|
|
3647
4115
|
subgraph: sg.name,
|
|
3648
4116
|
blockHeight
|
|
3649
4117
|
});
|
|
@@ -3652,5 +4120,5 @@ export {
|
|
|
3652
4120
|
handleSubgraphReorg
|
|
3653
4121
|
};
|
|
3654
4122
|
|
|
3655
|
-
//# debugId=
|
|
4123
|
+
//# debugId=C83516F4FE20D8A864756E2164756E21
|
|
3656
4124
|
//# sourceMappingURL=reorg.js.map
|