@secondlayer/subgraphs 3.5.0 → 3.6.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.js +325 -112
- package/dist/src/index.js.map +12 -10
- package/dist/src/runtime/block-processor.js +290 -29
- package/dist/src/runtime/block-processor.js.map +8 -5
- package/dist/src/runtime/catchup.js +304 -90
- package/dist/src/runtime/catchup.js.map +9 -7
- package/dist/src/runtime/processor.js +431 -170
- package/dist/src/runtime/processor.js.map +14 -11
- package/dist/src/runtime/reindex.js +323 -110
- package/dist/src/runtime/reindex.js.map +10 -8
- package/dist/src/runtime/reorg.js +296 -35
- package/dist/src/runtime/reorg.js.map +8 -5
- package/dist/src/runtime/runner.d.ts +6 -1
- package/dist/src/runtime/runner.js +7 -6
- package/dist/src/runtime/runner.js.map +3 -3
- package/dist/src/schema/index.js.map +1 -1
- package/dist/src/service.js +434 -173
- package/dist/src/service.js.map +13 -10
- package/package.json +2 -2
|
@@ -518,21 +518,21 @@ function buildEventPayload(filter, tx, event) {
|
|
|
518
518
|
return {
|
|
519
519
|
sender: decoded.sender,
|
|
520
520
|
recipient: decoded.recipient,
|
|
521
|
-
tokenId: decoded.value,
|
|
521
|
+
tokenId: decoded.raw_value ?? decoded.value,
|
|
522
522
|
assetIdentifier: decoded.asset_identifier,
|
|
523
523
|
tx: txMeta
|
|
524
524
|
};
|
|
525
525
|
case "nft_mint":
|
|
526
526
|
return {
|
|
527
527
|
recipient: decoded.recipient,
|
|
528
|
-
tokenId: decoded.value,
|
|
528
|
+
tokenId: decoded.raw_value ?? decoded.value,
|
|
529
529
|
assetIdentifier: decoded.asset_identifier,
|
|
530
530
|
tx: txMeta
|
|
531
531
|
};
|
|
532
532
|
case "nft_burn":
|
|
533
533
|
return {
|
|
534
534
|
sender: decoded.sender,
|
|
535
|
-
tokenId: decoded.value,
|
|
535
|
+
tokenId: decoded.raw_value ?? decoded.value,
|
|
536
536
|
assetIdentifier: decoded.asset_identifier,
|
|
537
537
|
tx: txMeta
|
|
538
538
|
};
|
|
@@ -564,8 +564,8 @@ function buildEventPayload(filter, tx, event) {
|
|
|
564
564
|
tx: txMeta
|
|
565
565
|
};
|
|
566
566
|
case "print_event": {
|
|
567
|
-
const
|
|
568
|
-
const rawValue =
|
|
567
|
+
const rawHex = event.data?.raw_value;
|
|
568
|
+
const rawValue = typeof rawHex === "string" && rawHex.startsWith("0x") ? decodeClarityValue(rawHex) : decoded.value;
|
|
569
569
|
const clarityObj = rawValue && typeof rawValue === "object" && !Array.isArray(rawValue) ? rawValue : null;
|
|
570
570
|
const topic = clarityObj?.topic ? String(clarityObj.topic) : decoded.topic ?? "";
|
|
571
571
|
const { topic: _, ...rest } = clarityObj ?? {};
|
|
@@ -949,10 +949,7 @@ function matchSources(sources, transactions, events, traitContracts = new Map) {
|
|
|
949
949
|
}
|
|
950
950
|
|
|
951
951
|
// src/runtime/block-processor.ts
|
|
952
|
-
import {
|
|
953
|
-
getSourceDb,
|
|
954
|
-
getTargetDb
|
|
955
|
-
} from "@secondlayer/shared/db";
|
|
952
|
+
import { getTargetDb } from "@secondlayer/shared/db";
|
|
956
953
|
import { resolveTraitContractIds } from "@secondlayer/shared/db/queries/contracts";
|
|
957
954
|
import {
|
|
958
955
|
isByoSubgraph,
|
|
@@ -960,15 +957,287 @@ import {
|
|
|
960
957
|
resolveSubgraphDb,
|
|
961
958
|
updateSubgraphStatus
|
|
962
959
|
} from "@secondlayer/shared/db/queries/subgraphs";
|
|
963
|
-
import { logger as
|
|
960
|
+
import { logger as logger5 } from "@secondlayer/shared/logger";
|
|
964
961
|
import { sql as sql3 } from "kysely";
|
|
965
962
|
|
|
966
963
|
// src/schema/utils.ts
|
|
967
964
|
import { pgSchemaName } from "@secondlayer/shared/db/queries/subgraphs";
|
|
968
965
|
|
|
966
|
+
// src/runtime/block-source.ts
|
|
967
|
+
import { getSourceDb } from "@secondlayer/shared/db";
|
|
968
|
+
import { IndexHttpClient } from "@secondlayer/shared/index-http";
|
|
969
|
+
import { logger as logger3 } from "@secondlayer/shared/logger";
|
|
970
|
+
|
|
971
|
+
// src/runtime/batch-loader.ts
|
|
972
|
+
async function loadBlockRange(db, fromHeight, toHeight) {
|
|
973
|
+
const [blocks, txs, events] = await Promise.all([
|
|
974
|
+
db.selectFrom("blocks").selectAll().where("height", ">=", fromHeight).where("height", "<=", toHeight).where("canonical", "=", true).execute(),
|
|
975
|
+
db.selectFrom("transactions").selectAll().where("block_height", ">=", fromHeight).where("block_height", "<=", toHeight).execute(),
|
|
976
|
+
db.selectFrom("events").selectAll().where("block_height", ">=", fromHeight).where("block_height", "<=", toHeight).execute()
|
|
977
|
+
]);
|
|
978
|
+
const txsByHeight = new Map;
|
|
979
|
+
for (const tx of txs) {
|
|
980
|
+
const h = Number(tx.block_height);
|
|
981
|
+
const list = txsByHeight.get(h) ?? [];
|
|
982
|
+
list.push(tx);
|
|
983
|
+
txsByHeight.set(h, list);
|
|
984
|
+
}
|
|
985
|
+
const eventsByHeight = new Map;
|
|
986
|
+
for (const evt of events) {
|
|
987
|
+
const h = Number(evt.block_height);
|
|
988
|
+
const list = eventsByHeight.get(h) ?? [];
|
|
989
|
+
list.push(evt);
|
|
990
|
+
eventsByHeight.set(h, list);
|
|
991
|
+
}
|
|
992
|
+
const result = new Map;
|
|
993
|
+
for (const block of blocks) {
|
|
994
|
+
const h = Number(block.height);
|
|
995
|
+
result.set(h, {
|
|
996
|
+
block,
|
|
997
|
+
txs: txsByHeight.get(h) ?? [],
|
|
998
|
+
events: eventsByHeight.get(h) ?? []
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
return result;
|
|
1002
|
+
}
|
|
1003
|
+
function avgEventsPerBlock(batch) {
|
|
1004
|
+
if (batch.size === 0)
|
|
1005
|
+
return 0;
|
|
1006
|
+
let totalEvents = 0;
|
|
1007
|
+
for (const data of batch.values()) {
|
|
1008
|
+
totalEvents += data.events.length;
|
|
1009
|
+
}
|
|
1010
|
+
return totalEvents / batch.size;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
// src/runtime/reconstruct.ts
|
|
1014
|
+
function isoToUnixSeconds(iso) {
|
|
1015
|
+
if (!iso)
|
|
1016
|
+
return 0;
|
|
1017
|
+
return Math.floor(new Date(iso).getTime() / 1000);
|
|
1018
|
+
}
|
|
1019
|
+
function reconstructBlock(b) {
|
|
1020
|
+
return {
|
|
1021
|
+
height: b.block_height,
|
|
1022
|
+
hash: b.block_hash,
|
|
1023
|
+
parent_hash: b.parent_hash,
|
|
1024
|
+
burn_block_height: b.burn_block_height,
|
|
1025
|
+
burn_block_hash: b.burn_block_hash,
|
|
1026
|
+
timestamp: isoToUnixSeconds(b.block_time),
|
|
1027
|
+
canonical: true,
|
|
1028
|
+
created_at: new Date(0)
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
1031
|
+
function reconstructTransaction(t) {
|
|
1032
|
+
return {
|
|
1033
|
+
tx_id: t.tx_id,
|
|
1034
|
+
block_height: t.block_height,
|
|
1035
|
+
tx_index: t.tx_index,
|
|
1036
|
+
type: t.tx_type,
|
|
1037
|
+
sender: t.sender,
|
|
1038
|
+
status: t.status,
|
|
1039
|
+
contract_id: t.contract_call?.contract_id ?? t.smart_contract?.contract_id ?? null,
|
|
1040
|
+
function_name: t.contract_call?.function_name ?? null,
|
|
1041
|
+
function_args: t.contract_call?.function_args_hex ?? [],
|
|
1042
|
+
raw_result: t.contract_call?.result_hex ?? null,
|
|
1043
|
+
raw_tx: "",
|
|
1044
|
+
created_at: new Date(0)
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
function reconstructEvent(e) {
|
|
1048
|
+
const base = {
|
|
1049
|
+
id: `${e.tx_id}#${e.event_index}`,
|
|
1050
|
+
tx_id: e.tx_id,
|
|
1051
|
+
block_height: e.block_height,
|
|
1052
|
+
event_index: e.event_index,
|
|
1053
|
+
created_at: new Date(0)
|
|
1054
|
+
};
|
|
1055
|
+
switch (e.event_type) {
|
|
1056
|
+
case "ft_transfer":
|
|
1057
|
+
case "ft_mint":
|
|
1058
|
+
case "ft_burn":
|
|
1059
|
+
return {
|
|
1060
|
+
...base,
|
|
1061
|
+
type: `${e.event_type}_event`,
|
|
1062
|
+
data: {
|
|
1063
|
+
asset_identifier: e.asset_identifier,
|
|
1064
|
+
sender: e.sender,
|
|
1065
|
+
recipient: e.recipient,
|
|
1066
|
+
amount: e.amount
|
|
1067
|
+
}
|
|
1068
|
+
};
|
|
1069
|
+
case "nft_transfer":
|
|
1070
|
+
case "nft_mint":
|
|
1071
|
+
case "nft_burn":
|
|
1072
|
+
return {
|
|
1073
|
+
...base,
|
|
1074
|
+
type: `${e.event_type}_event`,
|
|
1075
|
+
data: {
|
|
1076
|
+
asset_identifier: e.asset_identifier,
|
|
1077
|
+
sender: e.sender,
|
|
1078
|
+
recipient: e.recipient,
|
|
1079
|
+
raw_value: e.value
|
|
1080
|
+
}
|
|
1081
|
+
};
|
|
1082
|
+
case "stx_transfer":
|
|
1083
|
+
case "stx_mint":
|
|
1084
|
+
case "stx_burn":
|
|
1085
|
+
return {
|
|
1086
|
+
...base,
|
|
1087
|
+
type: `${e.event_type}_event`,
|
|
1088
|
+
data: {
|
|
1089
|
+
sender: e.sender,
|
|
1090
|
+
recipient: e.recipient,
|
|
1091
|
+
amount: e.amount,
|
|
1092
|
+
..."memo" in e ? { memo: e.memo ?? undefined } : {}
|
|
1093
|
+
}
|
|
1094
|
+
};
|
|
1095
|
+
case "stx_lock":
|
|
1096
|
+
return {
|
|
1097
|
+
...base,
|
|
1098
|
+
type: "stx_lock_event",
|
|
1099
|
+
data: {
|
|
1100
|
+
locked_address: e.sender,
|
|
1101
|
+
locked_amount: e.amount,
|
|
1102
|
+
unlock_height: e.payload.unlock_height
|
|
1103
|
+
}
|
|
1104
|
+
};
|
|
1105
|
+
case "print":
|
|
1106
|
+
return {
|
|
1107
|
+
...base,
|
|
1108
|
+
type: "contract_event",
|
|
1109
|
+
data: {
|
|
1110
|
+
topic: e.payload.topic,
|
|
1111
|
+
contract_id: e.contract_id,
|
|
1112
|
+
contract_identifier: e.contract_id,
|
|
1113
|
+
value: e.payload.value,
|
|
1114
|
+
raw_value: e.payload.raw_value
|
|
1115
|
+
}
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
// src/runtime/block-source.ts
|
|
1121
|
+
class PostgresBlockSource {
|
|
1122
|
+
async getTip() {
|
|
1123
|
+
const progress = await getSourceDb().selectFrom("index_progress").selectAll().where("network", "=", process.env.NETWORK ?? "mainnet").executeTakeFirst();
|
|
1124
|
+
return progress ? Number(progress.highest_seen_block) : 0;
|
|
1125
|
+
}
|
|
1126
|
+
loadBlockRange(fromHeight, toHeight) {
|
|
1127
|
+
return loadBlockRange(getSourceDb(), fromHeight, toHeight);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
var EVENT_FILTER_TO_INDEX_TYPE = {
|
|
1131
|
+
stx_transfer: "stx_transfer",
|
|
1132
|
+
stx_mint: "stx_mint",
|
|
1133
|
+
stx_burn: "stx_burn",
|
|
1134
|
+
stx_lock: "stx_lock",
|
|
1135
|
+
ft_transfer: "ft_transfer",
|
|
1136
|
+
ft_mint: "ft_mint",
|
|
1137
|
+
ft_burn: "ft_burn",
|
|
1138
|
+
nft_transfer: "nft_transfer",
|
|
1139
|
+
nft_mint: "nft_mint",
|
|
1140
|
+
nft_burn: "nft_burn",
|
|
1141
|
+
print_event: "print"
|
|
1142
|
+
};
|
|
1143
|
+
var TX_SOURCE_TYPES = new Set(["contract_call", "contract_deploy"]);
|
|
1144
|
+
var ALL_INDEX_EVENT_TYPES = [
|
|
1145
|
+
...new Set(Object.values(EVENT_FILTER_TO_INDEX_TYPE))
|
|
1146
|
+
];
|
|
1147
|
+
function sourceFilters(subgraph) {
|
|
1148
|
+
const sources = subgraph.sources;
|
|
1149
|
+
return Array.isArray(sources) ? sources : Object.values(sources);
|
|
1150
|
+
}
|
|
1151
|
+
function referencedIndexEventTypes(subgraph) {
|
|
1152
|
+
const filters = sourceFilters(subgraph);
|
|
1153
|
+
if (filters.some((f) => TX_SOURCE_TYPES.has(f.type))) {
|
|
1154
|
+
return ALL_INDEX_EVENT_TYPES;
|
|
1155
|
+
}
|
|
1156
|
+
const types = new Set;
|
|
1157
|
+
for (const f of filters) {
|
|
1158
|
+
const t = EVENT_FILTER_TO_INDEX_TYPE[f.type];
|
|
1159
|
+
if (t)
|
|
1160
|
+
types.add(t);
|
|
1161
|
+
}
|
|
1162
|
+
return [...types];
|
|
1163
|
+
}
|
|
1164
|
+
function isStreamsIndexEligible(subgraph) {
|
|
1165
|
+
if (Array.isArray(subgraph.sources))
|
|
1166
|
+
return false;
|
|
1167
|
+
const filters = sourceFilters(subgraph);
|
|
1168
|
+
if (filters.length === 0)
|
|
1169
|
+
return false;
|
|
1170
|
+
for (const f of filters) {
|
|
1171
|
+
const known = EVENT_FILTER_TO_INDEX_TYPE[f.type] || TX_SOURCE_TYPES.has(f.type);
|
|
1172
|
+
if (!known)
|
|
1173
|
+
return false;
|
|
1174
|
+
}
|
|
1175
|
+
return true;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
class PublicApiBlockSource {
|
|
1179
|
+
http;
|
|
1180
|
+
eventTypes;
|
|
1181
|
+
constructor(http, eventTypes) {
|
|
1182
|
+
this.http = http;
|
|
1183
|
+
this.eventTypes = eventTypes;
|
|
1184
|
+
}
|
|
1185
|
+
getTip() {
|
|
1186
|
+
return this.http.getIndexTip();
|
|
1187
|
+
}
|
|
1188
|
+
async loadBlockRange(fromHeight, toHeight) {
|
|
1189
|
+
const [blocks, txs, eventLists] = await Promise.all([
|
|
1190
|
+
this.http.walkBlocks(fromHeight, toHeight),
|
|
1191
|
+
this.http.walkTransactions(fromHeight, toHeight),
|
|
1192
|
+
Promise.all(this.eventTypes.map((t) => this.http.walkEvents(t, fromHeight, toHeight)))
|
|
1193
|
+
]);
|
|
1194
|
+
const map = new Map;
|
|
1195
|
+
for (const b of blocks) {
|
|
1196
|
+
map.set(b.block_height, {
|
|
1197
|
+
block: reconstructBlock(b),
|
|
1198
|
+
txs: [],
|
|
1199
|
+
events: []
|
|
1200
|
+
});
|
|
1201
|
+
}
|
|
1202
|
+
for (const t of txs) {
|
|
1203
|
+
map.get(t.block_height)?.txs.push(reconstructTransaction(t));
|
|
1204
|
+
}
|
|
1205
|
+
for (const list of eventLists) {
|
|
1206
|
+
for (const e of list) {
|
|
1207
|
+
map.get(e.block_height)?.events.push(reconstructEvent(e));
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
for (const bd of map.values()) {
|
|
1211
|
+
bd.txs.sort((a, b) => a.tx_index - b.tx_index);
|
|
1212
|
+
bd.events.sort((a, b) => a.event_index - b.event_index);
|
|
1213
|
+
}
|
|
1214
|
+
return map;
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
var postgresBlockSource = new PostgresBlockSource;
|
|
1218
|
+
function buildHttpClient() {
|
|
1219
|
+
const baseUrl = process.env.SUBGRAPH_INDEX_API_URL ?? process.env.STREAMS_API_URL ?? "http://api:3800";
|
|
1220
|
+
return new IndexHttpClient({
|
|
1221
|
+
indexBaseUrl: baseUrl,
|
|
1222
|
+
streamsBaseUrl: baseUrl,
|
|
1223
|
+
streamsApiKey: process.env.STREAMS_INTERNAL_API_KEY ?? "sk-sl_streams_l2_internal"
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
function resolveBlockSource(subgraph) {
|
|
1227
|
+
if (process.env.SUBGRAPH_SOURCE === "streams-index" && subgraph && isStreamsIndexEligible(subgraph)) {
|
|
1228
|
+
return new PublicApiBlockSource(buildHttpClient(), referencedIndexEventTypes(subgraph));
|
|
1229
|
+
}
|
|
1230
|
+
if (process.env.SUBGRAPH_SOURCE === "streams-index" && subgraph) {
|
|
1231
|
+
logger3.debug("Subgraph not streams-index eligible, using DB tap", {
|
|
1232
|
+
subgraph: subgraph.name
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
return postgresBlockSource;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
969
1238
|
// src/runtime/outbox-emit.ts
|
|
970
1239
|
import { createHash } from "node:crypto";
|
|
971
|
-
import { logger as
|
|
1240
|
+
import { logger as logger4 } from "@secondlayer/shared/logger";
|
|
972
1241
|
var loggedKillSwitch = false;
|
|
973
1242
|
function isEmitOutboxEnabled() {
|
|
974
1243
|
return process.env.SECONDLAYER_EMIT_OUTBOX !== "false";
|
|
@@ -987,7 +1256,7 @@ function stableStringify(obj) {
|
|
|
987
1256
|
async function emitSubscriptionOutbox(tx, subgraphName, manifest, matcher, blockHeight) {
|
|
988
1257
|
if (!isEmitOutboxEnabled()) {
|
|
989
1258
|
if (!loggedKillSwitch) {
|
|
990
|
-
|
|
1259
|
+
logger4.warn("SECONDLAYER_EMIT_OUTBOX=false — outbox emission bypassed");
|
|
991
1260
|
loggedKillSwitch = true;
|
|
992
1261
|
}
|
|
993
1262
|
return 0;
|
|
@@ -1228,7 +1497,6 @@ async function resolveTraitContracts(subgraph, blockHeight, db) {
|
|
|
1228
1497
|
return resolved;
|
|
1229
1498
|
}
|
|
1230
1499
|
async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
1231
|
-
const sourceDb = getSourceDb();
|
|
1232
1500
|
const targetDb = getTargetDb();
|
|
1233
1501
|
const blockStart = performance.now();
|
|
1234
1502
|
const result = {
|
|
@@ -1246,25 +1514,18 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
1246
1514
|
txs = opts.preloaded.txs;
|
|
1247
1515
|
evts = opts.preloaded.events;
|
|
1248
1516
|
} else {
|
|
1249
|
-
|
|
1250
|
-
if (!
|
|
1251
|
-
|
|
1517
|
+
const data = (await resolveBlockSource(subgraph).loadBlockRange(blockHeight, blockHeight)).get(blockHeight);
|
|
1518
|
+
if (!data) {
|
|
1519
|
+
logger5.debug("Block not found or non-canonical for subgraph processing", {
|
|
1252
1520
|
subgraph: subgraphName,
|
|
1253
1521
|
blockHeight
|
|
1254
1522
|
});
|
|
1255
1523
|
result.skipped = true;
|
|
1256
1524
|
return result;
|
|
1257
1525
|
}
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
blockHeight
|
|
1262
|
-
});
|
|
1263
|
-
result.skipped = true;
|
|
1264
|
-
return result;
|
|
1265
|
-
}
|
|
1266
|
-
txs = await sourceDb.selectFrom("transactions").selectAll().where("block_height", "=", blockHeight).execute();
|
|
1267
|
-
evts = await sourceDb.selectFrom("events").selectAll().where("block_height", "=", blockHeight).execute();
|
|
1526
|
+
block = data.block;
|
|
1527
|
+
txs = data.txs;
|
|
1528
|
+
evts = data.events;
|
|
1268
1529
|
}
|
|
1269
1530
|
const traitContracts = await resolveTraitContracts(subgraph, blockHeight, targetDb);
|
|
1270
1531
|
const matched = matchSources(subgraph.sources, txs, evts, traitContracts);
|
|
@@ -1355,7 +1616,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
1355
1616
|
const { rows } = await sql3.raw(`SELECT n_live_tup AS count FROM pg_stat_user_tables WHERE schemaname = '${schemaName}' AND relname = '${table}'`).execute(route.dataDb);
|
|
1356
1617
|
const count = Number(rows[0]?.count ?? 0);
|
|
1357
1618
|
if (count >= 1e7) {
|
|
1358
|
-
|
|
1619
|
+
logger5.warn("Subgraph table exceeds 10M rows (estimate)", {
|
|
1359
1620
|
subgraph: subgraphName,
|
|
1360
1621
|
table,
|
|
1361
1622
|
count
|
|
@@ -1363,7 +1624,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
1363
1624
|
}
|
|
1364
1625
|
}
|
|
1365
1626
|
} catch (err) {
|
|
1366
|
-
|
|
1627
|
+
logger5.debug("Row count sample failed", {
|
|
1367
1628
|
subgraph: subgraphName,
|
|
1368
1629
|
error: err instanceof Error ? err.message : String(err)
|
|
1369
1630
|
});
|
|
@@ -1436,56 +1697,12 @@ class StatsAccumulator {
|
|
|
1436
1697
|
}
|
|
1437
1698
|
|
|
1438
1699
|
// src/runtime/catchup.ts
|
|
1439
|
-
import {
|
|
1700
|
+
import { getTargetDb as getTargetDb2 } from "@secondlayer/shared/db";
|
|
1440
1701
|
import {
|
|
1441
1702
|
recordGapBatch
|
|
1442
1703
|
} from "@secondlayer/shared/db/queries/subgraph-gaps";
|
|
1443
1704
|
import { getSubgraph } from "@secondlayer/shared/db/queries/subgraphs";
|
|
1444
|
-
import { logger as
|
|
1445
|
-
|
|
1446
|
-
// src/runtime/batch-loader.ts
|
|
1447
|
-
async function loadBlockRange(db, fromHeight, toHeight) {
|
|
1448
|
-
const [blocks, txs, events] = await Promise.all([
|
|
1449
|
-
db.selectFrom("blocks").selectAll().where("height", ">=", fromHeight).where("height", "<=", toHeight).where("canonical", "=", true).execute(),
|
|
1450
|
-
db.selectFrom("transactions").selectAll().where("block_height", ">=", fromHeight).where("block_height", "<=", toHeight).execute(),
|
|
1451
|
-
db.selectFrom("events").selectAll().where("block_height", ">=", fromHeight).where("block_height", "<=", toHeight).execute()
|
|
1452
|
-
]);
|
|
1453
|
-
const txsByHeight = new Map;
|
|
1454
|
-
for (const tx of txs) {
|
|
1455
|
-
const h = Number(tx.block_height);
|
|
1456
|
-
const list = txsByHeight.get(h) ?? [];
|
|
1457
|
-
list.push(tx);
|
|
1458
|
-
txsByHeight.set(h, list);
|
|
1459
|
-
}
|
|
1460
|
-
const eventsByHeight = new Map;
|
|
1461
|
-
for (const evt of events) {
|
|
1462
|
-
const h = Number(evt.block_height);
|
|
1463
|
-
const list = eventsByHeight.get(h) ?? [];
|
|
1464
|
-
list.push(evt);
|
|
1465
|
-
eventsByHeight.set(h, list);
|
|
1466
|
-
}
|
|
1467
|
-
const result = new Map;
|
|
1468
|
-
for (const block of blocks) {
|
|
1469
|
-
const h = Number(block.height);
|
|
1470
|
-
result.set(h, {
|
|
1471
|
-
block,
|
|
1472
|
-
txs: txsByHeight.get(h) ?? [],
|
|
1473
|
-
events: eventsByHeight.get(h) ?? []
|
|
1474
|
-
});
|
|
1475
|
-
}
|
|
1476
|
-
return result;
|
|
1477
|
-
}
|
|
1478
|
-
function avgEventsPerBlock(batch) {
|
|
1479
|
-
if (batch.size === 0)
|
|
1480
|
-
return 0;
|
|
1481
|
-
let totalEvents = 0;
|
|
1482
|
-
for (const data of batch.values()) {
|
|
1483
|
-
totalEvents += data.events.length;
|
|
1484
|
-
}
|
|
1485
|
-
return totalEvents / batch.size;
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1488
|
-
// src/runtime/catchup.ts
|
|
1705
|
+
import { logger as logger6 } from "@secondlayer/shared/logger";
|
|
1489
1706
|
var LOG_INTERVAL = 1000;
|
|
1490
1707
|
var STANDARD_CATCHUP_BATCH_CONFIG = {
|
|
1491
1708
|
defaultBatchSize: 500,
|
|
@@ -1556,22 +1773,19 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
1556
1773
|
return 0;
|
|
1557
1774
|
catchingUp.add(subgraphName);
|
|
1558
1775
|
try {
|
|
1559
|
-
const
|
|
1776
|
+
const source = resolveBlockSource(subgraph);
|
|
1560
1777
|
const targetDb = getTargetDb2();
|
|
1561
1778
|
const subgraphRow = await getSubgraph(targetDb, subgraphName);
|
|
1562
1779
|
if (!subgraphRow)
|
|
1563
1780
|
return 0;
|
|
1564
1781
|
const lastProcessedBlock = Number(subgraphRow.last_processed_block);
|
|
1565
|
-
const
|
|
1566
|
-
if (
|
|
1567
|
-
return 0;
|
|
1568
|
-
const chainTip = Number(progress.highest_seen_block);
|
|
1569
|
-
if (lastProcessedBlock >= chainTip)
|
|
1782
|
+
const chainTip = await source.getTip();
|
|
1783
|
+
if (chainTip <= 0 || lastProcessedBlock >= chainTip)
|
|
1570
1784
|
return 0;
|
|
1571
1785
|
const subgraphStart = Number(subgraphRow.start_block) || 1;
|
|
1572
1786
|
const startBlock = Math.max(lastProcessedBlock + 1, subgraphStart);
|
|
1573
1787
|
const totalBlocks = chainTip - lastProcessedBlock;
|
|
1574
|
-
|
|
1788
|
+
logger6.info("Subgraph catch-up starting", {
|
|
1575
1789
|
subgraph: subgraphName,
|
|
1576
1790
|
from: startBlock,
|
|
1577
1791
|
to: chainTip,
|
|
@@ -1583,11 +1797,11 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
1583
1797
|
let batchSize = batchConfig.defaultBatchSize;
|
|
1584
1798
|
let currentHeight = startBlock;
|
|
1585
1799
|
let prefetchedBatchEnd = Math.min(currentHeight + batchSize - 1, chainTip);
|
|
1586
|
-
let nextBatchPromise = batchConfig.prefetch ? loadBlockRange(
|
|
1800
|
+
let nextBatchPromise = batchConfig.prefetch ? source.loadBlockRange(currentHeight, prefetchedBatchEnd) : undefined;
|
|
1587
1801
|
while (currentHeight <= chainTip) {
|
|
1588
1802
|
const currentRow = await getSubgraph(targetDb, subgraphName);
|
|
1589
1803
|
if (!currentRow || currentRow.status !== "active") {
|
|
1590
|
-
|
|
1804
|
+
logger6.info("Subgraph status changed, stopping catch-up", {
|
|
1591
1805
|
subgraph: subgraphName,
|
|
1592
1806
|
status: currentRow?.status ?? "deleted"
|
|
1593
1807
|
});
|
|
@@ -1601,13 +1815,13 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
1601
1815
|
const nextStart = batchEnd + 1;
|
|
1602
1816
|
if (nextStart <= chainTip) {
|
|
1603
1817
|
prefetchedBatchEnd = Math.min(nextStart + batchSize - 1, chainTip);
|
|
1604
|
-
nextBatchPromise = loadBlockRange(
|
|
1818
|
+
nextBatchPromise = source.loadBlockRange(nextStart, prefetchedBatchEnd);
|
|
1605
1819
|
} else {
|
|
1606
1820
|
nextBatchPromise = undefined;
|
|
1607
1821
|
}
|
|
1608
1822
|
} else {
|
|
1609
1823
|
batchEnd = Math.min(currentHeight + batchSize - 1, chainTip);
|
|
1610
|
-
batch = await loadBlockRange(
|
|
1824
|
+
batch = await source.loadBlockRange(currentHeight, batchEnd);
|
|
1611
1825
|
}
|
|
1612
1826
|
const batchFailedBlocks = [];
|
|
1613
1827
|
for (let height = currentHeight;height <= batchEnd; height++) {
|
|
@@ -1623,7 +1837,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
1623
1837
|
preloaded: blockData
|
|
1624
1838
|
});
|
|
1625
1839
|
} catch (err) {
|
|
1626
|
-
|
|
1840
|
+
logger6.error("Block processing error during catch-up", {
|
|
1627
1841
|
subgraph: subgraphName,
|
|
1628
1842
|
blockHeight: height,
|
|
1629
1843
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -1642,7 +1856,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
1642
1856
|
}
|
|
1643
1857
|
}
|
|
1644
1858
|
if (processed % LOG_INTERVAL === 0) {
|
|
1645
|
-
|
|
1859
|
+
logger6.info("Subgraph catch-up progress", {
|
|
1646
1860
|
subgraph: subgraphName,
|
|
1647
1861
|
processed,
|
|
1648
1862
|
total: totalBlocks,
|
|
@@ -1654,7 +1868,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
1654
1868
|
if (batchFailedBlocks.length > 0) {
|
|
1655
1869
|
const gaps = coalesceGaps(batchFailedBlocks);
|
|
1656
1870
|
await recordGapBatch(targetDb, subgraphRow.id, subgraphName, gaps).catch((err) => {
|
|
1657
|
-
|
|
1871
|
+
logger6.warn("Failed to record subgraph gaps", {
|
|
1658
1872
|
subgraph: subgraphName,
|
|
1659
1873
|
error: err instanceof Error ? err.message : String(err)
|
|
1660
1874
|
});
|
|
@@ -1665,7 +1879,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
1665
1879
|
currentHeight = batchEnd + 1;
|
|
1666
1880
|
}
|
|
1667
1881
|
await stats.flush(targetDb);
|
|
1668
|
-
|
|
1882
|
+
logger6.info("Subgraph catch-up complete", {
|
|
1669
1883
|
subgraph: subgraphName,
|
|
1670
1884
|
processed
|
|
1671
1885
|
});
|
|
@@ -1679,5 +1893,5 @@ export {
|
|
|
1679
1893
|
catchUpSubgraph
|
|
1680
1894
|
};
|
|
1681
1895
|
|
|
1682
|
-
//# debugId=
|
|
1896
|
+
//# debugId=9D92106B9D08283764756E2164756E21
|
|
1683
1897
|
//# sourceMappingURL=catchup.js.map
|