@secondlayer/subgraphs 4.1.4 → 4.1.5

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.
@@ -942,15 +942,614 @@ 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
1215
  import { logger as logger3 } from "@secondlayer/shared/logger";
948
1216
  import {
949
1217
  clarityValueToJS,
950
- deserializeCV as deserializeCV3,
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 cvToTree(cv) {
1230
+ switch (cv.type) {
1231
+ case "uint":
1232
+ case "int":
1233
+ return { kind: cv.type };
1234
+ case "true":
1235
+ case "false":
1236
+ return { kind: "bool" };
1237
+ case "address":
1238
+ case "contract":
1239
+ return { kind: "principal" };
1240
+ case "buffer":
1241
+ return { kind: "buffer", len: cv.value.length / 2 };
1242
+ case "ascii":
1243
+ return { kind: "ascii", len: cv.value.length };
1244
+ case "utf8":
1245
+ return { kind: "utf8", len: new TextEncoder().encode(cv.value).length };
1246
+ case "none":
1247
+ return { kind: "optional", inner: null };
1248
+ case "some":
1249
+ return { kind: "optional", inner: cvToTree(cv.value) };
1250
+ case "ok":
1251
+ return { kind: "response", ok: cvToTree(cv.value), err: null };
1252
+ case "err":
1253
+ return { kind: "response", ok: null, err: cvToTree(cv.value) };
1254
+ case "list": {
1255
+ let inner = null;
1256
+ for (const el of cv.value) {
1257
+ const t = cvToTree(el);
1258
+ inner = inner ? unify(inner, t) : t;
1259
+ }
1260
+ return { kind: "list", inner };
1261
+ }
1262
+ case "tuple": {
1263
+ const entries = new Map;
1264
+ for (const [k, v] of Object.entries(cv.value)) {
1265
+ entries.set(k, { tree: cvToTree(v), present: 1 });
1266
+ }
1267
+ return { kind: "tuple", count: 1, entries };
1268
+ }
1269
+ }
1270
+ }
1271
+ function unifyNullable(a, b) {
1272
+ if (!a)
1273
+ return b;
1274
+ if (!b)
1275
+ return a;
1276
+ return unify(a, b);
1277
+ }
1278
+ var UNION_KIND_ORDER = {
1279
+ uint: 0,
1280
+ int: 1,
1281
+ bool: 2,
1282
+ principal: 3,
1283
+ buffer: 4,
1284
+ ascii: 5,
1285
+ utf8: 6,
1286
+ list: 7,
1287
+ tuple: 8,
1288
+ response: 9
1289
+ };
1290
+ function makeUnion(members) {
1291
+ const sorted = [...members].sort((a, b) => (UNION_KIND_ORDER[a.kind] ?? 99) - (UNION_KIND_ORDER[b.kind] ?? 99));
1292
+ return { kind: "union", members: sorted };
1293
+ }
1294
+ function unify(a, b) {
1295
+ if (a.kind === "optional" || b.kind === "optional") {
1296
+ const ai = a.kind === "optional" ? a.inner : a;
1297
+ const bi = b.kind === "optional" ? b.inner : b;
1298
+ return { kind: "optional", inner: unifyNullable(ai, bi) };
1299
+ }
1300
+ if (a.kind === "union")
1301
+ return unionAdd(a.members, b);
1302
+ if (b.kind === "union")
1303
+ return unionAdd(b.members, a);
1304
+ if (a.kind !== b.kind)
1305
+ return makeUnion([a, b]);
1306
+ switch (a.kind) {
1307
+ case "uint":
1308
+ case "int":
1309
+ case "bool":
1310
+ case "principal":
1311
+ return a;
1312
+ case "buffer":
1313
+ case "ascii":
1314
+ case "utf8":
1315
+ return { kind: a.kind, len: Math.max(a.len, b.len) };
1316
+ case "list":
1317
+ return {
1318
+ kind: "list",
1319
+ inner: unifyNullable(a.inner, b.inner)
1320
+ };
1321
+ case "response": {
1322
+ const rb = b;
1323
+ return {
1324
+ kind: "response",
1325
+ ok: unifyNullable(a.ok, rb.ok),
1326
+ err: unifyNullable(a.err, rb.err)
1327
+ };
1328
+ }
1329
+ case "tuple": {
1330
+ const tb = b;
1331
+ const entries = new Map([...a.entries].map(([k, e]) => [k, { ...e }]));
1332
+ for (const [k, e] of tb.entries) {
1333
+ const existing = entries.get(k);
1334
+ entries.set(k, existing ? {
1335
+ tree: unify(existing.tree, e.tree),
1336
+ present: existing.present + e.present
1337
+ } : { ...e });
1338
+ }
1339
+ return { kind: "tuple", count: a.count + tb.count, entries };
1340
+ }
1341
+ }
1342
+ }
1343
+ function unionAdd(members, t) {
1344
+ if (t.kind === "union") {
1345
+ let acc = { kind: "union", members };
1346
+ for (const m of t.members)
1347
+ acc = unify(acc, m);
1348
+ return acc;
1349
+ }
1350
+ const next = [...members];
1351
+ for (let i = 0;i < next.length; i++) {
1352
+ const member = next[i];
1353
+ if (!member)
1354
+ continue;
1355
+ const merged = unify(member, t);
1356
+ if (merged.kind !== "union") {
1357
+ next[i] = merged;
1358
+ return makeUnion(next);
1359
+ }
1360
+ }
1361
+ next.push(t);
1362
+ return makeUnion(next);
1363
+ }
1364
+ function wrapOptional(t) {
1365
+ return t.kind === "optional" ? t : { kind: "optional", inner: t };
1366
+ }
1367
+ function renderClarity(t) {
1368
+ if (!t)
1369
+ return "?";
1370
+ switch (t.kind) {
1371
+ case "uint":
1372
+ case "int":
1373
+ case "bool":
1374
+ case "principal":
1375
+ return t.kind;
1376
+ case "buffer":
1377
+ return `(buff ${t.len})`;
1378
+ case "ascii":
1379
+ return `(string-ascii ${t.len})`;
1380
+ case "utf8":
1381
+ return `(string-utf8 ${t.len})`;
1382
+ case "optional":
1383
+ return `(optional ${renderClarity(t.inner)})`;
1384
+ case "list":
1385
+ return `(list ${renderClarity(t.inner)})`;
1386
+ case "response":
1387
+ return `(response ${renderClarity(t.ok)} ${renderClarity(t.err)})`;
1388
+ case "tuple": {
1389
+ const parts = [...t.entries].map(([k, e]) => {
1390
+ const tree = e.present < t.count ? wrapOptional(e.tree) : e.tree;
1391
+ return `(${k} ${renderClarity(tree)})`;
1392
+ });
1393
+ return `(tuple ${parts.join(" ")})`;
1394
+ }
1395
+ case "union":
1396
+ return t.members.map(renderClarity).join(" | ");
1397
+ }
1398
+ }
1399
+ function renderTs(t) {
1400
+ if (!t)
1401
+ return "unknown";
1402
+ switch (t.kind) {
1403
+ case "uint":
1404
+ case "int":
1405
+ return "bigint";
1406
+ case "bool":
1407
+ return "boolean";
1408
+ case "principal":
1409
+ case "buffer":
1410
+ case "ascii":
1411
+ case "utf8":
1412
+ return "string";
1413
+ case "optional":
1414
+ return t.inner ? `${renderTs(t.inner)} | null` : "unknown | null";
1415
+ case "list": {
1416
+ const inner = renderTs(t.inner);
1417
+ return inner.includes(" | ") ? `(${inner})[]` : `${inner}[]`;
1418
+ }
1419
+ case "response": {
1420
+ const sides = [...new Set([renderTs(t.ok), renderTs(t.err)])];
1421
+ return sides.join(" | ");
1422
+ }
1423
+ case "tuple": {
1424
+ const parts = [...t.entries].map(([k, e]) => {
1425
+ const opt = e.present < t.count ? "?" : "";
1426
+ return `${camelizeDataKey(k)}${opt}: ${renderTs(e.tree)}`;
1427
+ });
1428
+ return `{ ${parts.join("; ")} }`;
1429
+ }
1430
+ case "union":
1431
+ return [...new Set(t.members.map((m) => renderTs(m)))].join(" | ");
1432
+ }
1433
+ }
1434
+ function toColumnType(t) {
1435
+ if (!t)
1436
+ return "jsonb";
1437
+ switch (t.kind) {
1438
+ case "uint":
1439
+ return "uint";
1440
+ case "int":
1441
+ return "int";
1442
+ case "bool":
1443
+ return "boolean";
1444
+ case "principal":
1445
+ return "principal";
1446
+ case "buffer":
1447
+ case "ascii":
1448
+ case "utf8":
1449
+ return "text";
1450
+ case "list":
1451
+ case "tuple":
1452
+ return "jsonb";
1453
+ case "optional":
1454
+ return toColumnType(t.inner);
1455
+ case "response":
1456
+ return t.ok ? toColumnType(t.ok) : "jsonb";
1457
+ case "union":
1458
+ return "jsonb";
1459
+ }
1460
+ }
1461
+ var MAX_DECODED_PER_TOPIC_NEWEST = 75;
1462
+ var MAX_DECODED_PER_TOPIC_OLDEST = 25;
1463
+ function inferPrintTopics(samples) {
1464
+ const groups = new Map;
1465
+ for (const s of samples) {
1466
+ const group = groups.get(s.topic);
1467
+ if (group)
1468
+ group.push(s);
1469
+ else
1470
+ groups.set(s.topic, [s]);
1471
+ }
1472
+ const out = [];
1473
+ for (const [topic, rows] of groups) {
1474
+ let first = Number.POSITIVE_INFINITY;
1475
+ let last = Number.NEGATIVE_INFINITY;
1476
+ for (const r of rows) {
1477
+ if (r.blockHeight < first)
1478
+ first = r.blockHeight;
1479
+ if (r.blockHeight > last)
1480
+ last = r.blockHeight;
1481
+ }
1482
+ const withHex = [...rows].filter((r) => r.rawHex !== null).sort((a, b) => b.blockHeight - a.blockHeight);
1483
+ const budget = MAX_DECODED_PER_TOPIC_NEWEST + MAX_DECODED_PER_TOPIC_OLDEST;
1484
+ const picked = withHex.length <= budget ? withHex : [
1485
+ ...withHex.slice(0, MAX_DECODED_PER_TOPIC_NEWEST),
1486
+ ...withHex.slice(-MAX_DECODED_PER_TOPIC_OLDEST)
1487
+ ];
1488
+ const tuples = [];
1489
+ let decoded = 0;
1490
+ for (const p of picked) {
1491
+ try {
1492
+ const cv = deserializeCV3(p.rawHex);
1493
+ decoded++;
1494
+ if (cv.type === "tuple")
1495
+ tuples.push(cv);
1496
+ } catch {}
1497
+ }
1498
+ const nonTuple = decoded > 0 && tuples.length === 0;
1499
+ const fields = [];
1500
+ if (!nonTuple) {
1501
+ const stats = new Map;
1502
+ for (const t of tuples) {
1503
+ for (const [key, value] of Object.entries(t.value)) {
1504
+ if (key === "topic")
1505
+ continue;
1506
+ const tree = cvToTree(value);
1507
+ const existing = stats.get(key);
1508
+ if (existing) {
1509
+ existing.present++;
1510
+ existing.tree = unify(existing.tree, tree);
1511
+ if (value.type === "none")
1512
+ existing.noneCount++;
1513
+ if (value.type === "none" || value.type === "some") {
1514
+ existing.optionalSeen = true;
1515
+ }
1516
+ } else {
1517
+ stats.set(key, {
1518
+ tree,
1519
+ present: 1,
1520
+ noneCount: value.type === "none" ? 1 : 0,
1521
+ optionalSeen: value.type === "none" || value.type === "some"
1522
+ });
1523
+ }
1524
+ }
1525
+ }
1526
+ for (const [name, st] of [...stats].sort(([a], [b]) => a.localeCompare(b))) {
1527
+ const field = {
1528
+ name,
1529
+ camel_name: camelizeDataKey(name),
1530
+ clarity_type: renderClarity(st.tree),
1531
+ ts_type: renderTs(st.tree),
1532
+ column_type: toColumnType(st.tree),
1533
+ always_present: st.present === tuples.length
1534
+ };
1535
+ if (st.optionalSeen) {
1536
+ field.optional_some_rate = (st.present - st.noneCount) / st.present;
1537
+ }
1538
+ fields.push(field);
1539
+ }
1540
+ }
1541
+ out.push({
1542
+ topic,
1543
+ count: rows.length,
1544
+ first_height: first,
1545
+ last_height: last,
1546
+ non_tuple: nonTuple,
1547
+ fields
1548
+ });
1549
+ }
1550
+ return out.sort((a, b) => b.count - a.count);
1551
+ }
1552
+
954
1553
  // src/runtime/print-validate.ts
955
1554
  var COLUMN_TYPES = new Set([
956
1555
  "text",
@@ -1032,9 +1631,6 @@ function validatePrintPayload(prints, topic, data) {
1032
1631
 
1033
1632
  // src/runtime/runner.ts
1034
1633
  var DEFAULT_ERROR_THRESHOLD = 50;
1035
- function camelCase(str) {
1036
- return str.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
1037
- }
1038
1634
  function camelizeKeys(obj) {
1039
1635
  if (obj === null || obj === undefined)
1040
1636
  return obj;
@@ -1044,7 +1640,7 @@ function camelizeKeys(obj) {
1044
1640
  return obj.map(camelizeKeys);
1045
1641
  const result = {};
1046
1642
  for (const [k, v] of Object.entries(obj)) {
1047
- result[camelCase(k)] = camelizeKeys(v);
1643
+ result[camelizeDataKey(k)] = camelizeKeys(v);
1048
1644
  }
1049
1645
  return result;
1050
1646
  }
@@ -1111,7 +1707,7 @@ function buildContractCallInput(filter, tx) {
1111
1707
  return;
1112
1708
  try {
1113
1709
  const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
1114
- input[toCamelCase2(arg.name)] = decodeArg(arg.type, deserializeCV3(clean));
1710
+ input[toCamelCase2(arg.name)] = decodeArg(arg.type, deserializeCV4(clean));
1115
1711
  } catch {}
1116
1712
  });
1117
1713
  return input;
@@ -1234,7 +1830,7 @@ function buildEventPayload(filter, tx, event) {
1234
1830
  const { topic: _, ...rest } = clarityObj ?? {};
1235
1831
  const data = Object.keys(rest).length > 0 ? camelizeKeys(rest) : rawValue && typeof rawValue !== "object" ? rawValue : {};
1236
1832
  return {
1237
- contractId: decoded.contract_identifier ?? tx.contract_id ?? "",
1833
+ contractId: printContractId(decoded) ?? tx.contract_id ?? "",
1238
1834
  topic,
1239
1835
  data: data ?? {},
1240
1836
  tx: txMeta
@@ -1274,380 +1870,118 @@ function buildEventPayload(filter, tx, event) {
1274
1870
  }
1275
1871
  async function runHandlers(subgraph, matched, ctx, opts) {
1276
1872
  let processed = 0;
1277
- let errors = 0;
1278
- let skipped = 0;
1279
- const threshold = opts?.errorThreshold ?? DEFAULT_ERROR_THRESHOLD;
1280
- const filterLookup = new Map;
1281
- if (!Array.isArray(subgraph.sources)) {
1282
- for (const [name, filter] of Object.entries(subgraph.sources)) {
1283
- filterLookup.set(name, filter);
1284
- }
1285
- }
1286
- const units = [];
1287
- for (const { tx, events, sourceName } of matched) {
1288
- if (events.length === 0) {
1289
- units.push({ tx, sourceName, event: null });
1290
- } else {
1291
- for (const event of events)
1292
- units.push({ tx, sourceName, event });
1293
- }
1294
- }
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
- logger3.error("Subgraph error threshold reached, skipping remaining events", {
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;
1873
+ let errors = 0;
1874
+ let skipped = 0;
1875
+ const threshold = opts?.errorThreshold ?? DEFAULT_ERROR_THRESHOLD;
1876
+ const filterLookup = new Map;
1877
+ if (!Array.isArray(subgraph.sources)) {
1878
+ for (const [name, filter] of Object.entries(subgraph.sources)) {
1879
+ filterLookup.set(name, filter);
1481
1880
  }
1482
- case "ft_transfer":
1483
- case "ft_mint":
1484
- case "ft_burn": {
1485
- const eventType = `${filter.type}_event`;
1486
- for (const tx of transactions) {
1487
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
1488
- const matched = txEvents.filter((e) => {
1489
- if (e.type !== eventType)
1490
- return false;
1491
- const data = e.data;
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;
1881
+ }
1882
+ const units = [];
1883
+ for (const { tx, events, sourceName } of matched) {
1884
+ if (events.length === 0) {
1885
+ units.push({ tx, sourceName, event: null });
1886
+ } else {
1887
+ for (const event of events)
1888
+ units.push({ tx, sourceName, event });
1521
1889
  }
1522
- case "nft_transfer":
1523
- case "nft_mint":
1524
- case "nft_burn": {
1525
- const eventType = `${filter.type}_event`;
1526
- for (const tx of transactions) {
1527
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
1528
- const matched = txEvents.filter((e) => {
1529
- if (e.type !== eventType)
1530
- return false;
1531
- const data = e.data;
1532
- if (!data)
1533
- return false;
1534
- if (filter.assetIdentifier) {
1535
- const assetId = data.asset_identifier;
1536
- if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
1537
- return false;
1538
- }
1539
- if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
1540
- return false;
1541
- if ("sender" in filter && filter.sender) {
1542
- if (!matchPattern(data.sender, filter.sender))
1543
- return false;
1890
+ }
1891
+ units.sort((a, b) => (a.tx.tx_index ?? 0) - (b.tx.tx_index ?? 0) || (a.event?.event_index ?? -1) - (b.event?.event_index ?? -1));
1892
+ for (const { tx, event, sourceName } of units) {
1893
+ if (errors >= threshold) {
1894
+ logger3.error("Subgraph error threshold reached, skipping remaining events", {
1895
+ subgraph: subgraph.name,
1896
+ errors,
1897
+ threshold
1898
+ });
1899
+ return { processed, errors, ...skipped > 0 ? { skipped } : {} };
1900
+ }
1901
+ const handler = subgraph.handlers[sourceName] ?? subgraph.handlers["*"] ?? null;
1902
+ if (!handler) {
1903
+ logger3.warn("No handler found for source", {
1904
+ subgraph: subgraph.name,
1905
+ sourceName,
1906
+ txId: tx.tx_id
1907
+ });
1908
+ continue;
1909
+ }
1910
+ ctx.setTx({
1911
+ txId: tx.tx_id,
1912
+ sender: tx.sender,
1913
+ type: tx.type,
1914
+ status: tx.status,
1915
+ contractId: tx.contract_id ?? null,
1916
+ functionName: tx.function_name ?? null
1917
+ });
1918
+ const filter = filterLookup.get(sourceName);
1919
+ const checkpoint = ctx.opsCheckpoint();
1920
+ try {
1921
+ let payload;
1922
+ if (event === null) {
1923
+ payload = filter ? buildEventPayload(filter, tx, null) : {
1924
+ tx: {
1925
+ txId: tx.tx_id,
1926
+ sender: tx.sender,
1927
+ type: tx.type,
1928
+ status: tx.status,
1929
+ contractId: tx.contract_id,
1930
+ functionName: tx.function_name
1544
1931
  }
1545
- if ("recipient" in filter && filter.recipient) {
1546
- if (!matchPattern(data.recipient, filter.recipient))
1547
- return false;
1932
+ };
1933
+ } else if (filter) {
1934
+ payload = buildEventPayload(filter, tx, event);
1935
+ } else {
1936
+ const decoded = decodeEventData(event.data);
1937
+ payload = {
1938
+ ...decoded,
1939
+ _eventId: event.id,
1940
+ _eventType: event.type,
1941
+ _eventIndex: event.event_index,
1942
+ tx: {
1943
+ txId: tx.tx_id,
1944
+ sender: tx.sender,
1945
+ type: tx.type,
1946
+ status: tx.status,
1947
+ contractId: tx.contract_id,
1948
+ functionName: tx.function_name
1548
1949
  }
1549
- return true;
1550
- });
1551
- if (matched.length > 0) {
1552
- results.push({ tx, events: matched });
1553
- }
1950
+ };
1554
1951
  }
1555
- break;
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 });
1952
+ if (event !== null && filter?.type === "print_event" && filter.topic && payload.topic !== filter.topic) {
1953
+ continue;
1579
1954
  }
1580
- break;
1581
- }
1582
- case "contract_deploy": {
1583
- for (const tx of transactions) {
1584
- if (tx.type !== "smart_contract")
1955
+ if (event !== null && filter?.type === "print_event" && filter.prints) {
1956
+ const printPayload = payload;
1957
+ const verdict = validatePrintPayload(filter.prints, printPayload.topic ?? "", printPayload.data ?? {});
1958
+ if (!verdict.ok) {
1959
+ skipped++;
1960
+ logger3.warn("Print payload does not match declared prints schema", {
1961
+ subgraph: subgraph.name,
1962
+ sourceName,
1963
+ topic: printPayload.topic,
1964
+ txId: tx.tx_id,
1965
+ reason: verdict.reason
1966
+ });
1585
1967
  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
- }
1595
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
1596
- results.push({ tx, events: txEvents });
1597
- }
1598
- break;
1599
- }
1600
- case "print_event": {
1601
- for (const tx of transactions) {
1602
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
1603
- const matched = txEvents.filter((e) => {
1604
- if (e.type !== "smart_contract_event" && e.type !== "contract_event")
1605
- return false;
1606
- const data = e.data;
1607
- if (!data)
1608
- return false;
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 });
1624
1968
  }
1625
1969
  }
1626
- break;
1627
- }
1628
- }
1629
- return results;
1630
- }
1631
- function matchSources(sources, transactions, events, traitContracts = new Map, factoryContracts = new Map) {
1632
- const eventsByTx = new Map;
1633
- for (const event of events) {
1634
- const list = eventsByTx.get(event.tx_id) ?? [];
1635
- list.push(event);
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
- }
1970
+ await handler(payload, ctx);
1971
+ processed++;
1972
+ } catch (err) {
1973
+ ctx.rollbackTo(checkpoint);
1974
+ errors++;
1975
+ logger3.error("Subgraph handler error", {
1976
+ subgraph: subgraph.name,
1977
+ sourceName,
1978
+ txId: tx.tx_id,
1979
+ ...event !== null ? { eventId: event.id, eventType: event.type } : {},
1980
+ error: getErrorMessage(err)
1981
+ });
1648
1982
  }
1649
1983
  }
1650
- return results;
1984
+ return { processed, errors, ...skipped > 0 ? { skipped } : {} };
1651
1985
  }
1652
1986
 
1653
1987
  // src/runtime/block-processor.ts
@@ -1990,7 +2324,7 @@ function reconstructEvent(e) {
1990
2324
  type: "contract_event",
1991
2325
  data: {
1992
2326
  topic: e.payload.topic,
1993
- contract_identifier: e.contract_id,
2327
+ contract_id: e.contract_id,
1994
2328
  value: e.payload.value,
1995
2329
  raw_value: e.payload.raw_value
1996
2330
  }
@@ -3652,5 +3986,5 @@ export {
3652
3986
  handleSubgraphReorg
3653
3987
  };
3654
3988
 
3655
- //# debugId=33B13FDE84A36A1564756E2164756E21
3989
+ //# debugId=CCDDEB49D7B7E7B464756E2164756E21
3656
3990
  //# sourceMappingURL=reorg.js.map