@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
package/dist/src/index.d.ts
CHANGED
|
@@ -25,15 +25,24 @@ interface SubgraphRelation {
|
|
|
25
25
|
/** Target column(s) the fields point at (a uniqueKeys entry on the target). */
|
|
26
26
|
referencedColumns: string[];
|
|
27
27
|
}
|
|
28
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Table definition within a subgraph schema.
|
|
30
|
+
*
|
|
31
|
+
* The array fields are `readonly` so a schema literal can be hoisted out of
|
|
32
|
+
* the `defineSubgraph()` call with `as const` (or {@link defineSchema}) and
|
|
33
|
+
* still typecheck — `as const` produces `readonly [readonly ["holder"]]`,
|
|
34
|
+
* which a mutable `string[][]` rejects. Without that, extracting a helper
|
|
35
|
+
* that takes `ctx` meant losing the entire typed surface, which is why the
|
|
36
|
+
* shipped starter reached for `ctx: any`.
|
|
37
|
+
*/
|
|
29
38
|
interface SubgraphTable {
|
|
30
39
|
columns: Record<string, SubgraphColumn>;
|
|
31
40
|
/** Composite indexes (each entry is an array of column names) */
|
|
32
|
-
indexes?: string[][];
|
|
41
|
+
indexes?: readonly (readonly string[])[];
|
|
33
42
|
/** Unique key constraints (each entry is an array of column names). Required for upsert. */
|
|
34
|
-
uniqueKeys?: string[][];
|
|
43
|
+
uniqueKeys?: readonly (readonly string[])[];
|
|
35
44
|
/** Foreign-key relations to other tables (for typed ORM joins). */
|
|
36
|
-
relations?: SubgraphRelation[];
|
|
45
|
+
relations?: readonly SubgraphRelation[];
|
|
37
46
|
}
|
|
38
47
|
/** Subgraph schema — maps table names to table definitions */
|
|
39
48
|
type SubgraphSchema = Record<string, SubgraphTable>;
|
|
@@ -68,6 +77,45 @@ interface StxLockFilter {
|
|
|
68
77
|
type TraitScope = {
|
|
69
78
|
trait?: string
|
|
70
79
|
};
|
|
80
|
+
/**
|
|
81
|
+
* Scope a source to a DYNAMIC address set discovered from another source's
|
|
82
|
+
* events, instead of a fixed contract list.
|
|
83
|
+
*
|
|
84
|
+
* The standard shape it unlocks: a router plus pools created after you
|
|
85
|
+
* deploy. `contractId` arrays cover a set you know at authoring time; a
|
|
86
|
+
* factory covers the set that grows. It is deliberately NOT tied to contract
|
|
87
|
+
* deployment — the address can come from any field of any event, so it also
|
|
88
|
+
* covers launchpad-minted tokens, DAO extension allowlists, and registry
|
|
89
|
+
* entries.
|
|
90
|
+
*
|
|
91
|
+
* ```ts
|
|
92
|
+
* sources: {
|
|
93
|
+
* registry: { type: "print_event", contractId: REGISTRY, topic: "pool-created" },
|
|
94
|
+
* swaps: {
|
|
95
|
+
* type: "print_event",
|
|
96
|
+
* topic: "swap",
|
|
97
|
+
* // every pool the registry has ever announced, including new ones
|
|
98
|
+
* factory: { from: "registry", field: "data.pool" },
|
|
99
|
+
* },
|
|
100
|
+
* }
|
|
101
|
+
* ```
|
|
102
|
+
*
|
|
103
|
+
* Two guarantees the runtime provides:
|
|
104
|
+
* - **Same-block ordering** — a contract discovered in block N receives its
|
|
105
|
+
* own block-N events. Discovery runs before matching, within the block.
|
|
106
|
+
* - **Reorg-scoped** — the discovered set is stamped with the block that
|
|
107
|
+
* revealed it and rolled back with everything else on a fork, so an address
|
|
108
|
+
* announced on an orphaned chain does not linger forever.
|
|
109
|
+
*/
|
|
110
|
+
interface FactoryScope {
|
|
111
|
+
factory?: {
|
|
112
|
+
/** Source name whose events reveal the addresses. */
|
|
113
|
+
from: string
|
|
114
|
+
/** Dotted path to the address on that source's payload
|
|
115
|
+
* (e.g. `"data.pool"`, `"contractId"`, `"data.token.id"`). */
|
|
116
|
+
field: string
|
|
117
|
+
};
|
|
118
|
+
}
|
|
71
119
|
/** FT event filters */
|
|
72
120
|
interface FtTransferFilter extends TraitScope {
|
|
73
121
|
type: "ft_transfer";
|
|
@@ -147,9 +195,19 @@ interface ContractCallEvent {
|
|
|
147
195
|
};
|
|
148
196
|
}
|
|
149
197
|
/** Contract event filters */
|
|
150
|
-
interface ContractCallFilter extends TraitScope {
|
|
198
|
+
interface ContractCallFilter extends TraitScope, FactoryScope {
|
|
151
199
|
type: "contract_call";
|
|
152
|
-
|
|
200
|
+
/**
|
|
201
|
+
* One contract id, or a SET of them (max 20) — the standard DeFi shape is
|
|
202
|
+
* a router plus N pools, which used to cost N near-identical sources and
|
|
203
|
+
* handlers, or the `"*"` catch-all that erases per-source typing. Payload
|
|
204
|
+
* types are identical per `type`, so one handler serves the set and
|
|
205
|
+
* `event.contractId` disambiguates.
|
|
206
|
+
*
|
|
207
|
+
* Supports `*` wildcards (e.g. `"SP1ABC….pool-*"`). Mutually exclusive
|
|
208
|
+
* with `trait`, which resolves its own set per block.
|
|
209
|
+
*/
|
|
210
|
+
contractId?: string | readonly string[];
|
|
153
211
|
functionName?: string;
|
|
154
212
|
caller?: string;
|
|
155
213
|
/**
|
|
@@ -187,9 +245,11 @@ type PrintField = ColumnType | {
|
|
|
187
245
|
} | {
|
|
188
246
|
list: PrintField
|
|
189
247
|
};
|
|
190
|
-
interface PrintEventFilter extends TraitScope {
|
|
248
|
+
interface PrintEventFilter extends TraitScope, FactoryScope {
|
|
191
249
|
type: "print_event";
|
|
192
|
-
|
|
250
|
+
/** One contract id, or a set of them (max 20). Supports `*` wildcards.
|
|
251
|
+
* Mutually exclusive with `trait`. */
|
|
252
|
+
contractId?: string | readonly string[];
|
|
193
253
|
topic?: string;
|
|
194
254
|
/**
|
|
195
255
|
* Per-topic field schema. When declared, the handler's `event` is a
|
|
@@ -285,6 +345,16 @@ interface SubgraphDefinition {
|
|
|
285
345
|
* Validates a subgraph definition, returning the parsed result or throwing on failure.
|
|
286
346
|
*/
|
|
287
347
|
declare function validateSubgraphDefinition(def: unknown): SubgraphDefinition;
|
|
348
|
+
/**
|
|
349
|
+
* Per-source-type field vocabulary, DERIVED from
|
|
350
|
+
* {@link SubgraphFilterSchema}. Agent-facing surfaces (the MCP filters
|
|
351
|
+
* reference) used to hand-maintain a parallel copy of this list; deriving it
|
|
352
|
+
* means adding a field to a filter can never leave the documentation behind.
|
|
353
|
+
*/
|
|
354
|
+
declare function filterFieldsByType(): Array<{
|
|
355
|
+
type: string
|
|
356
|
+
fields: string[]
|
|
357
|
+
}>;
|
|
288
358
|
interface ReindexOptions {
|
|
289
359
|
fromBlock?: number;
|
|
290
360
|
toBlock?: number;
|
|
@@ -737,6 +807,48 @@ declare function defineSubgraph<
|
|
|
737
807
|
const Sources extends Record<string, SubgraphFilter>,
|
|
738
808
|
const S extends SubgraphSchema
|
|
739
809
|
>(def: TypedSubgraphDefinition<Sources, S>): TypedSubgraphDefinition<Sources, S>;
|
|
810
|
+
/**
|
|
811
|
+
* Identity helper that preserves a schema literal's exact types so it can be
|
|
812
|
+
* HOISTED out of the `defineSubgraph()` call and reused:
|
|
813
|
+
*
|
|
814
|
+
* ```ts
|
|
815
|
+
* const schema = defineSchema({
|
|
816
|
+
* balances: {
|
|
817
|
+
* columns: { holder: { type: "principal" }, amount: { type: "uint" } },
|
|
818
|
+
* uniqueKeys: [["holder"]],
|
|
819
|
+
* },
|
|
820
|
+
* });
|
|
821
|
+
*
|
|
822
|
+
* // An extracted helper keeps the full typed surface — table names and row
|
|
823
|
+
* // columns are still checked:
|
|
824
|
+
* function credit(ctx: TypedSubgraphContext<typeof schema>, holder: string) {
|
|
825
|
+
* ctx.increment("balances", { holder }, { amount: 1n });
|
|
826
|
+
* }
|
|
827
|
+
*
|
|
828
|
+
* defineSubgraph({ name: "balances", schema, sources, handlers });
|
|
829
|
+
* ```
|
|
830
|
+
*
|
|
831
|
+
* `as const` alone does not work here (it produces `readonly` arrays that a
|
|
832
|
+
* mutable `uniqueKeys` rejected) and a bare literal widens `type` to `string`.
|
|
833
|
+
* That gap is why the shipped starter typed its helper `ctx: any` — and in
|
|
834
|
+
* doing so modelled a non-commutative read-modify-write the docs warn against.
|
|
835
|
+
*/
|
|
836
|
+
declare function defineSchema<const S extends SubgraphSchema>(schema: S): S;
|
|
837
|
+
/**
|
|
838
|
+
* The typed handler context for a subgraph definition — for annotating
|
|
839
|
+
* helpers extracted out of a handler:
|
|
840
|
+
*
|
|
841
|
+
* ```ts
|
|
842
|
+
* const def = defineSubgraph({ … });
|
|
843
|
+
* function credit(ctx: InferContext<typeof def>) { … }
|
|
844
|
+
* ```
|
|
845
|
+
*
|
|
846
|
+
* Prefer `TypedSubgraphContext<typeof schema>` when the schema is hoisted;
|
|
847
|
+
* this is the form for when only the definition is in scope.
|
|
848
|
+
*/
|
|
849
|
+
type InferContext<D> = D extends {
|
|
850
|
+
schema: infer S extends SubgraphSchema
|
|
851
|
+
} ? TypedSubgraphContext<S> : never;
|
|
740
852
|
type PrintValidationResult = {
|
|
741
853
|
ok: true
|
|
742
854
|
} | {
|
|
@@ -988,4 +1100,4 @@ type SparseProbeTarget = {
|
|
|
988
1100
|
declare function canSparseScan(subgraph: SubgraphDefinition): boolean;
|
|
989
1101
|
/** Probe targets for a subgraph's filters: decoded type + contract scope when\\n* the filter pins one (assetIdentifier "SP….contract::asset" or contractId). */
|
|
990
1102
|
declare function sparseProbeTargets(subgraph: SubgraphDefinition): SparseProbeTarget[];
|
|
991
|
-
export { validateSubgraphDefinition, validatePrintPayload, sparseProbeTargets, resumeReindex, renderDeployPlan, reindexSubgraph, pgSchemaName, inferPrintTopics, hasBreakingChanges, generateSubgraphSQL, generatePrismaSchema, generateKyselySchema, generateIndexSchema, generateDrizzleSchema, diffSchema, deploySchema, defineSubgraph, canSparseScan, camelizeDataKey, backfillSubgraph, WriteRow, WhereInput, TypedSubgraphDefinition, TypedSubgraphContext, TypedHandlers, TxMeta, TableDiff, SystemRow, SubscribeOptions, SubgraphTableClient, SubgraphTable, SubgraphSchema, SubgraphHandler, SubgraphFilter, SubgraphDefinition, SubgraphContext, SubgraphColumn, StxTransferPayload, StxTransferFilter, StxMintPayload, StxMintFilter, StxLockPayload, StxLockFilter, StxBurnPayload, StxBurnFilter, SparseProbeTarget, RowValue, ReindexOptions, PrismaGenOptions, PrintValidationResult, PrintSample, PrintFieldToTS, PrintField, PrintEventPayload, PrintEventFor, PrintEventFilter, PrintDataOf, NftTransferPayload, NftTransferFilter, NftMintPayload, NftMintFilter, NftBurnPayload, NftBurnFilter, KyselyGenOptions, InferredTopicSchema, InferredPrintField, InferTableRow, InferSubgraphClient, InferColumnType, IndexCodegenTarget, IndexCodegenOptions, INDEX_CODEGEN_TABLES, GeneratedSQL, FtTransferPayload, FtTransferFilter, FtMintPayload, FtMintFilter, FtBurnPayload, FtBurnFilter, FindManyOptions, EventForFilter, DrizzleGenOptions, DeployPlan, DeployDiff, ContractDeployPayload, ContractDeployFilter, ContractCallPayload, ContractCallFilter, ContractCallEvent, ComputedValue, ComparisonFilter, ColumnType, ColumnToTS, ColumnDiff, ByoMigrationPlan, ByoBreakingChangeError, AnyEvent, AggregateSpec, AggregateResult };
|
|
1103
|
+
export { validateSubgraphDefinition, validatePrintPayload, sparseProbeTargets, resumeReindex, renderDeployPlan, reindexSubgraph, pgSchemaName, inferPrintTopics, hasBreakingChanges, generateSubgraphSQL, generatePrismaSchema, generateKyselySchema, generateIndexSchema, generateDrizzleSchema, filterFieldsByType, diffSchema, deploySchema, defineSubgraph, defineSchema, canSparseScan, camelizeDataKey, backfillSubgraph, WriteRow, WhereInput, TypedSubgraphDefinition, TypedSubgraphContext, TypedHandlers, TxMeta, TableDiff, SystemRow, SubscribeOptions, SubgraphTableClient, SubgraphTable, SubgraphSchema, SubgraphHandler, SubgraphFilter, SubgraphDefinition, SubgraphContext, SubgraphColumn, StxTransferPayload, StxTransferFilter, StxMintPayload, StxMintFilter, StxLockPayload, StxLockFilter, StxBurnPayload, StxBurnFilter, SparseProbeTarget, RowValue, ReindexOptions, PrismaGenOptions, PrintValidationResult, PrintSample, PrintFieldToTS, PrintField, PrintEventPayload, PrintEventFor, PrintEventFilter, PrintDataOf, NftTransferPayload, NftTransferFilter, NftMintPayload, NftMintFilter, NftBurnPayload, NftBurnFilter, KyselyGenOptions, InferredTopicSchema, InferredPrintField, InferTableRow, InferSubgraphClient, InferContext, InferColumnType, IndexCodegenTarget, IndexCodegenOptions, INDEX_CODEGEN_TABLES, GeneratedSQL, FtTransferPayload, FtTransferFilter, FtMintPayload, FtMintFilter, FtBurnPayload, FtBurnFilter, FindManyOptions, FactoryScope, EventForFilter, DrizzleGenOptions, DeployPlan, DeployDiff, ContractDeployPayload, ContractDeployFilter, ContractCallPayload, ContractCallFilter, ContractCallEvent, ComputedValue, ComparisonFilter, ColumnType, ColumnToTS, ColumnDiff, ByoMigrationPlan, ByoBreakingChangeError, AnyEvent, AggregateSpec, AggregateResult };
|
package/dist/src/index.js
CHANGED
|
@@ -86,24 +86,105 @@ var VALID_FILTER_TYPES = [
|
|
|
86
86
|
"contract_deploy",
|
|
87
87
|
"print_event"
|
|
88
88
|
];
|
|
89
|
-
var
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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 = {
|
|
93
98
|
minAmount: z.bigint().optional(),
|
|
94
|
-
maxAmount: z.bigint().optional()
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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;
|
|
107
188
|
var SubgraphDefinitionSchema = z.object({
|
|
108
189
|
name: SubgraphNameSchema,
|
|
109
190
|
version: z.string().optional(),
|
|
@@ -117,6 +198,23 @@ var SubgraphDefinitionSchema = z.object({
|
|
|
117
198
|
function validateSubgraphDefinition(def) {
|
|
118
199
|
return SubgraphDefinitionSchema.parse(def);
|
|
119
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
|
+
}
|
|
120
218
|
|
|
121
219
|
// src/runtime/context.ts
|
|
122
220
|
import { logger } from "@secondlayer/shared/logger";
|
|
@@ -217,6 +315,18 @@ function emitJournalDDL(schemaName) {
|
|
|
217
315
|
`CREATE INDEX IF NOT EXISTS idx_${schemaName}_journal_height ON ${schemaName}._journal (block_height)`
|
|
218
316
|
];
|
|
219
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
|
+
}
|
|
220
330
|
function emitForeignKeyDDL(schemaName, tableName, tableDef) {
|
|
221
331
|
return (tableDef.relations ?? []).map((rel) => {
|
|
222
332
|
const constraintName = `fk_${schemaName}_${tableName}_${rel.name}`;
|
|
@@ -235,6 +345,9 @@ function generateSubgraphSQL(def, schemaNameOverride) {
|
|
|
235
345
|
statements.push(...emitTableDDL(schemaName, tableName, tableDef));
|
|
236
346
|
}
|
|
237
347
|
statements.push(...emitJournalDDL(schemaName));
|
|
348
|
+
if (Object.values(def.sources ?? {}).some((src) => src.factory !== undefined)) {
|
|
349
|
+
statements.push(...emitFactoryDDL(schemaName));
|
|
350
|
+
}
|
|
238
351
|
for (const [tableName, tableDef] of Object.entries(def.schema)) {
|
|
239
352
|
statements.push(...emitForeignKeyDDL(schemaName, tableName, tableDef));
|
|
240
353
|
}
|
|
@@ -379,23 +492,21 @@ class SubgraphContext {
|
|
|
379
492
|
formatUnits(value, decimals) {
|
|
380
493
|
return formatUnits(value, decimals);
|
|
381
494
|
}
|
|
382
|
-
async
|
|
383
|
-
this.validateTable(table);
|
|
495
|
+
async readRows(table, where, limit) {
|
|
384
496
|
const qualifiedTable = `"${this.pgSchemaName}"."${table}"`;
|
|
385
497
|
const { clause } = buildWhereClause(where);
|
|
386
|
-
const query = `SELECT * FROM ${qualifiedTable} WHERE ${clause} LIMIT
|
|
498
|
+
const query = `SELECT * FROM ${qualifiedTable} WHERE ${clause}${limit !== undefined ? ` LIMIT ${limit}` : ""}`;
|
|
387
499
|
const { rows } = await sql.raw(query).execute(this.db);
|
|
388
|
-
|
|
389
|
-
|
|
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);
|
|
390
506
|
}
|
|
391
507
|
async findMany(table, where) {
|
|
392
508
|
this.validateTable(table);
|
|
393
|
-
|
|
394
|
-
const { clause } = buildWhereClause(where);
|
|
395
|
-
const query = `SELECT * FROM ${qualifiedTable} WHERE ${clause}`;
|
|
396
|
-
const { rows } = await sql.raw(query).execute(this.db);
|
|
397
|
-
const dbRows = rows.map((r) => this.coerceRow(table, r));
|
|
398
|
-
return this.overlayMany(table, where, dbRows);
|
|
509
|
+
return this.overlayMany(table, where, await this.readRows(table, where));
|
|
399
510
|
}
|
|
400
511
|
overlayOne(table, where, dbRow) {
|
|
401
512
|
if (this.ops.length === 0)
|
|
@@ -1315,6 +1426,9 @@ async function runHandlers(subgraph, matched, ctx, opts) {
|
|
|
1315
1426
|
|
|
1316
1427
|
// src/runtime/source-matcher.ts
|
|
1317
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
|
+
}
|
|
1318
1432
|
function matchPattern(value, pattern) {
|
|
1319
1433
|
if (!pattern.includes("*"))
|
|
1320
1434
|
return value === pattern;
|
|
@@ -1327,6 +1441,22 @@ function matchPattern(value, pattern) {
|
|
|
1327
1441
|
return re.test(value);
|
|
1328
1442
|
}
|
|
1329
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
|
+
}
|
|
1330
1460
|
function traitAllows(filter, contractId, traitContracts) {
|
|
1331
1461
|
const trait = filter.trait;
|
|
1332
1462
|
if (!trait)
|
|
@@ -1338,7 +1468,7 @@ function traitAllows(filter, contractId, traitContracts) {
|
|
|
1338
1468
|
function assetContract(assetId) {
|
|
1339
1469
|
return assetId?.split("::")[0];
|
|
1340
1470
|
}
|
|
1341
|
-
function matchFilter(filter, transactions, eventsByTx, traitContracts) {
|
|
1471
|
+
function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts = new Map) {
|
|
1342
1472
|
const results = [];
|
|
1343
1473
|
switch (filter.type) {
|
|
1344
1474
|
case "stx_transfer":
|
|
@@ -1465,9 +1595,11 @@ function matchFilter(filter, transactions, eventsByTx, traitContracts) {
|
|
|
1465
1595
|
if (tx.type !== "contract_call")
|
|
1466
1596
|
continue;
|
|
1467
1597
|
if (filter.contractId) {
|
|
1468
|
-
if (!tx.contract_id || !
|
|
1598
|
+
if (!tx.contract_id || !matchAny(tx.contract_id, filter.contractId))
|
|
1469
1599
|
continue;
|
|
1470
1600
|
}
|
|
1601
|
+
if (!factoryAllows(filter, tx.contract_id, factoryContracts))
|
|
1602
|
+
continue;
|
|
1471
1603
|
if (filter.functionName) {
|
|
1472
1604
|
if (!tx.function_name || !matchPattern(tx.function_name, filter.functionName))
|
|
1473
1605
|
continue;
|
|
@@ -1514,11 +1646,13 @@ function matchFilter(filter, transactions, eventsByTx, traitContracts) {
|
|
|
1514
1646
|
return false;
|
|
1515
1647
|
const printContractId = data.contract_identifier ?? data.contract_id;
|
|
1516
1648
|
if (filter.contractId) {
|
|
1517
|
-
if (!printContractId || !
|
|
1649
|
+
if (!printContractId || !matchAny(printContractId, filter.contractId))
|
|
1518
1650
|
return false;
|
|
1519
1651
|
}
|
|
1520
1652
|
if (!traitAllows(filter, printContractId, traitContracts))
|
|
1521
1653
|
return false;
|
|
1654
|
+
if (!factoryAllows(filter, printContractId, factoryContracts))
|
|
1655
|
+
return false;
|
|
1522
1656
|
return true;
|
|
1523
1657
|
});
|
|
1524
1658
|
if (matched.length > 0) {
|
|
@@ -1530,7 +1664,7 @@ function matchFilter(filter, transactions, eventsByTx, traitContracts) {
|
|
|
1530
1664
|
}
|
|
1531
1665
|
return results;
|
|
1532
1666
|
}
|
|
1533
|
-
function matchSources(sources, transactions, events, traitContracts = new Map) {
|
|
1667
|
+
function matchSources(sources, transactions, events, traitContracts = new Map, factoryContracts = new Map) {
|
|
1534
1668
|
const eventsByTx = new Map;
|
|
1535
1669
|
for (const event of events) {
|
|
1536
1670
|
const list = eventsByTx.get(event.tx_id) ?? [];
|
|
@@ -1540,7 +1674,7 @@ function matchSources(sources, transactions, events, traitContracts = new Map) {
|
|
|
1540
1674
|
const seen = new Set;
|
|
1541
1675
|
const results = [];
|
|
1542
1676
|
for (const [sourceName, filter] of Object.entries(sources)) {
|
|
1543
|
-
const matches = matchFilter(filter, transactions, eventsByTx, traitContracts);
|
|
1677
|
+
const matches = matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts);
|
|
1544
1678
|
for (const match of matches) {
|
|
1545
1679
|
const dedupeKey = `${match.tx.tx_id}:${sourceName}`;
|
|
1546
1680
|
if (!seen.has(dedupeKey)) {
|
|
@@ -2195,6 +2329,57 @@ async function resolveRoute(subgraphName, targetDb) {
|
|
|
2195
2329
|
function invalidateSubgraphRoute(subgraphName) {
|
|
2196
2330
|
routeCache.delete(subgraphName);
|
|
2197
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
|
+
}
|
|
2198
2383
|
async function resolveTraitContracts(subgraph, blockHeight, db) {
|
|
2199
2384
|
const traits = new Set;
|
|
2200
2385
|
for (const source of Object.values(subgraph.sources)) {
|
|
@@ -2281,8 +2466,11 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2281
2466
|
txs = data.txs;
|
|
2282
2467
|
evts = data.events;
|
|
2283
2468
|
}
|
|
2469
|
+
const route = await resolveRoute(subgraphName, targetDb);
|
|
2470
|
+
const schemaName = route.schemaName;
|
|
2284
2471
|
const traitContracts = await resolveTraitContracts(subgraph, blockHeight, targetDb);
|
|
2285
|
-
const
|
|
2472
|
+
const { resolved: factoryContracts, discovered } = await resolveFactoryContracts(subgraph, blockHeight, schemaName, targetDb, txs, evts);
|
|
2473
|
+
const matched = matchSources(subgraph.sources, txs, evts, traitContracts, factoryContracts);
|
|
2286
2474
|
result.matched = matched.length;
|
|
2287
2475
|
if (matched.length === 0) {
|
|
2288
2476
|
if (!opts?.skipProgressUpdate) {
|
|
@@ -2290,8 +2478,6 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2290
2478
|
}
|
|
2291
2479
|
return result;
|
|
2292
2480
|
}
|
|
2293
|
-
const route = await resolveRoute(subgraphName, targetDb);
|
|
2294
|
-
const schemaName = route.schemaName;
|
|
2295
2481
|
const blockMeta = {
|
|
2296
2482
|
height: block.height,
|
|
2297
2483
|
hash: block.hash,
|
|
@@ -2349,6 +2535,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2349
2535
|
});
|
|
2350
2536
|
result.processed = runResult.processed;
|
|
2351
2537
|
result.errors = runResult.errors;
|
|
2538
|
+
await persistFactoryDiscoveries(schemaName, route.dataDb, blockHeight, discovered);
|
|
2352
2539
|
await targetDb.transaction().execute(async (tx) => {
|
|
2353
2540
|
if (manifest && manifest.count > 0) {
|
|
2354
2541
|
await emitSubscriptionOutbox(tx, subgraphName, manifest, matcher, block.height);
|
|
@@ -3068,6 +3255,9 @@ async function backfillSubgraph(def, opts) {
|
|
|
3068
3255
|
function defineSubgraph(def) {
|
|
3069
3256
|
return def;
|
|
3070
3257
|
}
|
|
3258
|
+
function defineSchema(schema) {
|
|
3259
|
+
return schema;
|
|
3260
|
+
}
|
|
3071
3261
|
// src/print-schema.ts
|
|
3072
3262
|
import {
|
|
3073
3263
|
deserializeCV as deserializeCV3
|
|
@@ -3987,8 +4177,8 @@ function diffKeyLists(existing, incoming) {
|
|
|
3987
4177
|
const existingSet = new Set((existing ?? []).map(key2));
|
|
3988
4178
|
const incomingSet = new Set((incoming ?? []).map(key2));
|
|
3989
4179
|
return {
|
|
3990
|
-
added: (incoming ?? []).filter((cols) => !existingSet.has(key2(cols))),
|
|
3991
|
-
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])
|
|
3992
4182
|
};
|
|
3993
4183
|
}
|
|
3994
4184
|
function diffSchema(existing, incoming) {
|
|
@@ -4345,9 +4535,11 @@ export {
|
|
|
4345
4535
|
generateKyselySchema,
|
|
4346
4536
|
generateIndexSchema,
|
|
4347
4537
|
generateDrizzleSchema,
|
|
4538
|
+
filterFieldsByType,
|
|
4348
4539
|
diffSchema,
|
|
4349
4540
|
deploySchema,
|
|
4350
4541
|
defineSubgraph,
|
|
4542
|
+
defineSchema,
|
|
4351
4543
|
canSparseScan,
|
|
4352
4544
|
camelizeDataKey,
|
|
4353
4545
|
backfillSubgraph,
|
|
@@ -4355,5 +4547,5 @@ export {
|
|
|
4355
4547
|
ByoBreakingChangeError
|
|
4356
4548
|
};
|
|
4357
4549
|
|
|
4358
|
-
//# debugId=
|
|
4550
|
+
//# debugId=8BECEC189376FB2B64756E2164756E21
|
|
4359
4551
|
//# sourceMappingURL=index.js.map
|