@secondlayer/subgraphs 3.20.0 → 3.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/index.d.ts +188 -16
- package/dist/src/index.js +369 -41
- package/dist/src/index.js.map +12 -11
- package/dist/src/runtime/block-processor.d.ts +100 -13
- package/dist/src/runtime/block-processor.js +208 -22
- package/dist/src/runtime/block-processor.js.map +9 -8
- package/dist/src/runtime/catchup.d.ts +100 -13
- package/dist/src/runtime/catchup.js +208 -22
- package/dist/src/runtime/catchup.js.map +9 -8
- package/dist/src/runtime/context.d.ts +34 -8
- package/dist/src/runtime/context.js +25 -12
- package/dist/src/runtime/context.js.map +4 -4
- package/dist/src/runtime/processor.js +213 -22
- package/dist/src/runtime/processor.js.map +10 -9
- package/dist/src/runtime/reindex.d.ts +100 -13
- package/dist/src/runtime/reindex.js +208 -22
- package/dist/src/runtime/reindex.js.map +9 -8
- package/dist/src/runtime/reorg.d.ts +100 -13
- package/dist/src/runtime/reorg.js +213 -22
- package/dist/src/runtime/reorg.js.map +10 -9
- package/dist/src/runtime/replay.js +29 -6
- package/dist/src/runtime/replay.js.map +3 -3
- package/dist/src/runtime/runner.d.ts +125 -17
- package/dist/src/runtime/runner.js +100 -3
- package/dist/src/runtime/runner.js.map +5 -4
- package/dist/src/runtime/source-matcher.d.ts +98 -11
- package/dist/src/runtime/source-matcher.js +30 -6
- package/dist/src/runtime/source-matcher.js.map +3 -3
- package/dist/src/schema/index.d.ts +100 -13
- package/dist/src/schema/index.js +171 -20
- package/dist/src/schema/index.js.map +5 -5
- package/dist/src/service.js +213 -22
- package/dist/src/service.js.map +10 -9
- package/dist/src/testing/index.d.ts +620 -0
- package/dist/src/testing/index.js +1297 -0
- package/dist/src/testing/index.js.map +16 -0
- package/dist/src/triggers/index.d.ts +87 -9
- package/dist/src/types.d.ts +101 -14
- package/dist/src/validate.d.ts +136 -14
- package/dist/src/validate.js +158 -19
- package/dist/src/validate.js.map +3 -3
- package/package.json +7 -5
package/dist/src/index.js
CHANGED
|
@@ -14,6 +14,44 @@ var ColumnTypeSchema = z.enum([
|
|
|
14
14
|
"timestamp",
|
|
15
15
|
"jsonb"
|
|
16
16
|
]);
|
|
17
|
+
var PrintFieldSchema = z.lazy(() => z.union([
|
|
18
|
+
ColumnTypeSchema,
|
|
19
|
+
z.object({ type: PrintFieldSchema, optional: z.literal(true) }),
|
|
20
|
+
z.object({ tuple: z.record(z.string(), PrintFieldSchema) }),
|
|
21
|
+
z.object({ list: PrintFieldSchema })
|
|
22
|
+
]));
|
|
23
|
+
var AbiTypeSchema = z.lazy(() => z.union([
|
|
24
|
+
z.string(),
|
|
25
|
+
z.object({ buff: z.object({ length: z.number() }) }),
|
|
26
|
+
z.object({ "string-ascii": z.object({ length: z.number() }) }),
|
|
27
|
+
z.object({ "string-utf8": z.object({ length: z.number() }) }),
|
|
28
|
+
z.object({ optional: AbiTypeSchema }),
|
|
29
|
+
z.object({
|
|
30
|
+
list: z.object({ type: AbiTypeSchema, length: z.number() })
|
|
31
|
+
}),
|
|
32
|
+
z.object({
|
|
33
|
+
tuple: z.array(z.object({ name: z.string(), type: AbiTypeSchema }))
|
|
34
|
+
}),
|
|
35
|
+
z.object({
|
|
36
|
+
response: z.object({ ok: AbiTypeSchema, error: AbiTypeSchema })
|
|
37
|
+
})
|
|
38
|
+
]));
|
|
39
|
+
var AbiContractSchema = z.object({
|
|
40
|
+
functions: z.array(z.object({
|
|
41
|
+
name: z.string(),
|
|
42
|
+
access: z.enum(["public", "read-only", "private"], {
|
|
43
|
+
message: "ABI access must be public | read-only | private — a raw Hiro/Clarinet ABI uses `read_only`. Normalize it first: normalizeAbi() from @secondlayer/stacks/clarity."
|
|
44
|
+
}),
|
|
45
|
+
args: z.array(z.object({ name: z.string(), type: AbiTypeSchema })),
|
|
46
|
+
outputs: AbiTypeSchema
|
|
47
|
+
})),
|
|
48
|
+
maps: z.array(z.unknown()).optional(),
|
|
49
|
+
variables: z.array(z.unknown()).optional(),
|
|
50
|
+
fungible_tokens: z.array(z.unknown()).optional(),
|
|
51
|
+
non_fungible_tokens: z.array(z.unknown()).optional(),
|
|
52
|
+
implemented_traits: z.array(z.unknown()).optional(),
|
|
53
|
+
defined_traits: z.array(z.unknown()).optional()
|
|
54
|
+
});
|
|
17
55
|
var SubgraphColumnSchema = z.object({
|
|
18
56
|
type: ColumnTypeSchema,
|
|
19
57
|
nullable: z.boolean().optional(),
|
|
@@ -48,24 +86,105 @@ var VALID_FILTER_TYPES = [
|
|
|
48
86
|
"contract_deploy",
|
|
49
87
|
"print_event"
|
|
50
88
|
];
|
|
51
|
-
var
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
89
|
+
var contractIdField = z.union([
|
|
90
|
+
z.string(),
|
|
91
|
+
z.array(z.string()).min(1).max(20)
|
|
92
|
+
]);
|
|
93
|
+
var traitScope = { trait: z.string().optional() };
|
|
94
|
+
var factoryScope = {
|
|
95
|
+
factory: z.object({ from: z.string().min(1), field: z.string().min(1) }).strict().optional()
|
|
96
|
+
};
|
|
97
|
+
var amountRange = {
|
|
55
98
|
minAmount: z.bigint().optional(),
|
|
56
|
-
maxAmount: z.bigint().optional()
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
99
|
+
maxAmount: z.bigint().optional()
|
|
100
|
+
};
|
|
101
|
+
var SubgraphFilterUnion = z.discriminatedUnion("type", [
|
|
102
|
+
z.object({
|
|
103
|
+
type: z.literal("stx_transfer"),
|
|
104
|
+
sender: z.string().optional(),
|
|
105
|
+
recipient: z.string().optional(),
|
|
106
|
+
...amountRange
|
|
107
|
+
}).strict(),
|
|
108
|
+
z.object({
|
|
109
|
+
type: z.literal("stx_mint"),
|
|
110
|
+
recipient: z.string().optional(),
|
|
111
|
+
minAmount: z.bigint().optional()
|
|
112
|
+
}).strict(),
|
|
113
|
+
z.object({
|
|
114
|
+
type: z.literal("stx_burn"),
|
|
115
|
+
sender: z.string().optional(),
|
|
116
|
+
minAmount: z.bigint().optional()
|
|
117
|
+
}).strict(),
|
|
118
|
+
z.object({
|
|
119
|
+
type: z.literal("stx_lock"),
|
|
120
|
+
lockedAddress: z.string().optional(),
|
|
121
|
+
minAmount: z.bigint().optional()
|
|
122
|
+
}).strict(),
|
|
123
|
+
z.object({
|
|
124
|
+
type: z.literal("ft_transfer"),
|
|
125
|
+
assetIdentifier: z.string().optional(),
|
|
126
|
+
sender: z.string().optional(),
|
|
127
|
+
recipient: z.string().optional(),
|
|
128
|
+
minAmount: z.bigint().optional(),
|
|
129
|
+
...traitScope
|
|
130
|
+
}).strict(),
|
|
131
|
+
z.object({
|
|
132
|
+
type: z.literal("ft_mint"),
|
|
133
|
+
assetIdentifier: z.string().optional(),
|
|
134
|
+
recipient: z.string().optional(),
|
|
135
|
+
minAmount: z.bigint().optional(),
|
|
136
|
+
...traitScope
|
|
137
|
+
}).strict(),
|
|
138
|
+
z.object({
|
|
139
|
+
type: z.literal("ft_burn"),
|
|
140
|
+
assetIdentifier: z.string().optional(),
|
|
141
|
+
sender: z.string().optional(),
|
|
142
|
+
minAmount: z.bigint().optional(),
|
|
143
|
+
...traitScope
|
|
144
|
+
}).strict(),
|
|
145
|
+
z.object({
|
|
146
|
+
type: z.literal("nft_transfer"),
|
|
147
|
+
assetIdentifier: z.string().optional(),
|
|
148
|
+
sender: z.string().optional(),
|
|
149
|
+
recipient: z.string().optional(),
|
|
150
|
+
...traitScope
|
|
151
|
+
}).strict(),
|
|
152
|
+
z.object({
|
|
153
|
+
type: z.literal("nft_mint"),
|
|
154
|
+
assetIdentifier: z.string().optional(),
|
|
155
|
+
recipient: z.string().optional(),
|
|
156
|
+
...traitScope
|
|
157
|
+
}).strict(),
|
|
158
|
+
z.object({
|
|
159
|
+
type: z.literal("nft_burn"),
|
|
160
|
+
assetIdentifier: z.string().optional(),
|
|
161
|
+
sender: z.string().optional(),
|
|
162
|
+
...traitScope
|
|
163
|
+
}).strict(),
|
|
164
|
+
z.object({
|
|
165
|
+
type: z.literal("contract_call"),
|
|
166
|
+
contractId: contractIdField.optional(),
|
|
167
|
+
functionName: z.string().optional(),
|
|
168
|
+
caller: z.string().optional(),
|
|
169
|
+
abi: AbiContractSchema.optional(),
|
|
170
|
+
...traitScope,
|
|
171
|
+
...factoryScope
|
|
172
|
+
}).strict(),
|
|
173
|
+
z.object({
|
|
174
|
+
type: z.literal("contract_deploy"),
|
|
175
|
+
deployer: z.string().optional(),
|
|
176
|
+
contractName: z.string().optional()
|
|
177
|
+
}).strict(),
|
|
178
|
+
z.object({
|
|
179
|
+
type: z.literal("print_event"),
|
|
180
|
+
contractId: contractIdField.optional(),
|
|
181
|
+
topic: z.string().optional(),
|
|
182
|
+
prints: z.record(z.string(), z.record(z.string(), PrintFieldSchema)).optional(),
|
|
183
|
+
...traitScope,
|
|
184
|
+
...factoryScope
|
|
185
|
+
}).strict()
|
|
186
|
+
]);
|
|
187
|
+
var SubgraphFilterSchema = SubgraphFilterUnion;
|
|
69
188
|
var SubgraphDefinitionSchema = z.object({
|
|
70
189
|
name: SubgraphNameSchema,
|
|
71
190
|
version: z.string().optional(),
|
|
@@ -79,6 +198,23 @@ var SubgraphDefinitionSchema = z.object({
|
|
|
79
198
|
function validateSubgraphDefinition(def) {
|
|
80
199
|
return SubgraphDefinitionSchema.parse(def);
|
|
81
200
|
}
|
|
201
|
+
function filterFieldsByType() {
|
|
202
|
+
const options = SubgraphFilterUnion?._def?.options ?? [];
|
|
203
|
+
const out = [];
|
|
204
|
+
for (const option of options) {
|
|
205
|
+
const shape = option?.shape ?? option?._def?.shape;
|
|
206
|
+
if (!shape)
|
|
207
|
+
continue;
|
|
208
|
+
const literal = shape.type?._def?.values?.[0] ?? shape.type?._def?.value ?? undefined;
|
|
209
|
+
if (typeof literal !== "string")
|
|
210
|
+
continue;
|
|
211
|
+
out.push({
|
|
212
|
+
type: literal,
|
|
213
|
+
fields: Object.keys(shape).filter((k) => k !== "type")
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return out;
|
|
217
|
+
}
|
|
82
218
|
|
|
83
219
|
// src/runtime/context.ts
|
|
84
220
|
import { logger } from "@secondlayer/shared/logger";
|
|
@@ -179,6 +315,18 @@ function emitJournalDDL(schemaName) {
|
|
|
179
315
|
`CREATE INDEX IF NOT EXISTS idx_${schemaName}_journal_height ON ${schemaName}._journal (block_height)`
|
|
180
316
|
];
|
|
181
317
|
}
|
|
318
|
+
function emitFactoryDDL(schemaName) {
|
|
319
|
+
return [
|
|
320
|
+
`CREATE TABLE IF NOT EXISTS ${schemaName}._factory_addresses (
|
|
321
|
+
source_name TEXT NOT NULL,
|
|
322
|
+
address TEXT NOT NULL,
|
|
323
|
+
block_height BIGINT NOT NULL,
|
|
324
|
+
_created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
325
|
+
PRIMARY KEY (source_name, address)
|
|
326
|
+
)`,
|
|
327
|
+
`CREATE INDEX IF NOT EXISTS idx_${schemaName}_factory_height ON ${schemaName}._factory_addresses (block_height)`
|
|
328
|
+
];
|
|
329
|
+
}
|
|
182
330
|
function emitForeignKeyDDL(schemaName, tableName, tableDef) {
|
|
183
331
|
return (tableDef.relations ?? []).map((rel) => {
|
|
184
332
|
const constraintName = `fk_${schemaName}_${tableName}_${rel.name}`;
|
|
@@ -197,6 +345,9 @@ function generateSubgraphSQL(def, schemaNameOverride) {
|
|
|
197
345
|
statements.push(...emitTableDDL(schemaName, tableName, tableDef));
|
|
198
346
|
}
|
|
199
347
|
statements.push(...emitJournalDDL(schemaName));
|
|
348
|
+
if (Object.values(def.sources ?? {}).some((src) => src.factory !== undefined)) {
|
|
349
|
+
statements.push(...emitFactoryDDL(schemaName));
|
|
350
|
+
}
|
|
200
351
|
for (const [tableName, tableDef] of Object.entries(def.schema)) {
|
|
201
352
|
statements.push(...emitForeignKeyDDL(schemaName, tableName, tableDef));
|
|
202
353
|
}
|
|
@@ -341,23 +492,21 @@ class SubgraphContext {
|
|
|
341
492
|
formatUnits(value, decimals) {
|
|
342
493
|
return formatUnits(value, decimals);
|
|
343
494
|
}
|
|
344
|
-
async
|
|
345
|
-
this.validateTable(table);
|
|
495
|
+
async readRows(table, where, limit) {
|
|
346
496
|
const qualifiedTable = `"${this.pgSchemaName}"."${table}"`;
|
|
347
497
|
const { clause } = buildWhereClause(where);
|
|
348
|
-
const query = `SELECT * FROM ${qualifiedTable} WHERE ${clause} LIMIT
|
|
498
|
+
const query = `SELECT * FROM ${qualifiedTable} WHERE ${clause}${limit !== undefined ? ` LIMIT ${limit}` : ""}`;
|
|
349
499
|
const { rows } = await sql.raw(query).execute(this.db);
|
|
350
|
-
|
|
351
|
-
|
|
500
|
+
return rows.map((r) => this.coerceRow(table, r));
|
|
501
|
+
}
|
|
502
|
+
async findOne(table, where) {
|
|
503
|
+
this.validateTable(table);
|
|
504
|
+
const row = (await this.readRows(table, where, 1))[0] ?? null;
|
|
505
|
+
return this.overlayOne(table, where, row);
|
|
352
506
|
}
|
|
353
507
|
async findMany(table, where) {
|
|
354
508
|
this.validateTable(table);
|
|
355
|
-
|
|
356
|
-
const { clause } = buildWhereClause(where);
|
|
357
|
-
const query = `SELECT * FROM ${qualifiedTable} WHERE ${clause}`;
|
|
358
|
-
const { rows } = await sql.raw(query).execute(this.db);
|
|
359
|
-
const dbRows = rows.map((r) => this.coerceRow(table, r));
|
|
360
|
-
return this.overlayMany(table, where, dbRows);
|
|
509
|
+
return this.overlayMany(table, where, await this.readRows(table, where));
|
|
361
510
|
}
|
|
362
511
|
overlayOne(table, where, dbRow) {
|
|
363
512
|
if (this.ops.length === 0)
|
|
@@ -837,6 +986,87 @@ import {
|
|
|
837
986
|
deserializeCV as deserializeCV2,
|
|
838
987
|
toCamelCase
|
|
839
988
|
} from "@secondlayer/stacks/clarity";
|
|
989
|
+
|
|
990
|
+
// src/runtime/print-validate.ts
|
|
991
|
+
var COLUMN_TYPES = new Set([
|
|
992
|
+
"text",
|
|
993
|
+
"uint",
|
|
994
|
+
"int",
|
|
995
|
+
"principal",
|
|
996
|
+
"boolean",
|
|
997
|
+
"timestamp",
|
|
998
|
+
"jsonb"
|
|
999
|
+
]);
|
|
1000
|
+
function isColumnType(field) {
|
|
1001
|
+
return typeof field === "string" && COLUMN_TYPES.has(field);
|
|
1002
|
+
}
|
|
1003
|
+
function matchesField(field, value) {
|
|
1004
|
+
if (isColumnType(field)) {
|
|
1005
|
+
switch (field) {
|
|
1006
|
+
case "uint":
|
|
1007
|
+
case "int":
|
|
1008
|
+
return typeof value === "bigint" || typeof value === "number";
|
|
1009
|
+
case "boolean":
|
|
1010
|
+
return typeof value === "boolean";
|
|
1011
|
+
case "text":
|
|
1012
|
+
case "principal":
|
|
1013
|
+
case "timestamp":
|
|
1014
|
+
return typeof value === "string" || typeof value === "number" || typeof value === "bigint";
|
|
1015
|
+
case "jsonb":
|
|
1016
|
+
return value !== undefined;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
if (typeof field !== "object" || field === null)
|
|
1020
|
+
return false;
|
|
1021
|
+
if ("optional" in field) {
|
|
1022
|
+
return value === undefined || value === null || matchesField(field.type, value);
|
|
1023
|
+
}
|
|
1024
|
+
if ("tuple" in field) {
|
|
1025
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
1026
|
+
return false;
|
|
1027
|
+
}
|
|
1028
|
+
return matchesTopic(field.tuple, value).ok;
|
|
1029
|
+
}
|
|
1030
|
+
if ("list" in field) {
|
|
1031
|
+
return Array.isArray(value) && value.every((item) => matchesField(field.list, item));
|
|
1032
|
+
}
|
|
1033
|
+
return false;
|
|
1034
|
+
}
|
|
1035
|
+
function matchesTopic(declared, data) {
|
|
1036
|
+
for (const [name, field] of Object.entries(declared)) {
|
|
1037
|
+
const present = name in data;
|
|
1038
|
+
const optional = typeof field === "object" && field !== null && "optional" in field;
|
|
1039
|
+
if (!present) {
|
|
1040
|
+
if (optional)
|
|
1041
|
+
continue;
|
|
1042
|
+
return { ok: false, reason: `missing declared field "${name}"` };
|
|
1043
|
+
}
|
|
1044
|
+
if (!matchesField(field, data[name])) {
|
|
1045
|
+
return {
|
|
1046
|
+
ok: false,
|
|
1047
|
+
reason: `field "${name}" does not match its declared type (got ${describe(data[name])})`
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
return { ok: true };
|
|
1052
|
+
}
|
|
1053
|
+
function describe(value) {
|
|
1054
|
+
if (value === null)
|
|
1055
|
+
return "null";
|
|
1056
|
+
if (Array.isArray(value))
|
|
1057
|
+
return "array";
|
|
1058
|
+
return typeof value;
|
|
1059
|
+
}
|
|
1060
|
+
function validatePrintPayload(prints, topic, data) {
|
|
1061
|
+
if (!prints)
|
|
1062
|
+
return { ok: true };
|
|
1063
|
+
const declared = prints[topic];
|
|
1064
|
+
if (!declared)
|
|
1065
|
+
return { ok: true };
|
|
1066
|
+
return matchesTopic(declared, data);
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
// src/runtime/runner.ts
|
|
840
1070
|
var DEFAULT_ERROR_THRESHOLD = 50;
|
|
841
1071
|
function camelCase(str) {
|
|
842
1072
|
return str.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
@@ -1081,6 +1311,7 @@ function buildEventPayload(filter, tx, event) {
|
|
|
1081
1311
|
async function runHandlers(subgraph, matched, ctx, opts) {
|
|
1082
1312
|
let processed = 0;
|
|
1083
1313
|
let errors = 0;
|
|
1314
|
+
let skipped = 0;
|
|
1084
1315
|
const threshold = opts?.errorThreshold ?? DEFAULT_ERROR_THRESHOLD;
|
|
1085
1316
|
const filterLookup = new Map;
|
|
1086
1317
|
if (!Array.isArray(subgraph.sources)) {
|
|
@@ -1105,7 +1336,7 @@ async function runHandlers(subgraph, matched, ctx, opts) {
|
|
|
1105
1336
|
errors,
|
|
1106
1337
|
threshold
|
|
1107
1338
|
});
|
|
1108
|
-
return { processed, errors };
|
|
1339
|
+
return { processed, errors, ...skipped > 0 ? { skipped } : {} };
|
|
1109
1340
|
}
|
|
1110
1341
|
const handler = subgraph.handlers[sourceName] ?? subgraph.handlers["*"] ?? null;
|
|
1111
1342
|
if (!handler) {
|
|
@@ -1161,6 +1392,21 @@ async function runHandlers(subgraph, matched, ctx, opts) {
|
|
|
1161
1392
|
if (event !== null && filter?.type === "print_event" && filter.topic && payload.topic !== filter.topic) {
|
|
1162
1393
|
continue;
|
|
1163
1394
|
}
|
|
1395
|
+
if (event !== null && filter?.type === "print_event" && filter.prints) {
|
|
1396
|
+
const printPayload = payload;
|
|
1397
|
+
const verdict = validatePrintPayload(filter.prints, printPayload.topic ?? "", printPayload.data ?? {});
|
|
1398
|
+
if (!verdict.ok) {
|
|
1399
|
+
skipped++;
|
|
1400
|
+
logger2.warn("Print payload does not match declared prints schema", {
|
|
1401
|
+
subgraph: subgraph.name,
|
|
1402
|
+
sourceName,
|
|
1403
|
+
topic: printPayload.topic,
|
|
1404
|
+
txId: tx.tx_id,
|
|
1405
|
+
reason: verdict.reason
|
|
1406
|
+
});
|
|
1407
|
+
continue;
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1164
1410
|
await handler(payload, ctx);
|
|
1165
1411
|
processed++;
|
|
1166
1412
|
} catch (err) {
|
|
@@ -1175,11 +1421,14 @@ async function runHandlers(subgraph, matched, ctx, opts) {
|
|
|
1175
1421
|
});
|
|
1176
1422
|
}
|
|
1177
1423
|
}
|
|
1178
|
-
return { processed, errors };
|
|
1424
|
+
return { processed, errors, ...skipped > 0 ? { skipped } : {} };
|
|
1179
1425
|
}
|
|
1180
1426
|
|
|
1181
1427
|
// src/runtime/source-matcher.ts
|
|
1182
1428
|
var patternCache = new Map;
|
|
1429
|
+
function matchAny(value, pattern) {
|
|
1430
|
+
return Array.isArray(pattern) ? pattern.some((p) => matchPattern(value, p)) : matchPattern(value, pattern);
|
|
1431
|
+
}
|
|
1183
1432
|
function matchPattern(value, pattern) {
|
|
1184
1433
|
if (!pattern.includes("*"))
|
|
1185
1434
|
return value === pattern;
|
|
@@ -1192,6 +1441,22 @@ function matchPattern(value, pattern) {
|
|
|
1192
1441
|
return re.test(value);
|
|
1193
1442
|
}
|
|
1194
1443
|
var EMPTY_SET = new Set;
|
|
1444
|
+
function factoryAllows(filter, contractId, factoryContracts) {
|
|
1445
|
+
if (!filter.factory)
|
|
1446
|
+
return true;
|
|
1447
|
+
if (!contractId)
|
|
1448
|
+
return false;
|
|
1449
|
+
return (factoryContracts.get(filter.factory.from) ?? EMPTY_SET).has(contractId);
|
|
1450
|
+
}
|
|
1451
|
+
function readPath(payload, path) {
|
|
1452
|
+
let cur = payload;
|
|
1453
|
+
for (const key of path.split(".")) {
|
|
1454
|
+
if (cur === null || typeof cur !== "object")
|
|
1455
|
+
return;
|
|
1456
|
+
cur = cur[key];
|
|
1457
|
+
}
|
|
1458
|
+
return cur;
|
|
1459
|
+
}
|
|
1195
1460
|
function traitAllows(filter, contractId, traitContracts) {
|
|
1196
1461
|
const trait = filter.trait;
|
|
1197
1462
|
if (!trait)
|
|
@@ -1203,7 +1468,7 @@ function traitAllows(filter, contractId, traitContracts) {
|
|
|
1203
1468
|
function assetContract(assetId) {
|
|
1204
1469
|
return assetId?.split("::")[0];
|
|
1205
1470
|
}
|
|
1206
|
-
function matchFilter(filter, transactions, eventsByTx, traitContracts) {
|
|
1471
|
+
function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts = new Map) {
|
|
1207
1472
|
const results = [];
|
|
1208
1473
|
switch (filter.type) {
|
|
1209
1474
|
case "stx_transfer":
|
|
@@ -1330,9 +1595,11 @@ function matchFilter(filter, transactions, eventsByTx, traitContracts) {
|
|
|
1330
1595
|
if (tx.type !== "contract_call")
|
|
1331
1596
|
continue;
|
|
1332
1597
|
if (filter.contractId) {
|
|
1333
|
-
if (!tx.contract_id || !
|
|
1598
|
+
if (!tx.contract_id || !matchAny(tx.contract_id, filter.contractId))
|
|
1334
1599
|
continue;
|
|
1335
1600
|
}
|
|
1601
|
+
if (!factoryAllows(filter, tx.contract_id, factoryContracts))
|
|
1602
|
+
continue;
|
|
1336
1603
|
if (filter.functionName) {
|
|
1337
1604
|
if (!tx.function_name || !matchPattern(tx.function_name, filter.functionName))
|
|
1338
1605
|
continue;
|
|
@@ -1379,11 +1646,13 @@ function matchFilter(filter, transactions, eventsByTx, traitContracts) {
|
|
|
1379
1646
|
return false;
|
|
1380
1647
|
const printContractId = data.contract_identifier ?? data.contract_id;
|
|
1381
1648
|
if (filter.contractId) {
|
|
1382
|
-
if (!printContractId || !
|
|
1649
|
+
if (!printContractId || !matchAny(printContractId, filter.contractId))
|
|
1383
1650
|
return false;
|
|
1384
1651
|
}
|
|
1385
1652
|
if (!traitAllows(filter, printContractId, traitContracts))
|
|
1386
1653
|
return false;
|
|
1654
|
+
if (!factoryAllows(filter, printContractId, factoryContracts))
|
|
1655
|
+
return false;
|
|
1387
1656
|
return true;
|
|
1388
1657
|
});
|
|
1389
1658
|
if (matched.length > 0) {
|
|
@@ -1395,7 +1664,7 @@ function matchFilter(filter, transactions, eventsByTx, traitContracts) {
|
|
|
1395
1664
|
}
|
|
1396
1665
|
return results;
|
|
1397
1666
|
}
|
|
1398
|
-
function matchSources(sources, transactions, events, traitContracts = new Map) {
|
|
1667
|
+
function matchSources(sources, transactions, events, traitContracts = new Map, factoryContracts = new Map) {
|
|
1399
1668
|
const eventsByTx = new Map;
|
|
1400
1669
|
for (const event of events) {
|
|
1401
1670
|
const list = eventsByTx.get(event.tx_id) ?? [];
|
|
@@ -1405,7 +1674,7 @@ function matchSources(sources, transactions, events, traitContracts = new Map) {
|
|
|
1405
1674
|
const seen = new Set;
|
|
1406
1675
|
const results = [];
|
|
1407
1676
|
for (const [sourceName, filter] of Object.entries(sources)) {
|
|
1408
|
-
const matches = matchFilter(filter, transactions, eventsByTx, traitContracts);
|
|
1677
|
+
const matches = matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts);
|
|
1409
1678
|
for (const match of matches) {
|
|
1410
1679
|
const dedupeKey = `${match.tx.tx_id}:${sourceName}`;
|
|
1411
1680
|
if (!seen.has(dedupeKey)) {
|
|
@@ -2060,6 +2329,57 @@ async function resolveRoute(subgraphName, targetDb) {
|
|
|
2060
2329
|
function invalidateSubgraphRoute(subgraphName) {
|
|
2061
2330
|
routeCache.delete(subgraphName);
|
|
2062
2331
|
}
|
|
2332
|
+
async function resolveFactoryContracts(subgraph, blockHeight, schemaName, db, txs, evts) {
|
|
2333
|
+
const factories = new Map;
|
|
2334
|
+
for (const source of Object.values(subgraph.sources)) {
|
|
2335
|
+
const factory = source.factory;
|
|
2336
|
+
if (factory)
|
|
2337
|
+
factories.set(factory.from, factory);
|
|
2338
|
+
}
|
|
2339
|
+
const resolved = new Map;
|
|
2340
|
+
const discovered = [];
|
|
2341
|
+
if (factories.size === 0)
|
|
2342
|
+
return { resolved, discovered };
|
|
2343
|
+
for (const [discoveringSource, factory] of factories) {
|
|
2344
|
+
const known = new Set;
|
|
2345
|
+
try {
|
|
2346
|
+
const rows = await sql3`
|
|
2347
|
+
SELECT address FROM ${sql3.raw(`"${schemaName}"."_factory_addresses"`)}
|
|
2348
|
+
WHERE source_name = ${discoveringSource} AND block_height <= ${blockHeight}
|
|
2349
|
+
`.execute(db);
|
|
2350
|
+
for (const row of rows.rows)
|
|
2351
|
+
known.add(row.address);
|
|
2352
|
+
} catch {}
|
|
2353
|
+
const discovering = subgraph.sources[factory.from];
|
|
2354
|
+
if (discovering) {
|
|
2355
|
+
const matches = matchSources({ [factory.from]: discovering }, txs, evts, new Map, new Map);
|
|
2356
|
+
for (const match of matches) {
|
|
2357
|
+
for (const event of match.events ?? []) {
|
|
2358
|
+
const payload = buildEventPayload(discovering, match.tx, event);
|
|
2359
|
+
const value = readPath(payload, factory.field);
|
|
2360
|
+
if (typeof value === "string" && value.length > 0 && !known.has(value)) {
|
|
2361
|
+
known.add(value);
|
|
2362
|
+
discovered.push({ sourceName: discoveringSource, address: value });
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
resolved.set(discoveringSource, known);
|
|
2368
|
+
}
|
|
2369
|
+
return { resolved, discovered };
|
|
2370
|
+
}
|
|
2371
|
+
async function persistFactoryDiscoveries(schemaName, db, blockHeight, discovered) {
|
|
2372
|
+
if (discovered.length === 0)
|
|
2373
|
+
return;
|
|
2374
|
+
for (const { sourceName, address } of discovered) {
|
|
2375
|
+
await sql3`
|
|
2376
|
+
INSERT INTO ${sql3.raw(`"${schemaName}"."_factory_addresses"`)}
|
|
2377
|
+
(source_name, address, block_height)
|
|
2378
|
+
VALUES (${sourceName}, ${address}, ${blockHeight})
|
|
2379
|
+
ON CONFLICT (source_name, address) DO NOTHING
|
|
2380
|
+
`.execute(db);
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2063
2383
|
async function resolveTraitContracts(subgraph, blockHeight, db) {
|
|
2064
2384
|
const traits = new Set;
|
|
2065
2385
|
for (const source of Object.values(subgraph.sources)) {
|
|
@@ -2146,8 +2466,11 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2146
2466
|
txs = data.txs;
|
|
2147
2467
|
evts = data.events;
|
|
2148
2468
|
}
|
|
2469
|
+
const route = await resolveRoute(subgraphName, targetDb);
|
|
2470
|
+
const schemaName = route.schemaName;
|
|
2149
2471
|
const traitContracts = await resolveTraitContracts(subgraph, blockHeight, targetDb);
|
|
2150
|
-
const
|
|
2472
|
+
const { resolved: factoryContracts, discovered } = await resolveFactoryContracts(subgraph, blockHeight, schemaName, targetDb, txs, evts);
|
|
2473
|
+
const matched = matchSources(subgraph.sources, txs, evts, traitContracts, factoryContracts);
|
|
2151
2474
|
result.matched = matched.length;
|
|
2152
2475
|
if (matched.length === 0) {
|
|
2153
2476
|
if (!opts?.skipProgressUpdate) {
|
|
@@ -2155,8 +2478,6 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2155
2478
|
}
|
|
2156
2479
|
return result;
|
|
2157
2480
|
}
|
|
2158
|
-
const route = await resolveRoute(subgraphName, targetDb);
|
|
2159
|
-
const schemaName = route.schemaName;
|
|
2160
2481
|
const blockMeta = {
|
|
2161
2482
|
height: block.height,
|
|
2162
2483
|
hash: block.hash,
|
|
@@ -2214,6 +2535,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2214
2535
|
});
|
|
2215
2536
|
result.processed = runResult.processed;
|
|
2216
2537
|
result.errors = runResult.errors;
|
|
2538
|
+
await persistFactoryDiscoveries(schemaName, route.dataDb, blockHeight, discovered);
|
|
2217
2539
|
await targetDb.transaction().execute(async (tx) => {
|
|
2218
2540
|
if (manifest && manifest.count > 0) {
|
|
2219
2541
|
await emitSubscriptionOutbox(tx, subgraphName, manifest, matcher, block.height);
|
|
@@ -2933,6 +3255,9 @@ async function backfillSubgraph(def, opts) {
|
|
|
2933
3255
|
function defineSubgraph(def) {
|
|
2934
3256
|
return def;
|
|
2935
3257
|
}
|
|
3258
|
+
function defineSchema(schema) {
|
|
3259
|
+
return schema;
|
|
3260
|
+
}
|
|
2936
3261
|
// src/print-schema.ts
|
|
2937
3262
|
import {
|
|
2938
3263
|
deserializeCV as deserializeCV3
|
|
@@ -3852,8 +4177,8 @@ function diffKeyLists(existing, incoming) {
|
|
|
3852
4177
|
const existingSet = new Set((existing ?? []).map(key2));
|
|
3853
4178
|
const incomingSet = new Set((incoming ?? []).map(key2));
|
|
3854
4179
|
return {
|
|
3855
|
-
added: (incoming ?? []).filter((cols) => !existingSet.has(key2(cols))),
|
|
3856
|
-
removed: (existing ?? []).filter((cols) => !incomingSet.has(key2(cols)))
|
|
4180
|
+
added: (incoming ?? []).filter((cols) => !existingSet.has(key2(cols))).map((cols) => [...cols]),
|
|
4181
|
+
removed: (existing ?? []).filter((cols) => !incomingSet.has(key2(cols))).map((cols) => [...cols])
|
|
3857
4182
|
};
|
|
3858
4183
|
}
|
|
3859
4184
|
function diffSchema(existing, incoming) {
|
|
@@ -4197,6 +4522,7 @@ function getDefault(type) {
|
|
|
4197
4522
|
}
|
|
4198
4523
|
export {
|
|
4199
4524
|
validateSubgraphDefinition,
|
|
4525
|
+
validatePrintPayload,
|
|
4200
4526
|
sparseProbeTargets,
|
|
4201
4527
|
resumeReindex,
|
|
4202
4528
|
renderDeployPlan,
|
|
@@ -4209,9 +4535,11 @@ export {
|
|
|
4209
4535
|
generateKyselySchema,
|
|
4210
4536
|
generateIndexSchema,
|
|
4211
4537
|
generateDrizzleSchema,
|
|
4538
|
+
filterFieldsByType,
|
|
4212
4539
|
diffSchema,
|
|
4213
4540
|
deploySchema,
|
|
4214
4541
|
defineSubgraph,
|
|
4542
|
+
defineSchema,
|
|
4215
4543
|
canSparseScan,
|
|
4216
4544
|
camelizeDataKey,
|
|
4217
4545
|
backfillSubgraph,
|
|
@@ -4219,5 +4547,5 @@ export {
|
|
|
4219
4547
|
ByoBreakingChangeError
|
|
4220
4548
|
};
|
|
4221
4549
|
|
|
4222
|
-
//# debugId=
|
|
4550
|
+
//# debugId=8BECEC189376FB2B64756E2164756E21
|
|
4223
4551
|
//# sourceMappingURL=index.js.map
|