@secondlayer/subgraphs 3.21.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 +121 -9
- package/dist/src/index.js +231 -39
- package/dist/src/index.js.map +9 -9
- package/dist/src/runtime/block-processor.d.ts +68 -8
- package/dist/src/runtime/block-processor.js +109 -20
- package/dist/src/runtime/block-processor.js.map +6 -6
- package/dist/src/runtime/catchup.d.ts +68 -8
- package/dist/src/runtime/catchup.js +109 -20
- package/dist/src/runtime/catchup.js.map +6 -6
- 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 +114 -20
- package/dist/src/runtime/processor.js.map +7 -7
- package/dist/src/runtime/reindex.d.ts +68 -8
- package/dist/src/runtime/reindex.js +109 -20
- package/dist/src/runtime/reindex.js.map +6 -6
- package/dist/src/runtime/reorg.d.ts +68 -8
- package/dist/src/runtime/reorg.js +114 -20
- package/dist/src/runtime/reorg.js.map +7 -7
- package/dist/src/runtime/replay.js +29 -6
- package/dist/src/runtime/replay.js.map +3 -3
- package/dist/src/runtime/runner.d.ts +89 -12
- package/dist/src/runtime/source-matcher.d.ts +66 -6
- 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 +68 -8
- package/dist/src/schema/index.js +133 -20
- package/dist/src/schema/index.js.map +5 -5
- package/dist/src/service.js +114 -20
- package/dist/src/service.js.map +7 -7
- 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 +55 -4
- package/dist/src/types.d.ts +69 -9
- package/dist/src/validate.d.ts +86 -9
- package/dist/src/validate.js +117 -18
- package/dist/src/validate.js.map +3 -3
- package/package.json +6 -2
|
@@ -100,6 +100,18 @@ function emitJournalDDL(schemaName) {
|
|
|
100
100
|
`CREATE INDEX IF NOT EXISTS idx_${schemaName}_journal_height ON ${schemaName}._journal (block_height)`
|
|
101
101
|
];
|
|
102
102
|
}
|
|
103
|
+
function emitFactoryDDL(schemaName) {
|
|
104
|
+
return [
|
|
105
|
+
`CREATE TABLE IF NOT EXISTS ${schemaName}._factory_addresses (
|
|
106
|
+
source_name TEXT NOT NULL,
|
|
107
|
+
address TEXT NOT NULL,
|
|
108
|
+
block_height BIGINT NOT NULL,
|
|
109
|
+
_created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
110
|
+
PRIMARY KEY (source_name, address)
|
|
111
|
+
)`,
|
|
112
|
+
`CREATE INDEX IF NOT EXISTS idx_${schemaName}_factory_height ON ${schemaName}._factory_addresses (block_height)`
|
|
113
|
+
];
|
|
114
|
+
}
|
|
103
115
|
function emitForeignKeyDDL(schemaName, tableName, tableDef) {
|
|
104
116
|
return (tableDef.relations ?? []).map((rel) => {
|
|
105
117
|
const constraintName = `fk_${schemaName}_${tableName}_${rel.name}`;
|
|
@@ -118,6 +130,9 @@ function generateSubgraphSQL(def, schemaNameOverride) {
|
|
|
118
130
|
statements.push(...emitTableDDL(schemaName, tableName, tableDef));
|
|
119
131
|
}
|
|
120
132
|
statements.push(...emitJournalDDL(schemaName));
|
|
133
|
+
if (Object.values(def.sources ?? {}).some((src) => src.factory !== undefined)) {
|
|
134
|
+
statements.push(...emitFactoryDDL(schemaName));
|
|
135
|
+
}
|
|
121
136
|
for (const [tableName, tableDef] of Object.entries(def.schema)) {
|
|
122
137
|
statements.push(...emitForeignKeyDDL(schemaName, tableName, tableDef));
|
|
123
138
|
}
|
|
@@ -262,23 +277,21 @@ class SubgraphContext {
|
|
|
262
277
|
formatUnits(value, decimals) {
|
|
263
278
|
return formatUnits(value, decimals);
|
|
264
279
|
}
|
|
265
|
-
async
|
|
266
|
-
this.validateTable(table);
|
|
280
|
+
async readRows(table, where, limit) {
|
|
267
281
|
const qualifiedTable = `"${this.pgSchemaName}"."${table}"`;
|
|
268
282
|
const { clause } = buildWhereClause(where);
|
|
269
|
-
const query = `SELECT * FROM ${qualifiedTable} WHERE ${clause} LIMIT
|
|
283
|
+
const query = `SELECT * FROM ${qualifiedTable} WHERE ${clause}${limit !== undefined ? ` LIMIT ${limit}` : ""}`;
|
|
270
284
|
const { rows } = await sql.raw(query).execute(this.db);
|
|
271
|
-
|
|
272
|
-
|
|
285
|
+
return rows.map((r) => this.coerceRow(table, r));
|
|
286
|
+
}
|
|
287
|
+
async findOne(table, where) {
|
|
288
|
+
this.validateTable(table);
|
|
289
|
+
const row = (await this.readRows(table, where, 1))[0] ?? null;
|
|
290
|
+
return this.overlayOne(table, where, row);
|
|
273
291
|
}
|
|
274
292
|
async findMany(table, where) {
|
|
275
293
|
this.validateTable(table);
|
|
276
|
-
|
|
277
|
-
const { clause } = buildWhereClause(where);
|
|
278
|
-
const query = `SELECT * FROM ${qualifiedTable} WHERE ${clause}`;
|
|
279
|
-
const { rows } = await sql.raw(query).execute(this.db);
|
|
280
|
-
const dbRows = rows.map((r) => this.coerceRow(table, r));
|
|
281
|
-
return this.overlayMany(table, where, dbRows);
|
|
294
|
+
return this.overlayMany(table, where, await this.readRows(table, where));
|
|
282
295
|
}
|
|
283
296
|
overlayOne(table, where, dbRow) {
|
|
284
297
|
if (this.ops.length === 0)
|
|
@@ -1198,6 +1211,9 @@ async function runHandlers(subgraph, matched, ctx, opts) {
|
|
|
1198
1211
|
|
|
1199
1212
|
// src/runtime/source-matcher.ts
|
|
1200
1213
|
var patternCache = new Map;
|
|
1214
|
+
function matchAny(value, pattern) {
|
|
1215
|
+
return Array.isArray(pattern) ? pattern.some((p) => matchPattern(value, p)) : matchPattern(value, pattern);
|
|
1216
|
+
}
|
|
1201
1217
|
function matchPattern(value, pattern) {
|
|
1202
1218
|
if (!pattern.includes("*"))
|
|
1203
1219
|
return value === pattern;
|
|
@@ -1210,6 +1226,22 @@ function matchPattern(value, pattern) {
|
|
|
1210
1226
|
return re.test(value);
|
|
1211
1227
|
}
|
|
1212
1228
|
var EMPTY_SET = new Set;
|
|
1229
|
+
function factoryAllows(filter, contractId, factoryContracts) {
|
|
1230
|
+
if (!filter.factory)
|
|
1231
|
+
return true;
|
|
1232
|
+
if (!contractId)
|
|
1233
|
+
return false;
|
|
1234
|
+
return (factoryContracts.get(filter.factory.from) ?? EMPTY_SET).has(contractId);
|
|
1235
|
+
}
|
|
1236
|
+
function readPath(payload, path) {
|
|
1237
|
+
let cur = payload;
|
|
1238
|
+
for (const key of path.split(".")) {
|
|
1239
|
+
if (cur === null || typeof cur !== "object")
|
|
1240
|
+
return;
|
|
1241
|
+
cur = cur[key];
|
|
1242
|
+
}
|
|
1243
|
+
return cur;
|
|
1244
|
+
}
|
|
1213
1245
|
function traitAllows(filter, contractId, traitContracts) {
|
|
1214
1246
|
const trait = filter.trait;
|
|
1215
1247
|
if (!trait)
|
|
@@ -1221,7 +1253,7 @@ function traitAllows(filter, contractId, traitContracts) {
|
|
|
1221
1253
|
function assetContract(assetId) {
|
|
1222
1254
|
return assetId?.split("::")[0];
|
|
1223
1255
|
}
|
|
1224
|
-
function matchFilter(filter, transactions, eventsByTx, traitContracts) {
|
|
1256
|
+
function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts = new Map) {
|
|
1225
1257
|
const results = [];
|
|
1226
1258
|
switch (filter.type) {
|
|
1227
1259
|
case "stx_transfer":
|
|
@@ -1348,9 +1380,11 @@ function matchFilter(filter, transactions, eventsByTx, traitContracts) {
|
|
|
1348
1380
|
if (tx.type !== "contract_call")
|
|
1349
1381
|
continue;
|
|
1350
1382
|
if (filter.contractId) {
|
|
1351
|
-
if (!tx.contract_id || !
|
|
1383
|
+
if (!tx.contract_id || !matchAny(tx.contract_id, filter.contractId))
|
|
1352
1384
|
continue;
|
|
1353
1385
|
}
|
|
1386
|
+
if (!factoryAllows(filter, tx.contract_id, factoryContracts))
|
|
1387
|
+
continue;
|
|
1354
1388
|
if (filter.functionName) {
|
|
1355
1389
|
if (!tx.function_name || !matchPattern(tx.function_name, filter.functionName))
|
|
1356
1390
|
continue;
|
|
@@ -1397,11 +1431,13 @@ function matchFilter(filter, transactions, eventsByTx, traitContracts) {
|
|
|
1397
1431
|
return false;
|
|
1398
1432
|
const printContractId = data.contract_identifier ?? data.contract_id;
|
|
1399
1433
|
if (filter.contractId) {
|
|
1400
|
-
if (!printContractId || !
|
|
1434
|
+
if (!printContractId || !matchAny(printContractId, filter.contractId))
|
|
1401
1435
|
return false;
|
|
1402
1436
|
}
|
|
1403
1437
|
if (!traitAllows(filter, printContractId, traitContracts))
|
|
1404
1438
|
return false;
|
|
1439
|
+
if (!factoryAllows(filter, printContractId, factoryContracts))
|
|
1440
|
+
return false;
|
|
1405
1441
|
return true;
|
|
1406
1442
|
});
|
|
1407
1443
|
if (matched.length > 0) {
|
|
@@ -1413,7 +1449,7 @@ function matchFilter(filter, transactions, eventsByTx, traitContracts) {
|
|
|
1413
1449
|
}
|
|
1414
1450
|
return results;
|
|
1415
1451
|
}
|
|
1416
|
-
function matchSources(sources, transactions, events, traitContracts = new Map) {
|
|
1452
|
+
function matchSources(sources, transactions, events, traitContracts = new Map, factoryContracts = new Map) {
|
|
1417
1453
|
const eventsByTx = new Map;
|
|
1418
1454
|
for (const event of events) {
|
|
1419
1455
|
const list = eventsByTx.get(event.tx_id) ?? [];
|
|
@@ -1423,7 +1459,7 @@ function matchSources(sources, transactions, events, traitContracts = new Map) {
|
|
|
1423
1459
|
const seen = new Set;
|
|
1424
1460
|
const results = [];
|
|
1425
1461
|
for (const [sourceName, filter] of Object.entries(sources)) {
|
|
1426
|
-
const matches = matchFilter(filter, transactions, eventsByTx, traitContracts);
|
|
1462
|
+
const matches = matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts);
|
|
1427
1463
|
for (const match of matches) {
|
|
1428
1464
|
const dedupeKey = `${match.tx.tx_id}:${sourceName}`;
|
|
1429
1465
|
if (!seen.has(dedupeKey)) {
|
|
@@ -2078,6 +2114,57 @@ async function resolveRoute(subgraphName, targetDb) {
|
|
|
2078
2114
|
function invalidateSubgraphRoute(subgraphName) {
|
|
2079
2115
|
routeCache.delete(subgraphName);
|
|
2080
2116
|
}
|
|
2117
|
+
async function resolveFactoryContracts(subgraph, blockHeight, schemaName, db, txs, evts) {
|
|
2118
|
+
const factories = new Map;
|
|
2119
|
+
for (const source of Object.values(subgraph.sources)) {
|
|
2120
|
+
const factory = source.factory;
|
|
2121
|
+
if (factory)
|
|
2122
|
+
factories.set(factory.from, factory);
|
|
2123
|
+
}
|
|
2124
|
+
const resolved = new Map;
|
|
2125
|
+
const discovered = [];
|
|
2126
|
+
if (factories.size === 0)
|
|
2127
|
+
return { resolved, discovered };
|
|
2128
|
+
for (const [discoveringSource, factory] of factories) {
|
|
2129
|
+
const known = new Set;
|
|
2130
|
+
try {
|
|
2131
|
+
const rows = await sql3`
|
|
2132
|
+
SELECT address FROM ${sql3.raw(`"${schemaName}"."_factory_addresses"`)}
|
|
2133
|
+
WHERE source_name = ${discoveringSource} AND block_height <= ${blockHeight}
|
|
2134
|
+
`.execute(db);
|
|
2135
|
+
for (const row of rows.rows)
|
|
2136
|
+
known.add(row.address);
|
|
2137
|
+
} catch {}
|
|
2138
|
+
const discovering = subgraph.sources[factory.from];
|
|
2139
|
+
if (discovering) {
|
|
2140
|
+
const matches = matchSources({ [factory.from]: discovering }, txs, evts, new Map, new Map);
|
|
2141
|
+
for (const match of matches) {
|
|
2142
|
+
for (const event of match.events ?? []) {
|
|
2143
|
+
const payload = buildEventPayload(discovering, match.tx, event);
|
|
2144
|
+
const value = readPath(payload, factory.field);
|
|
2145
|
+
if (typeof value === "string" && value.length > 0 && !known.has(value)) {
|
|
2146
|
+
known.add(value);
|
|
2147
|
+
discovered.push({ sourceName: discoveringSource, address: value });
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
resolved.set(discoveringSource, known);
|
|
2153
|
+
}
|
|
2154
|
+
return { resolved, discovered };
|
|
2155
|
+
}
|
|
2156
|
+
async function persistFactoryDiscoveries(schemaName, db, blockHeight, discovered) {
|
|
2157
|
+
if (discovered.length === 0)
|
|
2158
|
+
return;
|
|
2159
|
+
for (const { sourceName, address } of discovered) {
|
|
2160
|
+
await sql3`
|
|
2161
|
+
INSERT INTO ${sql3.raw(`"${schemaName}"."_factory_addresses"`)}
|
|
2162
|
+
(source_name, address, block_height)
|
|
2163
|
+
VALUES (${sourceName}, ${address}, ${blockHeight})
|
|
2164
|
+
ON CONFLICT (source_name, address) DO NOTHING
|
|
2165
|
+
`.execute(db);
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2081
2168
|
async function resolveTraitContracts(subgraph, blockHeight, db) {
|
|
2082
2169
|
const traits = new Set;
|
|
2083
2170
|
for (const source of Object.values(subgraph.sources)) {
|
|
@@ -2164,8 +2251,11 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2164
2251
|
txs = data.txs;
|
|
2165
2252
|
evts = data.events;
|
|
2166
2253
|
}
|
|
2254
|
+
const route = await resolveRoute(subgraphName, targetDb);
|
|
2255
|
+
const schemaName = route.schemaName;
|
|
2167
2256
|
const traitContracts = await resolveTraitContracts(subgraph, blockHeight, targetDb);
|
|
2168
|
-
const
|
|
2257
|
+
const { resolved: factoryContracts, discovered } = await resolveFactoryContracts(subgraph, blockHeight, schemaName, targetDb, txs, evts);
|
|
2258
|
+
const matched = matchSources(subgraph.sources, txs, evts, traitContracts, factoryContracts);
|
|
2169
2259
|
result.matched = matched.length;
|
|
2170
2260
|
if (matched.length === 0) {
|
|
2171
2261
|
if (!opts?.skipProgressUpdate) {
|
|
@@ -2173,8 +2263,6 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2173
2263
|
}
|
|
2174
2264
|
return result;
|
|
2175
2265
|
}
|
|
2176
|
-
const route = await resolveRoute(subgraphName, targetDb);
|
|
2177
|
-
const schemaName = route.schemaName;
|
|
2178
2266
|
const blockMeta = {
|
|
2179
2267
|
height: block.height,
|
|
2180
2268
|
hash: block.hash,
|
|
@@ -2232,6 +2320,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2232
2320
|
});
|
|
2233
2321
|
result.processed = runResult.processed;
|
|
2234
2322
|
result.errors = runResult.errors;
|
|
2323
|
+
await persistFactoryDiscoveries(schemaName, route.dataDb, blockHeight, discovered);
|
|
2235
2324
|
await targetDb.transaction().execute(async (tx) => {
|
|
2236
2325
|
if (manifest && manifest.count > 0) {
|
|
2237
2326
|
await emitSubscriptionOutbox(tx, subgraphName, manifest, matcher, block.height);
|
|
@@ -3230,6 +3319,11 @@ async function handleSubgraphReorg(blockHeight, loadSubgraphDef) {
|
|
|
3230
3319
|
await client.unsafe(`DELETE FROM "${schemaName}"."${tableName}" WHERE "_block_height" >= $1`, [blockHeight]);
|
|
3231
3320
|
}
|
|
3232
3321
|
}
|
|
3322
|
+
await client.unsafe(`DO $$ BEGIN
|
|
3323
|
+
IF to_regclass('"${schemaName}"."_factory_addresses"') IS NOT NULL THEN
|
|
3324
|
+
DELETE FROM "${schemaName}"."_factory_addresses" WHERE block_height >= ${Number(blockHeight)};
|
|
3325
|
+
END IF;
|
|
3326
|
+
END $$`);
|
|
3233
3327
|
for (const [tableName, rows] of Object.entries(revertedByTable)) {
|
|
3234
3328
|
if (rows.length === 0)
|
|
3235
3329
|
continue;
|
|
@@ -3768,5 +3862,5 @@ export {
|
|
|
3768
3862
|
startSubgraphOperationRunner
|
|
3769
3863
|
};
|
|
3770
3864
|
|
|
3771
|
-
//# debugId=
|
|
3865
|
+
//# debugId=88213910F34356E564756E2164756E21
|
|
3772
3866
|
//# sourceMappingURL=processor.js.map
|