@secondlayer/subgraphs 4.4.1 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -21
- package/dist/{src/index.d.ts → index.d.ts} +59 -189
- package/dist/index.js +2151 -0
- package/dist/index.js.map +24 -0
- package/dist/{src/runtime → runtime}/emitter.d.ts +1 -1
- package/dist/{src/runtime → runtime}/emitter.js +8 -12
- package/dist/{src/runtime → runtime}/emitter.js.map +3 -3
- package/dist/{src/runtime → runtime}/replay.d.ts +1 -1
- package/dist/{src/runtime → runtime}/replay.js +342 -683
- package/dist/{src/runtime → runtime}/replay.js.map +5 -5
- package/dist/{src/schema → schema}/index.d.ts +10 -69
- package/dist/{src/schema → schema}/index.js +188 -281
- package/dist/schema/index.js.map +13 -0
- package/dist/{src/testing → testing}/index.d.ts +65 -115
- package/dist/{src/testing → testing}/index.js +554 -968
- package/dist/testing/index.js.map +21 -0
- package/dist/{src/types.d.ts → types.d.ts} +10 -66
- package/dist/{src/validate.d.ts → validate.d.ts} +11 -69
- package/dist/{src/validate.js → validate.js} +18 -98
- package/dist/validate.js.map +10 -0
- package/package.json +18 -26
- package/dist/src/index.js +0 -6081
- package/dist/src/index.js.map +0 -44
- package/dist/src/runtime/block-processor.d.ts +0 -559
- package/dist/src/runtime/block-processor.js +0 -3810
- package/dist/src/runtime/block-processor.js.map +0 -31
- package/dist/src/runtime/catchup.d.ts +0 -473
- package/dist/src/runtime/catchup.js +0 -4228
- package/dist/src/runtime/catchup.js.map +0 -35
- package/dist/src/runtime/clarity.d.ts +0 -15
- package/dist/src/runtime/clarity.js +0 -41
- package/dist/src/runtime/clarity.js.map +0 -10
- package/dist/src/runtime/context.d.ts +0 -325
- package/dist/src/runtime/context.js +0 -936
- package/dist/src/runtime/context.js.map +0 -13
- package/dist/src/runtime/processor.d.ts +0 -11
- package/dist/src/runtime/processor.js +0 -5372
- package/dist/src/runtime/processor.js.map +0 -40
- package/dist/src/runtime/reindex.d.ts +0 -539
- package/dist/src/runtime/reindex.js +0 -4802
- package/dist/src/runtime/reindex.js.map +0 -37
- package/dist/src/runtime/reorg.d.ts +0 -432
- package/dist/src/runtime/reorg.js +0 -4355
- package/dist/src/runtime/reorg.js.map +0 -36
- package/dist/src/runtime/runner.d.ts +0 -719
- package/dist/src/runtime/runner.js +0 -1332
- package/dist/src/runtime/runner.js.map +0 -16
- package/dist/src/runtime/source-matcher.d.ts +0 -304
- package/dist/src/runtime/source-matcher.js +0 -335
- package/dist/src/runtime/source-matcher.js.map +0 -10
- package/dist/src/runtime/stats.d.ts +0 -23
- package/dist/src/runtime/stats.js +0 -71
- package/dist/src/runtime/stats.js.map +0 -10
- package/dist/src/schema/index.js.map +0 -13
- package/dist/src/service.d.ts +0 -0
- package/dist/src/service.js +0 -5398
- package/dist/src/service.js.map +0 -41
- package/dist/src/testing/index.js.map +0 -22
- package/dist/src/triggers/index.d.ts +0 -381
- package/dist/src/triggers/index.js +0 -31
- package/dist/src/triggers/index.js.map +0 -10
- package/dist/src/validate.js.map +0 -10
- /package/dist/{src/types.js → types.js} +0 -0
- /package/dist/{src/types.js.map → types.js.map} +0 -0
|
@@ -1,331 +1,3 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
3
|
-
|
|
4
|
-
// src/runtime/source-matcher.ts
|
|
5
|
-
var patternCache = new Map;
|
|
6
|
-
function matchAny(value, pattern) {
|
|
7
|
-
return Array.isArray(pattern) ? pattern.some((p) => matchPattern(value, p)) : matchPattern(value, pattern);
|
|
8
|
-
}
|
|
9
|
-
function matchPattern(value, pattern) {
|
|
10
|
-
if (!pattern.includes("*"))
|
|
11
|
-
return value === pattern;
|
|
12
|
-
let re = patternCache.get(pattern);
|
|
13
|
-
if (!re) {
|
|
14
|
-
const regex = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
15
|
-
re = new RegExp(`^${regex}$`);
|
|
16
|
-
patternCache.set(pattern, re);
|
|
17
|
-
}
|
|
18
|
-
return re.test(value);
|
|
19
|
-
}
|
|
20
|
-
var EMPTY_SET = new Set;
|
|
21
|
-
function factoryAllows(filter, contractId, factoryContracts) {
|
|
22
|
-
if (!filter.factory)
|
|
23
|
-
return true;
|
|
24
|
-
if (!contractId)
|
|
25
|
-
return false;
|
|
26
|
-
return (factoryContracts.get(filter.factory.from) ?? EMPTY_SET).has(contractId);
|
|
27
|
-
}
|
|
28
|
-
function readPath(payload, path) {
|
|
29
|
-
let cur = payload;
|
|
30
|
-
for (const key of path.split(".")) {
|
|
31
|
-
if (cur === null || typeof cur !== "object")
|
|
32
|
-
return;
|
|
33
|
-
cur = cur[key];
|
|
34
|
-
}
|
|
35
|
-
return cur;
|
|
36
|
-
}
|
|
37
|
-
function traitAllows(filter, contractId, traitContracts) {
|
|
38
|
-
const trait = filter.trait;
|
|
39
|
-
if (!trait)
|
|
40
|
-
return true;
|
|
41
|
-
if (!contractId)
|
|
42
|
-
return false;
|
|
43
|
-
return (traitContracts.get(trait) ?? EMPTY_SET).has(contractId);
|
|
44
|
-
}
|
|
45
|
-
function assetContract(assetId) {
|
|
46
|
-
return assetId?.split("::")[0];
|
|
47
|
-
}
|
|
48
|
-
function printContractId(data) {
|
|
49
|
-
if (!data)
|
|
50
|
-
return;
|
|
51
|
-
const id = (typeof data.contract_identifier === "string" ? data.contract_identifier : undefined) ?? (typeof data.contract_id === "string" ? data.contract_id : undefined);
|
|
52
|
-
return id || undefined;
|
|
53
|
-
}
|
|
54
|
-
function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts = new Map, vmEventsByTx = new Map) {
|
|
55
|
-
const results = [];
|
|
56
|
-
switch (filter.type) {
|
|
57
|
-
case "stx_transfer":
|
|
58
|
-
case "stx_mint":
|
|
59
|
-
case "stx_burn":
|
|
60
|
-
case "stx_lock": {
|
|
61
|
-
const eventType = `${filter.type}_event`;
|
|
62
|
-
for (const tx of transactions) {
|
|
63
|
-
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
64
|
-
const matched = txEvents.filter((e) => e.type === eventType);
|
|
65
|
-
if (matched.length === 0)
|
|
66
|
-
continue;
|
|
67
|
-
const filtered = matched.filter((e) => {
|
|
68
|
-
const data = e.data;
|
|
69
|
-
if (!data)
|
|
70
|
-
return false;
|
|
71
|
-
if ("sender" in filter && filter.sender) {
|
|
72
|
-
if (!matchPattern(data.sender, filter.sender))
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
if ("recipient" in filter && filter.recipient) {
|
|
76
|
-
if (!matchPattern(data.recipient, filter.recipient))
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
if ("lockedAddress" in filter && filter.lockedAddress) {
|
|
80
|
-
if (!matchPattern(data.locked_address, filter.lockedAddress))
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
if ("minAmount" in filter && filter.minAmount !== undefined) {
|
|
84
|
-
const amount = BigInt(data.amount ?? data.locked_amount ?? "0");
|
|
85
|
-
if (amount < filter.minAmount)
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
if ("maxAmount" in filter && filter.maxAmount !== undefined) {
|
|
89
|
-
const amount = BigInt(data.amount ?? "0");
|
|
90
|
-
if (amount > filter.maxAmount)
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
return true;
|
|
94
|
-
});
|
|
95
|
-
if (filtered.length > 0) {
|
|
96
|
-
results.push({ tx, events: filtered });
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
break;
|
|
100
|
-
}
|
|
101
|
-
case "ft_transfer":
|
|
102
|
-
case "ft_mint":
|
|
103
|
-
case "ft_burn": {
|
|
104
|
-
const eventType = `${filter.type}_event`;
|
|
105
|
-
for (const tx of transactions) {
|
|
106
|
-
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
107
|
-
const matched = txEvents.filter((e) => {
|
|
108
|
-
if (e.type !== eventType)
|
|
109
|
-
return false;
|
|
110
|
-
const data = e.data;
|
|
111
|
-
if (!data)
|
|
112
|
-
return false;
|
|
113
|
-
if (filter.assetIdentifier) {
|
|
114
|
-
const assetId = data.asset_identifier;
|
|
115
|
-
if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
|
|
116
|
-
return false;
|
|
117
|
-
}
|
|
118
|
-
if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
|
|
119
|
-
return false;
|
|
120
|
-
if ("sender" in filter && filter.sender) {
|
|
121
|
-
if (!matchPattern(data.sender, filter.sender))
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
124
|
-
if ("recipient" in filter && filter.recipient) {
|
|
125
|
-
if (!matchPattern(data.recipient, filter.recipient))
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
if (filter.minAmount !== undefined) {
|
|
129
|
-
const amount = BigInt(data.amount ?? "0");
|
|
130
|
-
if (amount < filter.minAmount)
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
return true;
|
|
134
|
-
});
|
|
135
|
-
if (matched.length > 0) {
|
|
136
|
-
results.push({ tx, events: matched });
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
break;
|
|
140
|
-
}
|
|
141
|
-
case "nft_transfer":
|
|
142
|
-
case "nft_mint":
|
|
143
|
-
case "nft_burn": {
|
|
144
|
-
const eventType = `${filter.type}_event`;
|
|
145
|
-
for (const tx of transactions) {
|
|
146
|
-
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
147
|
-
const matched = txEvents.filter((e) => {
|
|
148
|
-
if (e.type !== eventType)
|
|
149
|
-
return false;
|
|
150
|
-
const data = e.data;
|
|
151
|
-
if (!data)
|
|
152
|
-
return false;
|
|
153
|
-
if (filter.assetIdentifier) {
|
|
154
|
-
const assetId = data.asset_identifier;
|
|
155
|
-
if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
|
|
156
|
-
return false;
|
|
157
|
-
}
|
|
158
|
-
if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
|
|
159
|
-
return false;
|
|
160
|
-
if ("sender" in filter && filter.sender) {
|
|
161
|
-
if (!matchPattern(data.sender, filter.sender))
|
|
162
|
-
return false;
|
|
163
|
-
}
|
|
164
|
-
if ("recipient" in filter && filter.recipient) {
|
|
165
|
-
if (!matchPattern(data.recipient, filter.recipient))
|
|
166
|
-
return false;
|
|
167
|
-
}
|
|
168
|
-
return true;
|
|
169
|
-
});
|
|
170
|
-
if (matched.length > 0) {
|
|
171
|
-
results.push({ tx, events: matched });
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
break;
|
|
175
|
-
}
|
|
176
|
-
case "contract_call": {
|
|
177
|
-
for (const tx of transactions) {
|
|
178
|
-
if (tx.type !== "contract_call")
|
|
179
|
-
continue;
|
|
180
|
-
if (filter.contractId) {
|
|
181
|
-
if (!tx.contract_id || !matchAny(tx.contract_id, filter.contractId))
|
|
182
|
-
continue;
|
|
183
|
-
}
|
|
184
|
-
if (!factoryAllows(filter, tx.contract_id, factoryContracts))
|
|
185
|
-
continue;
|
|
186
|
-
if (filter.functionName) {
|
|
187
|
-
if (!tx.function_name || !matchPattern(tx.function_name, filter.functionName))
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
if (filter.caller) {
|
|
191
|
-
if (!matchPattern(tx.sender, filter.caller))
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
if (!traitAllows(filter, tx.contract_id, traitContracts))
|
|
195
|
-
continue;
|
|
196
|
-
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
197
|
-
results.push({ tx, events: txEvents });
|
|
198
|
-
}
|
|
199
|
-
break;
|
|
200
|
-
}
|
|
201
|
-
case "contract_deploy": {
|
|
202
|
-
for (const tx of transactions) {
|
|
203
|
-
if (tx.type !== "smart_contract")
|
|
204
|
-
continue;
|
|
205
|
-
if (filter.deployer) {
|
|
206
|
-
if (!matchPattern(tx.sender, filter.deployer))
|
|
207
|
-
continue;
|
|
208
|
-
}
|
|
209
|
-
if (filter.contractName) {
|
|
210
|
-
const name = tx.contract_id?.split(".")[1] ?? "";
|
|
211
|
-
if (!matchPattern(name, filter.contractName))
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
214
|
-
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
215
|
-
results.push({ tx, events: txEvents });
|
|
216
|
-
}
|
|
217
|
-
break;
|
|
218
|
-
}
|
|
219
|
-
case "nested_contract_call":
|
|
220
|
-
case "var_set":
|
|
221
|
-
case "map_set":
|
|
222
|
-
case "map_insert":
|
|
223
|
-
case "map_delete": {
|
|
224
|
-
for (const tx of transactions) {
|
|
225
|
-
const txEvents = vmEventsByTx.get(tx.tx_id) ?? [];
|
|
226
|
-
const matched = txEvents.filter((e) => {
|
|
227
|
-
if (e.type !== filter.type)
|
|
228
|
-
return false;
|
|
229
|
-
const data = e.data;
|
|
230
|
-
if (!data)
|
|
231
|
-
return false;
|
|
232
|
-
const contractId = printContractId(data);
|
|
233
|
-
if ("contractId" in filter && filter.contractId) {
|
|
234
|
-
if (!contractId || !matchAny(contractId, filter.contractId))
|
|
235
|
-
return false;
|
|
236
|
-
}
|
|
237
|
-
if (!traitAllows(filter, contractId, traitContracts))
|
|
238
|
-
return false;
|
|
239
|
-
if (!factoryAllows(filter, contractId, factoryContracts))
|
|
240
|
-
return false;
|
|
241
|
-
if (filter.type === "nested_contract_call") {
|
|
242
|
-
if (filter.functionName) {
|
|
243
|
-
if (typeof data.function_name !== "string" || !matchPattern(data.function_name, filter.functionName))
|
|
244
|
-
return false;
|
|
245
|
-
}
|
|
246
|
-
if (filter.caller) {
|
|
247
|
-
if (typeof data.caller !== "string" || !matchPattern(data.caller, filter.caller))
|
|
248
|
-
return false;
|
|
249
|
-
}
|
|
250
|
-
if (filter.sender) {
|
|
251
|
-
if (typeof data.sender !== "string" || !matchPattern(data.sender, filter.sender))
|
|
252
|
-
return false;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
if (filter.type === "var_set" && filter.varName) {
|
|
256
|
-
if (typeof data.var_name !== "string" || !matchPattern(data.var_name, filter.varName))
|
|
257
|
-
return false;
|
|
258
|
-
}
|
|
259
|
-
if ((filter.type === "map_set" || filter.type === "map_insert" || filter.type === "map_delete") && filter.map) {
|
|
260
|
-
if (typeof data.map_name !== "string" || !matchPattern(data.map_name, filter.map))
|
|
261
|
-
return false;
|
|
262
|
-
}
|
|
263
|
-
return true;
|
|
264
|
-
});
|
|
265
|
-
if (matched.length > 0)
|
|
266
|
-
results.push({ tx, events: matched });
|
|
267
|
-
}
|
|
268
|
-
break;
|
|
269
|
-
}
|
|
270
|
-
case "print_event": {
|
|
271
|
-
for (const tx of transactions) {
|
|
272
|
-
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
273
|
-
const matched = txEvents.filter((e) => {
|
|
274
|
-
if (e.type !== "smart_contract_event" && e.type !== "contract_event")
|
|
275
|
-
return false;
|
|
276
|
-
const data = e.data;
|
|
277
|
-
if (!data)
|
|
278
|
-
return false;
|
|
279
|
-
if (data.topic !== "print")
|
|
280
|
-
return false;
|
|
281
|
-
const contractId = printContractId(data);
|
|
282
|
-
if (filter.contractId) {
|
|
283
|
-
if (!contractId || !matchAny(contractId, filter.contractId))
|
|
284
|
-
return false;
|
|
285
|
-
}
|
|
286
|
-
if (!traitAllows(filter, contractId, traitContracts))
|
|
287
|
-
return false;
|
|
288
|
-
if (!factoryAllows(filter, contractId, factoryContracts))
|
|
289
|
-
return false;
|
|
290
|
-
return true;
|
|
291
|
-
});
|
|
292
|
-
if (matched.length > 0) {
|
|
293
|
-
results.push({ tx, events: matched });
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
break;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
return results;
|
|
300
|
-
}
|
|
301
|
-
function matchSources(sources, transactions, events, traitContracts = new Map, factoryContracts = new Map, vmEvents = []) {
|
|
302
|
-
const eventsByTx = new Map;
|
|
303
|
-
for (const event of events) {
|
|
304
|
-
const list = eventsByTx.get(event.tx_id) ?? [];
|
|
305
|
-
list.push(event);
|
|
306
|
-
eventsByTx.set(event.tx_id, list);
|
|
307
|
-
}
|
|
308
|
-
const vmEventsByTx = new Map;
|
|
309
|
-
for (const event of vmEvents) {
|
|
310
|
-
const list = vmEventsByTx.get(event.tx_id) ?? [];
|
|
311
|
-
list.push(event);
|
|
312
|
-
vmEventsByTx.set(event.tx_id, list);
|
|
313
|
-
}
|
|
314
|
-
const seen = new Set;
|
|
315
|
-
const results = [];
|
|
316
|
-
for (const [sourceName, filter] of Object.entries(sources)) {
|
|
317
|
-
const matches = matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts, vmEventsByTx);
|
|
318
|
-
for (const match of matches) {
|
|
319
|
-
const dedupeKey = `${match.tx.tx_id}:${sourceName}`;
|
|
320
|
-
if (!seen.has(dedupeKey)) {
|
|
321
|
-
seen.add(dedupeKey);
|
|
322
|
-
results.push({ ...match, sourceName });
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
return results;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
1
|
// src/runtime/replay.ts
|
|
330
2
|
import { createHash } from "node:crypto";
|
|
331
3
|
import { getTargetDb } from "@secondlayer/shared/db";
|
|
@@ -408,201 +80,11 @@ async function loadBlockRange(db, fromHeight, toHeight) {
|
|
|
408
80
|
}
|
|
409
81
|
return result;
|
|
410
82
|
}
|
|
411
|
-
function avgEventsPerBlock(batch) {
|
|
412
|
-
if (batch.size === 0)
|
|
413
|
-
return 0;
|
|
414
|
-
let totalEvents = 0;
|
|
415
|
-
for (const data of batch.values()) {
|
|
416
|
-
totalEvents += data.events.length;
|
|
417
|
-
}
|
|
418
|
-
return totalEvents / batch.size;
|
|
419
|
-
}
|
|
420
83
|
|
|
421
84
|
// src/runtime/observer-http-source.ts
|
|
422
85
|
import {
|
|
423
86
|
VM_NODE_TO_STORED_TYPE
|
|
424
87
|
} from "@secondlayer/shared";
|
|
425
|
-
var OBSERVER_HTTP_EXPORT_PATH = "/internal/observer-events";
|
|
426
|
-
function stripTrailingSlash(url) {
|
|
427
|
-
return url.replace(/\/+$/, "");
|
|
428
|
-
}
|
|
429
|
-
function blockTimestamp(payload) {
|
|
430
|
-
for (const value of [
|
|
431
|
-
payload.timestamp,
|
|
432
|
-
payload.block_time,
|
|
433
|
-
payload.burn_block_time,
|
|
434
|
-
payload.burn_block_timestamp
|
|
435
|
-
]) {
|
|
436
|
-
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
437
|
-
return value;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
return 0;
|
|
441
|
-
}
|
|
442
|
-
function eventData(event) {
|
|
443
|
-
if (typeof event.type === "string" && event.type in event) {
|
|
444
|
-
return event[event.type] ?? {};
|
|
445
|
-
}
|
|
446
|
-
return {};
|
|
447
|
-
}
|
|
448
|
-
function mapNewBlockPayloadToBlockData(payload) {
|
|
449
|
-
const p = payload;
|
|
450
|
-
if (typeof p.block_height !== "number" || !Number.isFinite(p.block_height) || typeof p.index_block_hash !== "string" || p.index_block_hash.length === 0) {
|
|
451
|
-
throw new Error("observer /new_block payload missing block_height or index_block_hash");
|
|
452
|
-
}
|
|
453
|
-
const height = p.block_height;
|
|
454
|
-
const block = {
|
|
455
|
-
height,
|
|
456
|
-
hash: p.block_hash ?? "",
|
|
457
|
-
parent_hash: p.parent_block_hash ?? "",
|
|
458
|
-
burn_block_height: p.burn_block_height ?? 0,
|
|
459
|
-
burn_block_hash: p.burn_block_hash ?? null,
|
|
460
|
-
index_block_hash: p.index_block_hash,
|
|
461
|
-
timestamp: blockTimestamp(p),
|
|
462
|
-
canonical: true,
|
|
463
|
-
created_at: new Date(0)
|
|
464
|
-
};
|
|
465
|
-
const txs = (p.transactions ?? []).map((tx) => {
|
|
466
|
-
let contractId = null;
|
|
467
|
-
let functionName = null;
|
|
468
|
-
if (tx.tx_type === "contract_call" && tx.contract_call) {
|
|
469
|
-
contractId = tx.contract_call.contract_id ?? null;
|
|
470
|
-
functionName = tx.contract_call.function_name ?? null;
|
|
471
|
-
} else if (tx.tx_type === "smart_contract" && tx.smart_contract) {
|
|
472
|
-
contractId = tx.smart_contract.contract_id ?? null;
|
|
473
|
-
}
|
|
474
|
-
return {
|
|
475
|
-
tx_id: tx.txid ?? "",
|
|
476
|
-
block_height: height,
|
|
477
|
-
tx_index: tx.tx_index ?? 0,
|
|
478
|
-
type: tx.tx_type ?? "",
|
|
479
|
-
sender: tx.sender_address ?? "",
|
|
480
|
-
status: tx.status ?? "success",
|
|
481
|
-
contract_id: contractId,
|
|
482
|
-
function_name: functionName,
|
|
483
|
-
function_args: [],
|
|
484
|
-
raw_result: tx.raw_result ?? null,
|
|
485
|
-
raw_tx: tx.raw_tx ?? "",
|
|
486
|
-
created_at: new Date(0)
|
|
487
|
-
};
|
|
488
|
-
});
|
|
489
|
-
const events = (p.events ?? []).map((event) => {
|
|
490
|
-
const txId = event.txid ?? "";
|
|
491
|
-
const eventIndex = event.event_index ?? 0;
|
|
492
|
-
return {
|
|
493
|
-
id: `${txId}#${eventIndex}`,
|
|
494
|
-
tx_id: txId,
|
|
495
|
-
block_height: height,
|
|
496
|
-
event_index: eventIndex,
|
|
497
|
-
type: event.type ?? "",
|
|
498
|
-
data: eventData(event),
|
|
499
|
-
created_at: new Date(0)
|
|
500
|
-
};
|
|
501
|
-
});
|
|
502
|
-
const vmEvents = [];
|
|
503
|
-
for (const [i, trace] of (p.vm_events ?? []).entries()) {
|
|
504
|
-
const nodeType = trace.type;
|
|
505
|
-
if (typeof nodeType !== "string" || !(nodeType in VM_NODE_TO_STORED_TYPE))
|
|
506
|
-
continue;
|
|
507
|
-
const stored = VM_NODE_TO_STORED_TYPE[nodeType];
|
|
508
|
-
const body = trace[nodeType];
|
|
509
|
-
if (!body || typeof body !== "object")
|
|
510
|
-
continue;
|
|
511
|
-
const txId = trace.txid ?? "";
|
|
512
|
-
const vmIndex = i;
|
|
513
|
-
vmEvents.push({
|
|
514
|
-
id: vmEventId(txId, vmIndex),
|
|
515
|
-
tx_id: txId,
|
|
516
|
-
block_height: height,
|
|
517
|
-
event_index: vmIndex,
|
|
518
|
-
type: stored,
|
|
519
|
-
data: body,
|
|
520
|
-
created_at: new Date(0),
|
|
521
|
-
clock: "vm"
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
return { block, txs, events, vmEvents };
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
class ObserverHttpBlockSource {
|
|
528
|
-
baseUrl;
|
|
529
|
-
token;
|
|
530
|
-
fetchFn;
|
|
531
|
-
constructor(opts) {
|
|
532
|
-
this.baseUrl = stripTrailingSlash(opts.baseUrl);
|
|
533
|
-
this.token = opts.token ?? null;
|
|
534
|
-
this.fetchFn = opts.fetch ?? globalThis.fetch.bind(globalThis);
|
|
535
|
-
}
|
|
536
|
-
headers() {
|
|
537
|
-
if (!this.token)
|
|
538
|
-
return {};
|
|
539
|
-
return { Authorization: `Bearer ${this.token}` };
|
|
540
|
-
}
|
|
541
|
-
async getTip() {
|
|
542
|
-
const res = await this.fetchFn(`${this.baseUrl}${OBSERVER_HTTP_EXPORT_PATH}/tip`, { headers: this.headers() });
|
|
543
|
-
if (!res.ok) {
|
|
544
|
-
throw new Error(`observer tip HTTP ${res.status}: ${await res.text().catch(() => "")}`);
|
|
545
|
-
}
|
|
546
|
-
let body;
|
|
547
|
-
try {
|
|
548
|
-
body = await res.json();
|
|
549
|
-
} catch (err) {
|
|
550
|
-
throw new Error(`observer tip invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
551
|
-
}
|
|
552
|
-
if (typeof body.block_height !== "number" || !Number.isFinite(body.block_height)) {
|
|
553
|
-
throw new Error("observer tip missing block_height");
|
|
554
|
-
}
|
|
555
|
-
return body.block_height;
|
|
556
|
-
}
|
|
557
|
-
async loadBlockRange(fromHeight, toHeight) {
|
|
558
|
-
const result = new Map;
|
|
559
|
-
let afterHeight = fromHeight > 0 ? fromHeight - 1 : undefined;
|
|
560
|
-
let afterIndexBlockHash;
|
|
561
|
-
for (;; ) {
|
|
562
|
-
const url = new URL(`${this.baseUrl}${OBSERVER_HTTP_EXPORT_PATH}`);
|
|
563
|
-
url.searchParams.set("path", "/new_block");
|
|
564
|
-
url.searchParams.set("limit", "100");
|
|
565
|
-
if (afterHeight != null) {
|
|
566
|
-
url.searchParams.set("after_height", String(afterHeight));
|
|
567
|
-
}
|
|
568
|
-
if (afterIndexBlockHash) {
|
|
569
|
-
url.searchParams.set("after_index_block_hash", afterIndexBlockHash);
|
|
570
|
-
}
|
|
571
|
-
const res = await this.fetchFn(url.toString(), {
|
|
572
|
-
headers: this.headers()
|
|
573
|
-
});
|
|
574
|
-
if (!res.ok) {
|
|
575
|
-
throw new Error(`observer events HTTP ${res.status}: ${await res.text().catch(() => "")}`);
|
|
576
|
-
}
|
|
577
|
-
let body;
|
|
578
|
-
try {
|
|
579
|
-
body = await res.json();
|
|
580
|
-
} catch (err) {
|
|
581
|
-
throw new Error(`observer events invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
582
|
-
}
|
|
583
|
-
let pastTo = false;
|
|
584
|
-
for (const message of body.events ?? []) {
|
|
585
|
-
if (message.path === "/new_burn_block")
|
|
586
|
-
continue;
|
|
587
|
-
const height = message.payload?.block_height;
|
|
588
|
-
if (typeof height === "number" && height > toHeight) {
|
|
589
|
-
pastTo = true;
|
|
590
|
-
break;
|
|
591
|
-
}
|
|
592
|
-
const data = mapNewBlockPayloadToBlockData(message.payload);
|
|
593
|
-
const h = data.block.height;
|
|
594
|
-
if (h >= fromHeight && h <= toHeight) {
|
|
595
|
-
result.set(h, data);
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
if (pastTo || body.next == null)
|
|
599
|
-
break;
|
|
600
|
-
afterHeight = body.next.after_height;
|
|
601
|
-
afterIndexBlockHash = body.next.after_index_block_hash;
|
|
602
|
-
}
|
|
603
|
-
return result;
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
88
|
|
|
607
89
|
// src/runtime/reconstruct.ts
|
|
608
90
|
function isoToUnixSeconds(iso) {
|
|
@@ -816,9 +298,6 @@ function sourceFilters(subgraph) {
|
|
|
816
298
|
const sources = subgraph.sources;
|
|
817
299
|
return Array.isArray(sources) ? sources : Object.values(sources);
|
|
818
300
|
}
|
|
819
|
-
function needsTransactionData(subgraph) {
|
|
820
|
-
return sourceFilters(subgraph).some((f) => TX_SOURCE_TYPES.has(f.type));
|
|
821
|
-
}
|
|
822
301
|
function synthesizeTxsFromEvents(events) {
|
|
823
302
|
const byId = new Map;
|
|
824
303
|
for (const e of events) {
|
|
@@ -840,25 +319,6 @@ function indexEventTypesForFilterTypes(filterTypes) {
|
|
|
840
319
|
}
|
|
841
320
|
return [...types];
|
|
842
321
|
}
|
|
843
|
-
function referencedIndexEventTypes(subgraph) {
|
|
844
|
-
if (!subgraph.sources)
|
|
845
|
-
return [];
|
|
846
|
-
return indexEventTypesForFilterTypes(sourceFilters(subgraph).map((f) => f.type));
|
|
847
|
-
}
|
|
848
|
-
function isStreamsIndexEligible(subgraph) {
|
|
849
|
-
if (Array.isArray(subgraph.sources))
|
|
850
|
-
return false;
|
|
851
|
-
const filters = sourceFilters(subgraph);
|
|
852
|
-
if (filters.length === 0)
|
|
853
|
-
return false;
|
|
854
|
-
for (const f of filters) {
|
|
855
|
-
const known = EVENT_FILTER_TO_INDEX_TYPE[f.type] || TX_SOURCE_TYPES.has(f.type);
|
|
856
|
-
if (!known)
|
|
857
|
-
return false;
|
|
858
|
-
}
|
|
859
|
-
return true;
|
|
860
|
-
}
|
|
861
|
-
|
|
862
322
|
class PublicApiBlockSource {
|
|
863
323
|
http;
|
|
864
324
|
eventTypes;
|
|
@@ -898,159 +358,358 @@ class PublicApiBlockSource {
|
|
|
898
358
|
events: []
|
|
899
359
|
});
|
|
900
360
|
}
|
|
901
|
-
const txs = this.needsTransactions ? txRows.map(reconstructTransaction) : synthesizeTxsFromEvents(eventLists.flat());
|
|
902
|
-
for (const t of txs) {
|
|
903
|
-
map.get(t.block_height)?.txs.push(t);
|
|
361
|
+
const txs = this.needsTransactions ? txRows.map(reconstructTransaction) : synthesizeTxsFromEvents(eventLists.flat());
|
|
362
|
+
for (const t of txs) {
|
|
363
|
+
map.get(t.block_height)?.txs.push(t);
|
|
364
|
+
}
|
|
365
|
+
for (const list of eventLists) {
|
|
366
|
+
for (const e of list) {
|
|
367
|
+
const bd = map.get(e.block_height);
|
|
368
|
+
if (!bd)
|
|
369
|
+
continue;
|
|
370
|
+
if (VM_INDEX_EVENT_TYPES.has(e.event_type)) {
|
|
371
|
+
bd.vmEvents ??= [];
|
|
372
|
+
bd.vmEvents.push(reconstructEvent(e));
|
|
373
|
+
} else {
|
|
374
|
+
bd.events.push(reconstructEvent(e));
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
for (const bd of map.values()) {
|
|
379
|
+
bd.txs.sort((a, b) => a.tx_index - b.tx_index);
|
|
380
|
+
bd.events.sort((a, b) => a.event_index - b.event_index);
|
|
381
|
+
bd.vmEvents?.sort((a, b) => a.event_index - b.event_index);
|
|
382
|
+
}
|
|
383
|
+
return map;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
var postgresBlockSource = new PostgresBlockSource;
|
|
387
|
+
function buildHttpClient() {
|
|
388
|
+
return createInternalIndexHttpClient();
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// src/runtime/trigger-evaluator.ts
|
|
392
|
+
import { getSourceDb as getSourceDb3 } from "@secondlayer/shared/db";
|
|
393
|
+
import { resolveTraitContractIds } from "@secondlayer/shared/db/queries/contracts";
|
|
394
|
+
|
|
395
|
+
// src/runtime/decoder-bound.ts
|
|
396
|
+
import { decodeStreamsCursor, isEmptyRangeCursor } from "@secondlayer/shared";
|
|
397
|
+
import { getSourceDb as getSourceDb2 } from "@secondlayer/shared/db";
|
|
398
|
+
|
|
399
|
+
// src/runtime/source-matcher.ts
|
|
400
|
+
var patternCache = new Map;
|
|
401
|
+
function matchAny(value, pattern) {
|
|
402
|
+
return Array.isArray(pattern) ? pattern.some((p) => matchPattern(value, p)) : matchPattern(value, pattern);
|
|
403
|
+
}
|
|
404
|
+
function matchPattern(value, pattern) {
|
|
405
|
+
if (!pattern.includes("*"))
|
|
406
|
+
return value === pattern;
|
|
407
|
+
let re = patternCache.get(pattern);
|
|
408
|
+
if (!re) {
|
|
409
|
+
const regex = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
410
|
+
re = new RegExp(`^${regex}$`);
|
|
411
|
+
patternCache.set(pattern, re);
|
|
412
|
+
}
|
|
413
|
+
return re.test(value);
|
|
414
|
+
}
|
|
415
|
+
var EMPTY_SET = new Set;
|
|
416
|
+
function factoryAllows(filter, contractId, factoryContracts) {
|
|
417
|
+
if (!filter.factory)
|
|
418
|
+
return true;
|
|
419
|
+
if (!contractId)
|
|
420
|
+
return false;
|
|
421
|
+
return (factoryContracts.get(filter.factory.from) ?? EMPTY_SET).has(contractId);
|
|
422
|
+
}
|
|
423
|
+
function traitAllows(filter, contractId, traitContracts) {
|
|
424
|
+
const trait = filter.trait;
|
|
425
|
+
if (!trait)
|
|
426
|
+
return true;
|
|
427
|
+
if (!contractId)
|
|
428
|
+
return false;
|
|
429
|
+
return (traitContracts.get(trait) ?? EMPTY_SET).has(contractId);
|
|
430
|
+
}
|
|
431
|
+
function assetContract(assetId) {
|
|
432
|
+
return assetId?.split("::")[0];
|
|
433
|
+
}
|
|
434
|
+
function printContractId(data) {
|
|
435
|
+
if (!data)
|
|
436
|
+
return;
|
|
437
|
+
const id = (typeof data.contract_identifier === "string" ? data.contract_identifier : undefined) ?? (typeof data.contract_id === "string" ? data.contract_id : undefined);
|
|
438
|
+
return id || undefined;
|
|
439
|
+
}
|
|
440
|
+
function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts = new Map, vmEventsByTx = new Map) {
|
|
441
|
+
const results = [];
|
|
442
|
+
switch (filter.type) {
|
|
443
|
+
case "stx_transfer":
|
|
444
|
+
case "stx_mint":
|
|
445
|
+
case "stx_burn":
|
|
446
|
+
case "stx_lock": {
|
|
447
|
+
const eventType = `${filter.type}_event`;
|
|
448
|
+
for (const tx of transactions) {
|
|
449
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
450
|
+
const matched = txEvents.filter((e) => e.type === eventType);
|
|
451
|
+
if (matched.length === 0)
|
|
452
|
+
continue;
|
|
453
|
+
const filtered = matched.filter((e) => {
|
|
454
|
+
const data = e.data;
|
|
455
|
+
if (!data)
|
|
456
|
+
return false;
|
|
457
|
+
if ("sender" in filter && filter.sender) {
|
|
458
|
+
if (!matchPattern(data.sender, filter.sender))
|
|
459
|
+
return false;
|
|
460
|
+
}
|
|
461
|
+
if ("recipient" in filter && filter.recipient) {
|
|
462
|
+
if (!matchPattern(data.recipient, filter.recipient))
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
if ("lockedAddress" in filter && filter.lockedAddress) {
|
|
466
|
+
if (!matchPattern(data.locked_address, filter.lockedAddress))
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
if ("minAmount" in filter && filter.minAmount !== undefined) {
|
|
470
|
+
const amount = BigInt(data.amount ?? data.locked_amount ?? "0");
|
|
471
|
+
if (amount < filter.minAmount)
|
|
472
|
+
return false;
|
|
473
|
+
}
|
|
474
|
+
if ("maxAmount" in filter && filter.maxAmount !== undefined) {
|
|
475
|
+
const amount = BigInt(data.amount ?? "0");
|
|
476
|
+
if (amount > filter.maxAmount)
|
|
477
|
+
return false;
|
|
478
|
+
}
|
|
479
|
+
return true;
|
|
480
|
+
});
|
|
481
|
+
if (filtered.length > 0) {
|
|
482
|
+
results.push({ tx, events: filtered });
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
break;
|
|
486
|
+
}
|
|
487
|
+
case "ft_transfer":
|
|
488
|
+
case "ft_mint":
|
|
489
|
+
case "ft_burn": {
|
|
490
|
+
const eventType = `${filter.type}_event`;
|
|
491
|
+
for (const tx of transactions) {
|
|
492
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
493
|
+
const matched = txEvents.filter((e) => {
|
|
494
|
+
if (e.type !== eventType)
|
|
495
|
+
return false;
|
|
496
|
+
const data = e.data;
|
|
497
|
+
if (!data)
|
|
498
|
+
return false;
|
|
499
|
+
if (filter.assetIdentifier) {
|
|
500
|
+
const assetId = data.asset_identifier;
|
|
501
|
+
if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
|
|
502
|
+
return false;
|
|
503
|
+
}
|
|
504
|
+
if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
|
|
505
|
+
return false;
|
|
506
|
+
if ("sender" in filter && filter.sender) {
|
|
507
|
+
if (!matchPattern(data.sender, filter.sender))
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
if ("recipient" in filter && filter.recipient) {
|
|
511
|
+
if (!matchPattern(data.recipient, filter.recipient))
|
|
512
|
+
return false;
|
|
513
|
+
}
|
|
514
|
+
if (filter.minAmount !== undefined) {
|
|
515
|
+
const amount = BigInt(data.amount ?? "0");
|
|
516
|
+
if (amount < filter.minAmount)
|
|
517
|
+
return false;
|
|
518
|
+
}
|
|
519
|
+
return true;
|
|
520
|
+
});
|
|
521
|
+
if (matched.length > 0) {
|
|
522
|
+
results.push({ tx, events: matched });
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
case "nft_transfer":
|
|
528
|
+
case "nft_mint":
|
|
529
|
+
case "nft_burn": {
|
|
530
|
+
const eventType = `${filter.type}_event`;
|
|
531
|
+
for (const tx of transactions) {
|
|
532
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
533
|
+
const matched = txEvents.filter((e) => {
|
|
534
|
+
if (e.type !== eventType)
|
|
535
|
+
return false;
|
|
536
|
+
const data = e.data;
|
|
537
|
+
if (!data)
|
|
538
|
+
return false;
|
|
539
|
+
if (filter.assetIdentifier) {
|
|
540
|
+
const assetId = data.asset_identifier;
|
|
541
|
+
if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
|
|
542
|
+
return false;
|
|
543
|
+
}
|
|
544
|
+
if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
|
|
545
|
+
return false;
|
|
546
|
+
if ("sender" in filter && filter.sender) {
|
|
547
|
+
if (!matchPattern(data.sender, filter.sender))
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
if ("recipient" in filter && filter.recipient) {
|
|
551
|
+
if (!matchPattern(data.recipient, filter.recipient))
|
|
552
|
+
return false;
|
|
553
|
+
}
|
|
554
|
+
return true;
|
|
555
|
+
});
|
|
556
|
+
if (matched.length > 0) {
|
|
557
|
+
results.push({ tx, events: matched });
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
break;
|
|
904
561
|
}
|
|
905
|
-
|
|
906
|
-
for (const
|
|
907
|
-
|
|
908
|
-
if (!bd)
|
|
562
|
+
case "contract_call": {
|
|
563
|
+
for (const tx of transactions) {
|
|
564
|
+
if (tx.type !== "contract_call")
|
|
909
565
|
continue;
|
|
910
|
-
if (
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
}
|
|
914
|
-
|
|
566
|
+
if (filter.contractId) {
|
|
567
|
+
if (!tx.contract_id || !matchAny(tx.contract_id, filter.contractId))
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
if (!factoryAllows(filter, tx.contract_id, factoryContracts))
|
|
571
|
+
continue;
|
|
572
|
+
if (filter.functionName) {
|
|
573
|
+
if (!tx.function_name || !matchPattern(tx.function_name, filter.functionName))
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
576
|
+
if (filter.caller) {
|
|
577
|
+
if (!matchPattern(tx.sender, filter.caller))
|
|
578
|
+
continue;
|
|
915
579
|
}
|
|
580
|
+
if (!traitAllows(filter, tx.contract_id, traitContracts))
|
|
581
|
+
continue;
|
|
582
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
583
|
+
results.push({ tx, events: txEvents });
|
|
916
584
|
}
|
|
585
|
+
break;
|
|
917
586
|
}
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
try {
|
|
936
|
-
return await this.primary.getTip();
|
|
937
|
-
} catch (err) {
|
|
938
|
-
logger.warn("block source primary getTip failed — using DB tap", {
|
|
939
|
-
error: err instanceof Error ? err.message : String(err)
|
|
940
|
-
});
|
|
941
|
-
return this.fallback.getTip();
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
async loadBlockRange(fromHeight, toHeight) {
|
|
945
|
-
try {
|
|
946
|
-
return await this.primary.loadBlockRange(fromHeight, toHeight);
|
|
947
|
-
} catch (err) {
|
|
948
|
-
logger.warn("block source primary loadBlockRange failed — using DB tap", {
|
|
949
|
-
from: fromHeight,
|
|
950
|
-
to: toHeight,
|
|
951
|
-
error: err instanceof Error ? err.message : String(err)
|
|
952
|
-
});
|
|
953
|
-
return this.fallback.loadBlockRange(fromHeight, toHeight);
|
|
587
|
+
case "contract_deploy": {
|
|
588
|
+
for (const tx of transactions) {
|
|
589
|
+
if (tx.type !== "smart_contract")
|
|
590
|
+
continue;
|
|
591
|
+
if (filter.deployer) {
|
|
592
|
+
if (!matchPattern(tx.sender, filter.deployer))
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
if (filter.contractName) {
|
|
596
|
+
const name = tx.contract_id?.split(".")[1] ?? "";
|
|
597
|
+
if (!matchPattern(name, filter.contractName))
|
|
598
|
+
continue;
|
|
599
|
+
}
|
|
600
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
601
|
+
results.push({ tx, events: txEvents });
|
|
602
|
+
}
|
|
603
|
+
break;
|
|
954
604
|
}
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
605
|
+
case "nested_contract_call":
|
|
606
|
+
case "var_set":
|
|
607
|
+
case "map_set":
|
|
608
|
+
case "map_insert":
|
|
609
|
+
case "map_delete": {
|
|
610
|
+
for (const tx of transactions) {
|
|
611
|
+
const txEvents = vmEventsByTx.get(tx.tx_id) ?? [];
|
|
612
|
+
const matched = txEvents.filter((e) => {
|
|
613
|
+
if (e.type !== filter.type)
|
|
614
|
+
return false;
|
|
615
|
+
const data = e.data;
|
|
616
|
+
if (!data)
|
|
617
|
+
return false;
|
|
618
|
+
const contractId = printContractId(data);
|
|
619
|
+
if ("contractId" in filter && filter.contractId) {
|
|
620
|
+
if (!contractId || !matchAny(contractId, filter.contractId))
|
|
621
|
+
return false;
|
|
622
|
+
}
|
|
623
|
+
if (!traitAllows(filter, contractId, traitContracts))
|
|
624
|
+
return false;
|
|
625
|
+
if (!factoryAllows(filter, contractId, factoryContracts))
|
|
626
|
+
return false;
|
|
627
|
+
if (filter.type === "nested_contract_call") {
|
|
628
|
+
if (filter.functionName) {
|
|
629
|
+
if (typeof data.function_name !== "string" || !matchPattern(data.function_name, filter.functionName))
|
|
630
|
+
return false;
|
|
631
|
+
}
|
|
632
|
+
if (filter.caller) {
|
|
633
|
+
if (typeof data.caller !== "string" || !matchPattern(data.caller, filter.caller))
|
|
634
|
+
return false;
|
|
635
|
+
}
|
|
636
|
+
if (filter.sender) {
|
|
637
|
+
if (typeof data.sender !== "string" || !matchPattern(data.sender, filter.sender))
|
|
638
|
+
return false;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
if (filter.type === "var_set" && filter.varName) {
|
|
642
|
+
if (typeof data.var_name !== "string" || !matchPattern(data.var_name, filter.varName))
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
645
|
+
if ((filter.type === "map_set" || filter.type === "map_insert" || filter.type === "map_delete") && filter.map) {
|
|
646
|
+
if (typeof data.map_name !== "string" || !matchPattern(data.map_name, filter.map))
|
|
647
|
+
return false;
|
|
648
|
+
}
|
|
649
|
+
return true;
|
|
650
|
+
});
|
|
651
|
+
if (matched.length > 0)
|
|
652
|
+
results.push({ tx, events: matched });
|
|
653
|
+
}
|
|
654
|
+
break;
|
|
963
655
|
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
656
|
+
case "print_event": {
|
|
657
|
+
for (const tx of transactions) {
|
|
658
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
659
|
+
const matched = txEvents.filter((e) => {
|
|
660
|
+
if (e.type !== "smart_contract_event" && e.type !== "contract_event")
|
|
661
|
+
return false;
|
|
662
|
+
const data = e.data;
|
|
663
|
+
if (!data)
|
|
664
|
+
return false;
|
|
665
|
+
if (data.topic !== "print")
|
|
666
|
+
return false;
|
|
667
|
+
const contractId = printContractId(data);
|
|
668
|
+
if (filter.contractId) {
|
|
669
|
+
if (!contractId || !matchAny(contractId, filter.contractId))
|
|
670
|
+
return false;
|
|
671
|
+
}
|
|
672
|
+
if (!traitAllows(filter, contractId, traitContracts))
|
|
673
|
+
return false;
|
|
674
|
+
if (!factoryAllows(filter, contractId, factoryContracts))
|
|
675
|
+
return false;
|
|
676
|
+
return true;
|
|
677
|
+
});
|
|
678
|
+
if (matched.length > 0) {
|
|
679
|
+
results.push({ tx, events: matched });
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
break;
|
|
975
683
|
}
|
|
976
|
-
return new ObserverHttpBlockSource({
|
|
977
|
-
baseUrl,
|
|
978
|
-
token: process.env.OBSERVER_HTTP_EXPORT_TOKEN || null
|
|
979
|
-
});
|
|
980
|
-
}
|
|
981
|
-
if (process.env.SUBGRAPH_SOURCE === "streams-index" && subgraph && isStreamsIndexEligible(subgraph)) {
|
|
982
|
-
return new FallbackBlockSource(new PublicApiBlockSource(buildHttpClient(), referencedIndexEventTypes(subgraph), canSparseScan(subgraph) ? sparseProbeTargets(subgraph) : undefined, needsTransactionData(subgraph)), postgresBlockSource);
|
|
983
|
-
}
|
|
984
|
-
if (process.env.SUBGRAPH_SOURCE === "streams-index" && subgraph) {
|
|
985
|
-
logger.debug("Subgraph not streams-index eligible, using DB tap", {
|
|
986
|
-
subgraph: subgraph.name
|
|
987
|
-
});
|
|
988
684
|
}
|
|
989
|
-
return
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
// src/runtime/trigger-evaluator.ts
|
|
993
|
-
import { getSourceDb as getSourceDb3 } from "@secondlayer/shared/db";
|
|
994
|
-
import { resolveTraitContractIds } from "@secondlayer/shared/db/queries/contracts";
|
|
995
|
-
|
|
996
|
-
// src/runtime/decoder-bound.ts
|
|
997
|
-
import { decodeStreamsCursor, isEmptyRangeCursor } from "@secondlayer/shared";
|
|
998
|
-
import { getSourceDb as getSourceDb2 } from "@secondlayer/shared/db";
|
|
999
|
-
function decoderNameForEventType(indexEventType) {
|
|
1000
|
-
return `decode.${indexEventType}.v1`;
|
|
1001
|
-
}
|
|
1002
|
-
function decoderNamesForIndexEventTypes(eventTypes) {
|
|
1003
|
-
return eventTypes.filter((t) => !VM_INDEX_EVENT_TYPES.has(t)).map(decoderNameForEventType);
|
|
1004
|
-
}
|
|
1005
|
-
function decoderNamesForSubgraph(subgraph) {
|
|
1006
|
-
return decoderNamesForIndexEventTypes(referencedIndexEventTypes(subgraph));
|
|
1007
|
-
}
|
|
1008
|
-
function usesDecodedIndexPlane(subgraph) {
|
|
1009
|
-
return process.env.SUBGRAPH_SOURCE === "streams-index" && isStreamsIndexEligible(subgraph);
|
|
685
|
+
return results;
|
|
1010
686
|
}
|
|
1011
|
-
function
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
return decoded.block_height;
|
|
1018
|
-
return Math.max(0, decoded.block_height - 1);
|
|
1019
|
-
} catch {
|
|
1020
|
-
return null;
|
|
687
|
+
function matchSources(sources, transactions, events, traitContracts = new Map, factoryContracts = new Map, vmEvents = []) {
|
|
688
|
+
const eventsByTx = new Map;
|
|
689
|
+
for (const event of events) {
|
|
690
|
+
const list = eventsByTx.get(event.tx_id) ?? [];
|
|
691
|
+
list.push(event);
|
|
692
|
+
eventsByTx.set(event.tx_id, list);
|
|
1021
693
|
}
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
const rows = await sourceDb.selectFrom("decoder_checkpoints").select(["decoder_name", "last_cursor"]).where("decoder_name", "in", decoderNames).execute();
|
|
1028
|
-
const byName = new Map(rows.map((r) => [r.decoder_name, r.last_cursor]));
|
|
1029
|
-
const missing = [];
|
|
1030
|
-
const heights = [];
|
|
1031
|
-
for (const name of decoderNames) {
|
|
1032
|
-
const height = committedHeight(byName.get(name));
|
|
1033
|
-
if (height === null)
|
|
1034
|
-
missing.push(name);
|
|
1035
|
-
else
|
|
1036
|
-
heights.push(height);
|
|
694
|
+
const vmEventsByTx = new Map;
|
|
695
|
+
for (const event of vmEvents) {
|
|
696
|
+
const list = vmEventsByTx.get(event.tx_id) ?? [];
|
|
697
|
+
list.push(event);
|
|
698
|
+
vmEventsByTx.set(event.tx_id, list);
|
|
1037
699
|
}
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
700
|
+
const seen = new Set;
|
|
701
|
+
const results = [];
|
|
702
|
+
for (const [sourceName, filter] of Object.entries(sources)) {
|
|
703
|
+
const matches = matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts, vmEventsByTx);
|
|
704
|
+
for (const match of matches) {
|
|
705
|
+
const dedupeKey = `${match.tx.tx_id}:${sourceName}`;
|
|
706
|
+
if (!seen.has(dedupeKey)) {
|
|
707
|
+
seen.add(dedupeKey);
|
|
708
|
+
results.push({ ...match, sourceName });
|
|
709
|
+
}
|
|
710
|
+
}
|
|
1048
711
|
}
|
|
1049
|
-
return
|
|
1050
|
-
ok: true,
|
|
1051
|
-
tip: Math.min(rawTip, bound.height),
|
|
1052
|
-
floor: bound.height
|
|
1053
|
-
};
|
|
712
|
+
return results;
|
|
1054
713
|
}
|
|
1055
714
|
|
|
1056
715
|
// src/runtime/trigger-evaluator.ts
|
|
@@ -1473,9 +1132,9 @@ async function replayChainWebhook(db, sub, input, opts) {
|
|
|
1473
1132
|
return { replayId, enqueuedCount: enqueued, scannedCount: scanned };
|
|
1474
1133
|
}
|
|
1475
1134
|
export {
|
|
1476
|
-
|
|
1477
|
-
|
|
1135
|
+
replayChainWebhook,
|
|
1136
|
+
replayWebhook
|
|
1478
1137
|
};
|
|
1479
1138
|
|
|
1480
|
-
//# debugId=
|
|
1139
|
+
//# debugId=12B00680B3E69AE464756E2164756E21
|
|
1481
1140
|
//# sourceMappingURL=replay.js.map
|