@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.
Files changed (40) hide show
  1. package/dist/src/index.d.ts +121 -9
  2. package/dist/src/index.js +231 -39
  3. package/dist/src/index.js.map +9 -9
  4. package/dist/src/runtime/block-processor.d.ts +68 -8
  5. package/dist/src/runtime/block-processor.js +109 -20
  6. package/dist/src/runtime/block-processor.js.map +6 -6
  7. package/dist/src/runtime/catchup.d.ts +68 -8
  8. package/dist/src/runtime/catchup.js +109 -20
  9. package/dist/src/runtime/catchup.js.map +6 -6
  10. package/dist/src/runtime/context.d.ts +34 -8
  11. package/dist/src/runtime/context.js +25 -12
  12. package/dist/src/runtime/context.js.map +4 -4
  13. package/dist/src/runtime/processor.js +114 -20
  14. package/dist/src/runtime/processor.js.map +7 -7
  15. package/dist/src/runtime/reindex.d.ts +68 -8
  16. package/dist/src/runtime/reindex.js +109 -20
  17. package/dist/src/runtime/reindex.js.map +6 -6
  18. package/dist/src/runtime/reorg.d.ts +68 -8
  19. package/dist/src/runtime/reorg.js +114 -20
  20. package/dist/src/runtime/reorg.js.map +7 -7
  21. package/dist/src/runtime/replay.js +29 -6
  22. package/dist/src/runtime/replay.js.map +3 -3
  23. package/dist/src/runtime/runner.d.ts +89 -12
  24. package/dist/src/runtime/source-matcher.d.ts +66 -6
  25. package/dist/src/runtime/source-matcher.js +30 -6
  26. package/dist/src/runtime/source-matcher.js.map +3 -3
  27. package/dist/src/schema/index.d.ts +68 -8
  28. package/dist/src/schema/index.js +133 -20
  29. package/dist/src/schema/index.js.map +5 -5
  30. package/dist/src/service.js +114 -20
  31. package/dist/src/service.js.map +7 -7
  32. package/dist/src/testing/index.d.ts +620 -0
  33. package/dist/src/testing/index.js +1297 -0
  34. package/dist/src/testing/index.js.map +16 -0
  35. package/dist/src/triggers/index.d.ts +55 -4
  36. package/dist/src/types.d.ts +69 -9
  37. package/dist/src/validate.d.ts +86 -9
  38. package/dist/src/validate.js +117 -18
  39. package/dist/src/validate.js.map +3 -3
  40. 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 findOne(table, where) {
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 1`;
283
+ const query = `SELECT * FROM ${qualifiedTable} WHERE ${clause}${limit !== undefined ? ` LIMIT ${limit}` : ""}`;
270
284
  const { rows } = await sql.raw(query).execute(this.db);
271
- const row = rows[0] ?? null;
272
- return this.overlayOne(table, where, row ? this.coerceRow(table, row) : null);
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
- const qualifiedTable = `"${this.pgSchemaName}"."${table}"`;
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 || !matchPattern(tx.contract_id, filter.contractId))
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 || !matchPattern(printContractId, filter.contractId))
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 matched = matchSources(subgraph.sources, txs, evts, traitContracts);
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=0DCB2AA2FDAD1E6864756E2164756E21
3865
+ //# debugId=88213910F34356E564756E2164756E21
3772
3866
  //# sourceMappingURL=processor.js.map