@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.
- package/dist/src/index.d.ts +4 -2
- package/dist/src/index.js +3070 -3066
- package/dist/src/index.js.map +7 -7
- package/dist/src/runtime/block-processor.js +706 -372
- package/dist/src/runtime/block-processor.js.map +7 -6
- package/dist/src/runtime/catchup.js +706 -372
- package/dist/src/runtime/catchup.js.map +7 -6
- package/dist/src/runtime/processor.js +706 -372
- package/dist/src/runtime/processor.js.map +7 -6
- package/dist/src/runtime/reindex.js +706 -372
- package/dist/src/runtime/reindex.js.map +7 -6
- package/dist/src/runtime/reorg.js +706 -372
- package/dist/src/runtime/reorg.js.map +7 -6
- package/dist/src/runtime/replay.js +12 -6
- package/dist/src/runtime/replay.js.map +4 -4
- package/dist/src/runtime/runner.js +604 -8
- package/dist/src/runtime/runner.js.map +6 -4
- package/dist/src/runtime/source-matcher.d.ts +8 -1
- package/dist/src/runtime/source-matcher.js +12 -5
- package/dist/src/runtime/source-matcher.js.map +3 -3
- package/dist/src/service.js +706 -372
- package/dist/src/service.js.map +7 -6
- package/dist/src/testing/index.js +604 -8
- package/dist/src/testing/index.js.map +6 -4
- package/package.json +1 -1
|
@@ -32,15 +32,614 @@ function decodeFunctionArgs(args) {
|
|
|
32
32
|
return args.map(decodeClarityValue);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
// src/runtime/source-matcher.ts
|
|
36
|
+
var patternCache = new Map;
|
|
37
|
+
function matchAny(value, pattern) {
|
|
38
|
+
return Array.isArray(pattern) ? pattern.some((p) => matchPattern(value, p)) : matchPattern(value, pattern);
|
|
39
|
+
}
|
|
40
|
+
function matchPattern(value, pattern) {
|
|
41
|
+
if (!pattern.includes("*"))
|
|
42
|
+
return value === pattern;
|
|
43
|
+
let re = patternCache.get(pattern);
|
|
44
|
+
if (!re) {
|
|
45
|
+
const regex = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
46
|
+
re = new RegExp(`^${regex}$`);
|
|
47
|
+
patternCache.set(pattern, re);
|
|
48
|
+
}
|
|
49
|
+
return re.test(value);
|
|
50
|
+
}
|
|
51
|
+
var EMPTY_SET = new Set;
|
|
52
|
+
function factoryAllows(filter, contractId, factoryContracts) {
|
|
53
|
+
if (!filter.factory)
|
|
54
|
+
return true;
|
|
55
|
+
if (!contractId)
|
|
56
|
+
return false;
|
|
57
|
+
return (factoryContracts.get(filter.factory.from) ?? EMPTY_SET).has(contractId);
|
|
58
|
+
}
|
|
59
|
+
function readPath(payload, path) {
|
|
60
|
+
let cur = payload;
|
|
61
|
+
for (const key of path.split(".")) {
|
|
62
|
+
if (cur === null || typeof cur !== "object")
|
|
63
|
+
return;
|
|
64
|
+
cur = cur[key];
|
|
65
|
+
}
|
|
66
|
+
return cur;
|
|
67
|
+
}
|
|
68
|
+
function traitAllows(filter, contractId, traitContracts) {
|
|
69
|
+
const trait = filter.trait;
|
|
70
|
+
if (!trait)
|
|
71
|
+
return true;
|
|
72
|
+
if (!contractId)
|
|
73
|
+
return false;
|
|
74
|
+
return (traitContracts.get(trait) ?? EMPTY_SET).has(contractId);
|
|
75
|
+
}
|
|
76
|
+
function assetContract(assetId) {
|
|
77
|
+
return assetId?.split("::")[0];
|
|
78
|
+
}
|
|
79
|
+
function printContractId(data) {
|
|
80
|
+
if (!data)
|
|
81
|
+
return;
|
|
82
|
+
const id = (typeof data.contract_identifier === "string" ? data.contract_identifier : undefined) ?? (typeof data.contract_id === "string" ? data.contract_id : undefined);
|
|
83
|
+
return id || undefined;
|
|
84
|
+
}
|
|
85
|
+
function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts = new Map) {
|
|
86
|
+
const results = [];
|
|
87
|
+
switch (filter.type) {
|
|
88
|
+
case "stx_transfer":
|
|
89
|
+
case "stx_mint":
|
|
90
|
+
case "stx_burn":
|
|
91
|
+
case "stx_lock": {
|
|
92
|
+
const eventType = `${filter.type}_event`;
|
|
93
|
+
for (const tx of transactions) {
|
|
94
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
95
|
+
const matched = txEvents.filter((e) => e.type === eventType);
|
|
96
|
+
if (matched.length === 0)
|
|
97
|
+
continue;
|
|
98
|
+
const filtered = matched.filter((e) => {
|
|
99
|
+
const data = e.data;
|
|
100
|
+
if (!data)
|
|
101
|
+
return false;
|
|
102
|
+
if ("sender" in filter && filter.sender) {
|
|
103
|
+
if (!matchPattern(data.sender, filter.sender))
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
if ("recipient" in filter && filter.recipient) {
|
|
107
|
+
if (!matchPattern(data.recipient, filter.recipient))
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
if ("lockedAddress" in filter && filter.lockedAddress) {
|
|
111
|
+
if (!matchPattern(data.locked_address, filter.lockedAddress))
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
if ("minAmount" in filter && filter.minAmount !== undefined) {
|
|
115
|
+
const amount = BigInt(data.amount ?? data.locked_amount ?? "0");
|
|
116
|
+
if (amount < filter.minAmount)
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
if ("maxAmount" in filter && filter.maxAmount !== undefined) {
|
|
120
|
+
const amount = BigInt(data.amount ?? "0");
|
|
121
|
+
if (amount > filter.maxAmount)
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
});
|
|
126
|
+
if (filtered.length > 0) {
|
|
127
|
+
results.push({ tx, events: filtered });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
case "ft_transfer":
|
|
133
|
+
case "ft_mint":
|
|
134
|
+
case "ft_burn": {
|
|
135
|
+
const eventType = `${filter.type}_event`;
|
|
136
|
+
for (const tx of transactions) {
|
|
137
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
138
|
+
const matched = txEvents.filter((e) => {
|
|
139
|
+
if (e.type !== eventType)
|
|
140
|
+
return false;
|
|
141
|
+
const data = e.data;
|
|
142
|
+
if (!data)
|
|
143
|
+
return false;
|
|
144
|
+
if (filter.assetIdentifier) {
|
|
145
|
+
const assetId = data.asset_identifier;
|
|
146
|
+
if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
|
|
150
|
+
return false;
|
|
151
|
+
if ("sender" in filter && filter.sender) {
|
|
152
|
+
if (!matchPattern(data.sender, filter.sender))
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
if ("recipient" in filter && filter.recipient) {
|
|
156
|
+
if (!matchPattern(data.recipient, filter.recipient))
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
if (filter.minAmount !== undefined) {
|
|
160
|
+
const amount = BigInt(data.amount ?? "0");
|
|
161
|
+
if (amount < filter.minAmount)
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
return true;
|
|
165
|
+
});
|
|
166
|
+
if (matched.length > 0) {
|
|
167
|
+
results.push({ tx, events: matched });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
case "nft_transfer":
|
|
173
|
+
case "nft_mint":
|
|
174
|
+
case "nft_burn": {
|
|
175
|
+
const eventType = `${filter.type}_event`;
|
|
176
|
+
for (const tx of transactions) {
|
|
177
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
178
|
+
const matched = txEvents.filter((e) => {
|
|
179
|
+
if (e.type !== eventType)
|
|
180
|
+
return false;
|
|
181
|
+
const data = e.data;
|
|
182
|
+
if (!data)
|
|
183
|
+
return false;
|
|
184
|
+
if (filter.assetIdentifier) {
|
|
185
|
+
const assetId = data.asset_identifier;
|
|
186
|
+
if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
|
|
190
|
+
return false;
|
|
191
|
+
if ("sender" in filter && filter.sender) {
|
|
192
|
+
if (!matchPattern(data.sender, filter.sender))
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
if ("recipient" in filter && filter.recipient) {
|
|
196
|
+
if (!matchPattern(data.recipient, filter.recipient))
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
return true;
|
|
200
|
+
});
|
|
201
|
+
if (matched.length > 0) {
|
|
202
|
+
results.push({ tx, events: matched });
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
case "contract_call": {
|
|
208
|
+
for (const tx of transactions) {
|
|
209
|
+
if (tx.type !== "contract_call")
|
|
210
|
+
continue;
|
|
211
|
+
if (filter.contractId) {
|
|
212
|
+
if (!tx.contract_id || !matchAny(tx.contract_id, filter.contractId))
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
if (!factoryAllows(filter, tx.contract_id, factoryContracts))
|
|
216
|
+
continue;
|
|
217
|
+
if (filter.functionName) {
|
|
218
|
+
if (!tx.function_name || !matchPattern(tx.function_name, filter.functionName))
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
if (filter.caller) {
|
|
222
|
+
if (!matchPattern(tx.sender, filter.caller))
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (!traitAllows(filter, tx.contract_id, traitContracts))
|
|
226
|
+
continue;
|
|
227
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
228
|
+
results.push({ tx, events: txEvents });
|
|
229
|
+
}
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
case "contract_deploy": {
|
|
233
|
+
for (const tx of transactions) {
|
|
234
|
+
if (tx.type !== "smart_contract")
|
|
235
|
+
continue;
|
|
236
|
+
if (filter.deployer) {
|
|
237
|
+
if (!matchPattern(tx.sender, filter.deployer))
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
if (filter.contractName) {
|
|
241
|
+
const name = tx.contract_id?.split(".")[1] ?? "";
|
|
242
|
+
if (!matchPattern(name, filter.contractName))
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
246
|
+
results.push({ tx, events: txEvents });
|
|
247
|
+
}
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
case "print_event": {
|
|
251
|
+
for (const tx of transactions) {
|
|
252
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
253
|
+
const matched = txEvents.filter((e) => {
|
|
254
|
+
if (e.type !== "smart_contract_event" && e.type !== "contract_event")
|
|
255
|
+
return false;
|
|
256
|
+
const data = e.data;
|
|
257
|
+
if (!data)
|
|
258
|
+
return false;
|
|
259
|
+
if (data.topic !== "print")
|
|
260
|
+
return false;
|
|
261
|
+
const contractId = printContractId(data);
|
|
262
|
+
if (filter.contractId) {
|
|
263
|
+
if (!contractId || !matchAny(contractId, filter.contractId))
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
if (!traitAllows(filter, contractId, traitContracts))
|
|
267
|
+
return false;
|
|
268
|
+
if (!factoryAllows(filter, contractId, factoryContracts))
|
|
269
|
+
return false;
|
|
270
|
+
return true;
|
|
271
|
+
});
|
|
272
|
+
if (matched.length > 0) {
|
|
273
|
+
results.push({ tx, events: matched });
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return results;
|
|
280
|
+
}
|
|
281
|
+
function matchSources(sources, transactions, events, traitContracts = new Map, factoryContracts = new Map) {
|
|
282
|
+
const eventsByTx = new Map;
|
|
283
|
+
for (const event of events) {
|
|
284
|
+
const list = eventsByTx.get(event.tx_id) ?? [];
|
|
285
|
+
list.push(event);
|
|
286
|
+
eventsByTx.set(event.tx_id, list);
|
|
287
|
+
}
|
|
288
|
+
const seen = new Set;
|
|
289
|
+
const results = [];
|
|
290
|
+
for (const [sourceName, filter] of Object.entries(sources)) {
|
|
291
|
+
const matches = matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts);
|
|
292
|
+
for (const match of matches) {
|
|
293
|
+
const dedupeKey = `${match.tx.tx_id}:${sourceName}`;
|
|
294
|
+
if (!seen.has(dedupeKey)) {
|
|
295
|
+
seen.add(dedupeKey);
|
|
296
|
+
results.push({ ...match, sourceName });
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return results;
|
|
301
|
+
}
|
|
302
|
+
|
|
35
303
|
// src/runtime/runner.ts
|
|
36
304
|
import { getErrorMessage } from "@secondlayer/shared";
|
|
37
305
|
import { logger } from "@secondlayer/shared/logger";
|
|
38
306
|
import {
|
|
39
307
|
clarityValueToJS,
|
|
40
|
-
deserializeCV as
|
|
308
|
+
deserializeCV as deserializeCV3,
|
|
41
309
|
toCamelCase
|
|
42
310
|
} from "@secondlayer/stacks/clarity";
|
|
43
311
|
|
|
312
|
+
// src/print-schema.ts
|
|
313
|
+
import {
|
|
314
|
+
deserializeCV as deserializeCV2
|
|
315
|
+
} from "@secondlayer/stacks/clarity";
|
|
316
|
+
function camelizeDataKey(str) {
|
|
317
|
+
return str.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
318
|
+
}
|
|
319
|
+
function cvToTree(cv) {
|
|
320
|
+
switch (cv.type) {
|
|
321
|
+
case "uint":
|
|
322
|
+
case "int":
|
|
323
|
+
return { kind: cv.type };
|
|
324
|
+
case "true":
|
|
325
|
+
case "false":
|
|
326
|
+
return { kind: "bool" };
|
|
327
|
+
case "address":
|
|
328
|
+
case "contract":
|
|
329
|
+
return { kind: "principal" };
|
|
330
|
+
case "buffer":
|
|
331
|
+
return { kind: "buffer", len: cv.value.length / 2 };
|
|
332
|
+
case "ascii":
|
|
333
|
+
return { kind: "ascii", len: cv.value.length };
|
|
334
|
+
case "utf8":
|
|
335
|
+
return { kind: "utf8", len: new TextEncoder().encode(cv.value).length };
|
|
336
|
+
case "none":
|
|
337
|
+
return { kind: "optional", inner: null };
|
|
338
|
+
case "some":
|
|
339
|
+
return { kind: "optional", inner: cvToTree(cv.value) };
|
|
340
|
+
case "ok":
|
|
341
|
+
return { kind: "response", ok: cvToTree(cv.value), err: null };
|
|
342
|
+
case "err":
|
|
343
|
+
return { kind: "response", ok: null, err: cvToTree(cv.value) };
|
|
344
|
+
case "list": {
|
|
345
|
+
let inner = null;
|
|
346
|
+
for (const el of cv.value) {
|
|
347
|
+
const t = cvToTree(el);
|
|
348
|
+
inner = inner ? unify(inner, t) : t;
|
|
349
|
+
}
|
|
350
|
+
return { kind: "list", inner };
|
|
351
|
+
}
|
|
352
|
+
case "tuple": {
|
|
353
|
+
const entries = new Map;
|
|
354
|
+
for (const [k, v] of Object.entries(cv.value)) {
|
|
355
|
+
entries.set(k, { tree: cvToTree(v), present: 1 });
|
|
356
|
+
}
|
|
357
|
+
return { kind: "tuple", count: 1, entries };
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
function unifyNullable(a, b) {
|
|
362
|
+
if (!a)
|
|
363
|
+
return b;
|
|
364
|
+
if (!b)
|
|
365
|
+
return a;
|
|
366
|
+
return unify(a, b);
|
|
367
|
+
}
|
|
368
|
+
var UNION_KIND_ORDER = {
|
|
369
|
+
uint: 0,
|
|
370
|
+
int: 1,
|
|
371
|
+
bool: 2,
|
|
372
|
+
principal: 3,
|
|
373
|
+
buffer: 4,
|
|
374
|
+
ascii: 5,
|
|
375
|
+
utf8: 6,
|
|
376
|
+
list: 7,
|
|
377
|
+
tuple: 8,
|
|
378
|
+
response: 9
|
|
379
|
+
};
|
|
380
|
+
function makeUnion(members) {
|
|
381
|
+
const sorted = [...members].sort((a, b) => (UNION_KIND_ORDER[a.kind] ?? 99) - (UNION_KIND_ORDER[b.kind] ?? 99));
|
|
382
|
+
return { kind: "union", members: sorted };
|
|
383
|
+
}
|
|
384
|
+
function unify(a, b) {
|
|
385
|
+
if (a.kind === "optional" || b.kind === "optional") {
|
|
386
|
+
const ai = a.kind === "optional" ? a.inner : a;
|
|
387
|
+
const bi = b.kind === "optional" ? b.inner : b;
|
|
388
|
+
return { kind: "optional", inner: unifyNullable(ai, bi) };
|
|
389
|
+
}
|
|
390
|
+
if (a.kind === "union")
|
|
391
|
+
return unionAdd(a.members, b);
|
|
392
|
+
if (b.kind === "union")
|
|
393
|
+
return unionAdd(b.members, a);
|
|
394
|
+
if (a.kind !== b.kind)
|
|
395
|
+
return makeUnion([a, b]);
|
|
396
|
+
switch (a.kind) {
|
|
397
|
+
case "uint":
|
|
398
|
+
case "int":
|
|
399
|
+
case "bool":
|
|
400
|
+
case "principal":
|
|
401
|
+
return a;
|
|
402
|
+
case "buffer":
|
|
403
|
+
case "ascii":
|
|
404
|
+
case "utf8":
|
|
405
|
+
return { kind: a.kind, len: Math.max(a.len, b.len) };
|
|
406
|
+
case "list":
|
|
407
|
+
return {
|
|
408
|
+
kind: "list",
|
|
409
|
+
inner: unifyNullable(a.inner, b.inner)
|
|
410
|
+
};
|
|
411
|
+
case "response": {
|
|
412
|
+
const rb = b;
|
|
413
|
+
return {
|
|
414
|
+
kind: "response",
|
|
415
|
+
ok: unifyNullable(a.ok, rb.ok),
|
|
416
|
+
err: unifyNullable(a.err, rb.err)
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
case "tuple": {
|
|
420
|
+
const tb = b;
|
|
421
|
+
const entries = new Map([...a.entries].map(([k, e]) => [k, { ...e }]));
|
|
422
|
+
for (const [k, e] of tb.entries) {
|
|
423
|
+
const existing = entries.get(k);
|
|
424
|
+
entries.set(k, existing ? {
|
|
425
|
+
tree: unify(existing.tree, e.tree),
|
|
426
|
+
present: existing.present + e.present
|
|
427
|
+
} : { ...e });
|
|
428
|
+
}
|
|
429
|
+
return { kind: "tuple", count: a.count + tb.count, entries };
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
function unionAdd(members, t) {
|
|
434
|
+
if (t.kind === "union") {
|
|
435
|
+
let acc = { kind: "union", members };
|
|
436
|
+
for (const m of t.members)
|
|
437
|
+
acc = unify(acc, m);
|
|
438
|
+
return acc;
|
|
439
|
+
}
|
|
440
|
+
const next = [...members];
|
|
441
|
+
for (let i = 0;i < next.length; i++) {
|
|
442
|
+
const member = next[i];
|
|
443
|
+
if (!member)
|
|
444
|
+
continue;
|
|
445
|
+
const merged = unify(member, t);
|
|
446
|
+
if (merged.kind !== "union") {
|
|
447
|
+
next[i] = merged;
|
|
448
|
+
return makeUnion(next);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
next.push(t);
|
|
452
|
+
return makeUnion(next);
|
|
453
|
+
}
|
|
454
|
+
function wrapOptional(t) {
|
|
455
|
+
return t.kind === "optional" ? t : { kind: "optional", inner: t };
|
|
456
|
+
}
|
|
457
|
+
function renderClarity(t) {
|
|
458
|
+
if (!t)
|
|
459
|
+
return "?";
|
|
460
|
+
switch (t.kind) {
|
|
461
|
+
case "uint":
|
|
462
|
+
case "int":
|
|
463
|
+
case "bool":
|
|
464
|
+
case "principal":
|
|
465
|
+
return t.kind;
|
|
466
|
+
case "buffer":
|
|
467
|
+
return `(buff ${t.len})`;
|
|
468
|
+
case "ascii":
|
|
469
|
+
return `(string-ascii ${t.len})`;
|
|
470
|
+
case "utf8":
|
|
471
|
+
return `(string-utf8 ${t.len})`;
|
|
472
|
+
case "optional":
|
|
473
|
+
return `(optional ${renderClarity(t.inner)})`;
|
|
474
|
+
case "list":
|
|
475
|
+
return `(list ${renderClarity(t.inner)})`;
|
|
476
|
+
case "response":
|
|
477
|
+
return `(response ${renderClarity(t.ok)} ${renderClarity(t.err)})`;
|
|
478
|
+
case "tuple": {
|
|
479
|
+
const parts = [...t.entries].map(([k, e]) => {
|
|
480
|
+
const tree = e.present < t.count ? wrapOptional(e.tree) : e.tree;
|
|
481
|
+
return `(${k} ${renderClarity(tree)})`;
|
|
482
|
+
});
|
|
483
|
+
return `(tuple ${parts.join(" ")})`;
|
|
484
|
+
}
|
|
485
|
+
case "union":
|
|
486
|
+
return t.members.map(renderClarity).join(" | ");
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
function renderTs(t) {
|
|
490
|
+
if (!t)
|
|
491
|
+
return "unknown";
|
|
492
|
+
switch (t.kind) {
|
|
493
|
+
case "uint":
|
|
494
|
+
case "int":
|
|
495
|
+
return "bigint";
|
|
496
|
+
case "bool":
|
|
497
|
+
return "boolean";
|
|
498
|
+
case "principal":
|
|
499
|
+
case "buffer":
|
|
500
|
+
case "ascii":
|
|
501
|
+
case "utf8":
|
|
502
|
+
return "string";
|
|
503
|
+
case "optional":
|
|
504
|
+
return t.inner ? `${renderTs(t.inner)} | null` : "unknown | null";
|
|
505
|
+
case "list": {
|
|
506
|
+
const inner = renderTs(t.inner);
|
|
507
|
+
return inner.includes(" | ") ? `(${inner})[]` : `${inner}[]`;
|
|
508
|
+
}
|
|
509
|
+
case "response": {
|
|
510
|
+
const sides = [...new Set([renderTs(t.ok), renderTs(t.err)])];
|
|
511
|
+
return sides.join(" | ");
|
|
512
|
+
}
|
|
513
|
+
case "tuple": {
|
|
514
|
+
const parts = [...t.entries].map(([k, e]) => {
|
|
515
|
+
const opt = e.present < t.count ? "?" : "";
|
|
516
|
+
return `${camelizeDataKey(k)}${opt}: ${renderTs(e.tree)}`;
|
|
517
|
+
});
|
|
518
|
+
return `{ ${parts.join("; ")} }`;
|
|
519
|
+
}
|
|
520
|
+
case "union":
|
|
521
|
+
return [...new Set(t.members.map((m) => renderTs(m)))].join(" | ");
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
function toColumnType(t) {
|
|
525
|
+
if (!t)
|
|
526
|
+
return "jsonb";
|
|
527
|
+
switch (t.kind) {
|
|
528
|
+
case "uint":
|
|
529
|
+
return "uint";
|
|
530
|
+
case "int":
|
|
531
|
+
return "int";
|
|
532
|
+
case "bool":
|
|
533
|
+
return "boolean";
|
|
534
|
+
case "principal":
|
|
535
|
+
return "principal";
|
|
536
|
+
case "buffer":
|
|
537
|
+
case "ascii":
|
|
538
|
+
case "utf8":
|
|
539
|
+
return "text";
|
|
540
|
+
case "list":
|
|
541
|
+
case "tuple":
|
|
542
|
+
return "jsonb";
|
|
543
|
+
case "optional":
|
|
544
|
+
return toColumnType(t.inner);
|
|
545
|
+
case "response":
|
|
546
|
+
return t.ok ? toColumnType(t.ok) : "jsonb";
|
|
547
|
+
case "union":
|
|
548
|
+
return "jsonb";
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
var MAX_DECODED_PER_TOPIC_NEWEST = 75;
|
|
552
|
+
var MAX_DECODED_PER_TOPIC_OLDEST = 25;
|
|
553
|
+
function inferPrintTopics(samples) {
|
|
554
|
+
const groups = new Map;
|
|
555
|
+
for (const s of samples) {
|
|
556
|
+
const group = groups.get(s.topic);
|
|
557
|
+
if (group)
|
|
558
|
+
group.push(s);
|
|
559
|
+
else
|
|
560
|
+
groups.set(s.topic, [s]);
|
|
561
|
+
}
|
|
562
|
+
const out = [];
|
|
563
|
+
for (const [topic, rows] of groups) {
|
|
564
|
+
let first = Number.POSITIVE_INFINITY;
|
|
565
|
+
let last = Number.NEGATIVE_INFINITY;
|
|
566
|
+
for (const r of rows) {
|
|
567
|
+
if (r.blockHeight < first)
|
|
568
|
+
first = r.blockHeight;
|
|
569
|
+
if (r.blockHeight > last)
|
|
570
|
+
last = r.blockHeight;
|
|
571
|
+
}
|
|
572
|
+
const withHex = [...rows].filter((r) => r.rawHex !== null).sort((a, b) => b.blockHeight - a.blockHeight);
|
|
573
|
+
const budget = MAX_DECODED_PER_TOPIC_NEWEST + MAX_DECODED_PER_TOPIC_OLDEST;
|
|
574
|
+
const picked = withHex.length <= budget ? withHex : [
|
|
575
|
+
...withHex.slice(0, MAX_DECODED_PER_TOPIC_NEWEST),
|
|
576
|
+
...withHex.slice(-MAX_DECODED_PER_TOPIC_OLDEST)
|
|
577
|
+
];
|
|
578
|
+
const tuples = [];
|
|
579
|
+
let decoded = 0;
|
|
580
|
+
for (const p of picked) {
|
|
581
|
+
try {
|
|
582
|
+
const cv = deserializeCV2(p.rawHex);
|
|
583
|
+
decoded++;
|
|
584
|
+
if (cv.type === "tuple")
|
|
585
|
+
tuples.push(cv);
|
|
586
|
+
} catch {}
|
|
587
|
+
}
|
|
588
|
+
const nonTuple = decoded > 0 && tuples.length === 0;
|
|
589
|
+
const fields = [];
|
|
590
|
+
if (!nonTuple) {
|
|
591
|
+
const stats = new Map;
|
|
592
|
+
for (const t of tuples) {
|
|
593
|
+
for (const [key, value] of Object.entries(t.value)) {
|
|
594
|
+
if (key === "topic")
|
|
595
|
+
continue;
|
|
596
|
+
const tree = cvToTree(value);
|
|
597
|
+
const existing = stats.get(key);
|
|
598
|
+
if (existing) {
|
|
599
|
+
existing.present++;
|
|
600
|
+
existing.tree = unify(existing.tree, tree);
|
|
601
|
+
if (value.type === "none")
|
|
602
|
+
existing.noneCount++;
|
|
603
|
+
if (value.type === "none" || value.type === "some") {
|
|
604
|
+
existing.optionalSeen = true;
|
|
605
|
+
}
|
|
606
|
+
} else {
|
|
607
|
+
stats.set(key, {
|
|
608
|
+
tree,
|
|
609
|
+
present: 1,
|
|
610
|
+
noneCount: value.type === "none" ? 1 : 0,
|
|
611
|
+
optionalSeen: value.type === "none" || value.type === "some"
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
for (const [name, st] of [...stats].sort(([a], [b]) => a.localeCompare(b))) {
|
|
617
|
+
const field = {
|
|
618
|
+
name,
|
|
619
|
+
camel_name: camelizeDataKey(name),
|
|
620
|
+
clarity_type: renderClarity(st.tree),
|
|
621
|
+
ts_type: renderTs(st.tree),
|
|
622
|
+
column_type: toColumnType(st.tree),
|
|
623
|
+
always_present: st.present === tuples.length
|
|
624
|
+
};
|
|
625
|
+
if (st.optionalSeen) {
|
|
626
|
+
field.optional_some_rate = (st.present - st.noneCount) / st.present;
|
|
627
|
+
}
|
|
628
|
+
fields.push(field);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
out.push({
|
|
632
|
+
topic,
|
|
633
|
+
count: rows.length,
|
|
634
|
+
first_height: first,
|
|
635
|
+
last_height: last,
|
|
636
|
+
non_tuple: nonTuple,
|
|
637
|
+
fields
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
return out.sort((a, b) => b.count - a.count);
|
|
641
|
+
}
|
|
642
|
+
|
|
44
643
|
// src/runtime/print-validate.ts
|
|
45
644
|
var COLUMN_TYPES = new Set([
|
|
46
645
|
"text",
|
|
@@ -122,9 +721,6 @@ function validatePrintPayload(prints, topic, data) {
|
|
|
122
721
|
|
|
123
722
|
// src/runtime/runner.ts
|
|
124
723
|
var DEFAULT_ERROR_THRESHOLD = 50;
|
|
125
|
-
function camelCase(str) {
|
|
126
|
-
return str.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
127
|
-
}
|
|
128
724
|
function camelizeKeys(obj) {
|
|
129
725
|
if (obj === null || obj === undefined)
|
|
130
726
|
return obj;
|
|
@@ -134,7 +730,7 @@ function camelizeKeys(obj) {
|
|
|
134
730
|
return obj.map(camelizeKeys);
|
|
135
731
|
const result = {};
|
|
136
732
|
for (const [k, v] of Object.entries(obj)) {
|
|
137
|
-
result[
|
|
733
|
+
result[camelizeDataKey(k)] = camelizeKeys(v);
|
|
138
734
|
}
|
|
139
735
|
return result;
|
|
140
736
|
}
|
|
@@ -201,7 +797,7 @@ function buildContractCallInput(filter, tx) {
|
|
|
201
797
|
return;
|
|
202
798
|
try {
|
|
203
799
|
const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
204
|
-
input[toCamelCase(arg.name)] = decodeArg(arg.type,
|
|
800
|
+
input[toCamelCase(arg.name)] = decodeArg(arg.type, deserializeCV3(clean));
|
|
205
801
|
} catch {}
|
|
206
802
|
});
|
|
207
803
|
return input;
|
|
@@ -324,7 +920,7 @@ function buildEventPayload(filter, tx, event) {
|
|
|
324
920
|
const { topic: _, ...rest } = clarityObj ?? {};
|
|
325
921
|
const data = Object.keys(rest).length > 0 ? camelizeKeys(rest) : rawValue && typeof rawValue !== "object" ? rawValue : {};
|
|
326
922
|
return {
|
|
327
|
-
contractId: decoded
|
|
923
|
+
contractId: printContractId(decoded) ?? tx.contract_id ?? "",
|
|
328
924
|
topic,
|
|
329
925
|
data: data ?? {},
|
|
330
926
|
tx: txMeta
|
|
@@ -483,5 +1079,5 @@ export {
|
|
|
483
1079
|
buildContractCallInput
|
|
484
1080
|
};
|
|
485
1081
|
|
|
486
|
-
//# debugId=
|
|
1082
|
+
//# debugId=88617EDFE963086E64756E2164756E21
|
|
487
1083
|
//# sourceMappingURL=runner.js.map
|