@secondlayer/subgraphs 0.8.1 → 0.9.1
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 +113 -32
- package/dist/src/index.js +486 -99
- package/dist/src/index.js.map +9 -10
- package/dist/src/runtime/block-processor.d.ts +110 -20
- package/dist/src/runtime/block-processor.js +452 -90
- package/dist/src/runtime/block-processor.js.map +7 -8
- package/dist/src/runtime/catchup.d.ts +110 -20
- package/dist/src/runtime/catchup.js +457 -94
- package/dist/src/runtime/catchup.js.map +8 -9
- package/dist/src/runtime/clarity.js +3 -3
- package/dist/src/runtime/clarity.js.map +3 -3
- package/dist/src/runtime/context.d.ts +23 -5
- package/dist/src/runtime/context.js +57 -1
- package/dist/src/runtime/context.js.map +3 -3
- package/dist/src/runtime/processor.js +457 -94
- package/dist/src/runtime/processor.js.map +8 -9
- package/dist/src/runtime/reindex.d.ts +110 -20
- package/dist/src/runtime/reindex.js +452 -90
- package/dist/src/runtime/reindex.js.map +7 -8
- package/dist/src/runtime/reorg.d.ts +110 -20
- package/dist/src/runtime/reorg.js +452 -90
- package/dist/src/runtime/reorg.js.map +7 -8
- package/dist/src/runtime/runner.d.ts +152 -42
- package/dist/src/runtime/runner.js +226 -30
- package/dist/src/runtime/runner.js.map +4 -4
- package/dist/src/runtime/source-matcher.d.ts +88 -31
- package/dist/src/runtime/source-matcher.js +171 -61
- package/dist/src/runtime/source-matcher.js.map +4 -5
- package/dist/src/schema/index.d.ts +110 -20
- package/dist/src/schema/index.js +34 -9
- package/dist/src/schema/index.js.map +3 -3
- package/dist/src/service.js +457 -94
- package/dist/src/service.js.map +8 -9
- package/dist/src/templates.js +52 -51
- package/dist/src/templates.js.map +3 -3
- package/dist/src/types.d.ts +111 -29
- package/dist/src/types.js +1 -20
- package/dist/src/types.js.map +3 -4
- package/dist/src/validate.d.ts +112 -22
- package/dist/src/validate.js +35 -10
- package/dist/src/validate.js.map +3 -3
- package/package.json +2 -2
|
@@ -1,20 +1,6 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
3
3
|
|
|
4
|
-
// src/types.ts
|
|
5
|
-
function sourceKey(source) {
|
|
6
|
-
if (source.contract) {
|
|
7
|
-
if (source.function)
|
|
8
|
-
return `${source.contract}::${source.function}`;
|
|
9
|
-
if (source.event)
|
|
10
|
-
return `${source.contract}::${source.event}`;
|
|
11
|
-
return source.contract;
|
|
12
|
-
}
|
|
13
|
-
if (source.type)
|
|
14
|
-
return source.type;
|
|
15
|
-
return "*";
|
|
16
|
-
}
|
|
17
|
-
|
|
18
4
|
// src/runtime/source-matcher.ts
|
|
19
5
|
var patternCache = new Map;
|
|
20
6
|
function matchPattern(value, pattern) {
|
|
@@ -28,62 +14,186 @@ function matchPattern(value, pattern) {
|
|
|
28
14
|
}
|
|
29
15
|
return re.test(value);
|
|
30
16
|
}
|
|
31
|
-
function
|
|
17
|
+
function matchFilter(filter, transactions, eventsByTx) {
|
|
32
18
|
const results = [];
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
19
|
+
switch (filter.type) {
|
|
20
|
+
case "stx_transfer":
|
|
21
|
+
case "stx_mint":
|
|
22
|
+
case "stx_burn":
|
|
23
|
+
case "stx_lock": {
|
|
24
|
+
const eventType = `${filter.type}_event`;
|
|
25
|
+
for (const tx of transactions) {
|
|
26
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
27
|
+
const matched = txEvents.filter((e) => e.type === eventType);
|
|
28
|
+
if (matched.length === 0)
|
|
29
|
+
continue;
|
|
30
|
+
const filtered = matched.filter((e) => {
|
|
42
31
|
const data = e.data;
|
|
43
|
-
|
|
44
|
-
if (rawAmount === undefined)
|
|
32
|
+
if (!data)
|
|
45
33
|
return false;
|
|
46
|
-
|
|
47
|
-
|
|
34
|
+
if ("sender" in filter && filter.sender) {
|
|
35
|
+
if (!matchPattern(data.sender, filter.sender))
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
if ("recipient" in filter && filter.recipient) {
|
|
39
|
+
if (!matchPattern(data.recipient, filter.recipient))
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
if ("lockedAddress" in filter && filter.lockedAddress) {
|
|
43
|
+
if (!matchPattern(data.locked_address, filter.lockedAddress))
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
if ("minAmount" in filter && filter.minAmount !== undefined) {
|
|
47
|
+
const amount = BigInt(data.amount ?? data.locked_amount ?? "0");
|
|
48
|
+
if (amount < filter.minAmount)
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
if ("maxAmount" in filter && filter.maxAmount !== undefined) {
|
|
52
|
+
const amount = BigInt(data.amount ?? "0");
|
|
53
|
+
if (amount > filter.maxAmount)
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
48
57
|
});
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
|
|
58
|
+
if (filtered.length > 0) {
|
|
59
|
+
results.push({ tx, events: filtered });
|
|
60
|
+
}
|
|
52
61
|
}
|
|
53
|
-
|
|
54
|
-
continue;
|
|
62
|
+
break;
|
|
55
63
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
case "ft_transfer":
|
|
65
|
+
case "ft_mint":
|
|
66
|
+
case "ft_burn": {
|
|
67
|
+
const eventType = `${filter.type}_event`;
|
|
68
|
+
for (const tx of transactions) {
|
|
69
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
70
|
+
const matched = txEvents.filter((e) => {
|
|
71
|
+
if (e.type !== eventType)
|
|
72
|
+
return false;
|
|
73
|
+
const data = e.data;
|
|
74
|
+
if (!data)
|
|
75
|
+
return false;
|
|
76
|
+
if (filter.assetIdentifier) {
|
|
77
|
+
const assetId = data.asset_identifier;
|
|
78
|
+
if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
if ("sender" in filter && filter.sender) {
|
|
82
|
+
if (!matchPattern(data.sender, filter.sender))
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
if ("recipient" in filter && filter.recipient) {
|
|
86
|
+
if (!matchPattern(data.recipient, filter.recipient))
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
if (filter.minAmount !== undefined) {
|
|
90
|
+
const amount = BigInt(data.amount ?? "0");
|
|
91
|
+
if (amount < filter.minAmount)
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
return true;
|
|
95
|
+
});
|
|
96
|
+
if (matched.length > 0) {
|
|
97
|
+
results.push({ tx, events: matched });
|
|
98
|
+
}
|
|
63
99
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
case "nft_transfer":
|
|
103
|
+
case "nft_mint":
|
|
104
|
+
case "nft_burn": {
|
|
105
|
+
const eventType = `${filter.type}_event`;
|
|
106
|
+
for (const tx of transactions) {
|
|
107
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
108
|
+
const matched = txEvents.filter((e) => {
|
|
109
|
+
if (e.type !== eventType)
|
|
110
|
+
return false;
|
|
68
111
|
const data = e.data;
|
|
69
|
-
|
|
70
|
-
|
|
112
|
+
if (!data)
|
|
113
|
+
return false;
|
|
114
|
+
if (filter.assetIdentifier) {
|
|
115
|
+
const assetId = data.asset_identifier;
|
|
116
|
+
if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
if ("sender" in filter && filter.sender) {
|
|
120
|
+
if (!matchPattern(data.sender, filter.sender))
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
if ("recipient" in filter && filter.recipient) {
|
|
124
|
+
if (!matchPattern(data.recipient, filter.recipient))
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
return true;
|
|
71
128
|
});
|
|
72
|
-
if (
|
|
129
|
+
if (matched.length > 0) {
|
|
130
|
+
results.push({ tx, events: matched });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
case "contract_call": {
|
|
136
|
+
for (const tx of transactions) {
|
|
137
|
+
if (tx.type !== "contract_call")
|
|
138
|
+
continue;
|
|
139
|
+
if (filter.contractId) {
|
|
140
|
+
if (!tx.contract_id || !matchPattern(tx.contract_id, filter.contractId))
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (filter.functionName) {
|
|
144
|
+
if (!tx.function_name || !matchPattern(tx.function_name, filter.functionName))
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (filter.caller) {
|
|
148
|
+
if (!matchPattern(tx.sender, filter.caller))
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
152
|
+
results.push({ tx, events: txEvents });
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
case "contract_deploy": {
|
|
157
|
+
for (const tx of transactions) {
|
|
158
|
+
if (tx.type !== "smart_contract")
|
|
73
159
|
continue;
|
|
160
|
+
if (filter.deployer) {
|
|
161
|
+
if (!matchPattern(tx.sender, filter.deployer))
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (filter.contractName) {
|
|
165
|
+
const name = tx.contract_id?.split(".")[1] ?? "";
|
|
166
|
+
if (!matchPattern(name, filter.contractName))
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
170
|
+
results.push({ tx, events: txEvents });
|
|
74
171
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
case "print_event": {
|
|
175
|
+
for (const tx of transactions) {
|
|
176
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
177
|
+
const matched = txEvents.filter((e) => {
|
|
178
|
+
if (e.type !== "smart_contract_event")
|
|
179
|
+
return false;
|
|
79
180
|
const data = e.data;
|
|
80
|
-
|
|
81
|
-
|
|
181
|
+
if (!data)
|
|
182
|
+
return false;
|
|
183
|
+
if (data.topic !== "print")
|
|
184
|
+
return false;
|
|
185
|
+
if (filter.contractId) {
|
|
186
|
+
const contractId = data.contract_identifier;
|
|
187
|
+
if (!contractId || !matchPattern(contractId, filter.contractId))
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
return true;
|
|
82
191
|
});
|
|
192
|
+
if (matched.length > 0) {
|
|
193
|
+
results.push({ tx, events: matched });
|
|
194
|
+
}
|
|
83
195
|
}
|
|
84
|
-
|
|
85
|
-
results.push({ tx, events: matchedEvents, sourceKey: key });
|
|
86
|
-
}
|
|
196
|
+
break;
|
|
87
197
|
}
|
|
88
198
|
}
|
|
89
199
|
return results;
|
|
@@ -97,13 +207,13 @@ function matchSources(sources, transactions, events) {
|
|
|
97
207
|
}
|
|
98
208
|
const seen = new Set;
|
|
99
209
|
const results = [];
|
|
100
|
-
for (const
|
|
101
|
-
const matches =
|
|
210
|
+
for (const [sourceName, filter] of Object.entries(sources)) {
|
|
211
|
+
const matches = matchFilter(filter, transactions, eventsByTx);
|
|
102
212
|
for (const match of matches) {
|
|
103
|
-
const dedupeKey = `${match.tx.tx_id}:${
|
|
213
|
+
const dedupeKey = `${match.tx.tx_id}:${sourceName}`;
|
|
104
214
|
if (!seen.has(dedupeKey)) {
|
|
105
215
|
seen.add(dedupeKey);
|
|
106
|
-
results.push(match);
|
|
216
|
+
results.push({ ...match, sourceName });
|
|
107
217
|
}
|
|
108
218
|
}
|
|
109
219
|
}
|
|
@@ -113,5 +223,5 @@ export {
|
|
|
113
223
|
matchSources
|
|
114
224
|
};
|
|
115
225
|
|
|
116
|
-
//# debugId=
|
|
226
|
+
//# debugId=F16470C1CAB3E0FE64756E2164756E21
|
|
117
227
|
//# sourceMappingURL=source-matcher.js.map
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/
|
|
3
|
+
"sources": ["../src/runtime/source-matcher.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"
|
|
6
|
-
"import { type SubgraphSource, sourceKey } from \"../types.ts\";\n\nexport interface MatchedTx {\n\ttx: {\n\t\ttx_id: string;\n\t\ttype: string;\n\t\tsender: string;\n\t\tstatus: string;\n\t\tcontract_id?: string | null;\n\t\tfunction_name?: string | null;\n\t};\n\tevents: {\n\t\tid: string;\n\t\ttx_id: string;\n\t\ttype: string;\n\t\tevent_index: number;\n\t\tdata: unknown;\n\t}[];\n\t/** Which source produced this match — used for handler dispatch */\n\tsourceKey: string;\n}\n\n/**\n * Check if a string matches a pattern with `*` wildcard support.\n * Compiled RegExp objects are cached to avoid recompilation on every call.\n */\nconst patternCache = new Map<string, RegExp>();\n\nfunction matchPattern(value: string, pattern: string): boolean {\n\tif (!pattern.includes(\"*\")) return value === pattern;\n\tlet re = patternCache.get(pattern);\n\tif (!re) {\n\t\tconst regex = pattern\n\t\t\t.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\t\t\t.replace(/\\*/g, \".*\");\n\t\tre = new RegExp(`^${regex}$`);\n\t\tpatternCache.set(pattern, re);\n\t}\n\treturn re.test(value);\n}\n\ntype TxRecord = {\n\ttx_id: string;\n\ttype: string;\n\tsender: string;\n\tstatus: string;\n\tcontract_id?: string | null;\n\tfunction_name?: string | null;\n};\ntype EventRecord = {\n\tid: string;\n\ttx_id: string;\n\ttype: string;\n\tevent_index: number;\n\tdata: unknown;\n};\n\n/**\n * Match a single source against transactions and events.\n */\nfunction matchSource(\n\tsource: SubgraphSource,\n\ttransactions: TxRecord[],\n\teventsByTx: Map<string, EventRecord[]>,\n): MatchedTx[] {\n\tconst results: MatchedTx[] = [];\n\tconst key = sourceKey(source);\n\n\tfor (const tx of transactions) {\n\t\t// Type-based matching (e.g., token_transfer → matches tx.type)\n\t\tif (source.type) {\n\t\t\tif (!matchPattern(tx.type, source.type)) continue;\n\n\t\t\tconst txEvents = eventsByTx.get(tx.tx_id) ?? [];\n\n\t\t\t// Collect all events for this tx (type-based sources match the whole tx)\n\t\t\tlet matchedEvents = txEvents;\n\n\t\t\t// minAmount filter — check events with an amount field\n\t\t\tif (source.minAmount !== undefined) {\n\t\t\t\tconst amountEvents = matchedEvents.filter((e) => {\n\t\t\t\t\tconst data = e.data as Record<string, unknown> | null;\n\t\t\t\t\tconst rawAmount = data?.amount as string | number | undefined;\n\t\t\t\t\tif (rawAmount === undefined) return false;\n\t\t\t\t\tconst amount = BigInt(rawAmount);\n\t\t\t\t\treturn amount >= source.minAmount!;\n\t\t\t\t});\n\t\t\t\tif (amountEvents.length === 0) continue;\n\t\t\t\tmatchedEvents = amountEvents;\n\t\t\t}\n\n\t\t\tresults.push({ tx, events: matchedEvents, sourceKey: key });\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Contract-based matching\n\t\tif (source.contract) {\n\t\t\tconst txContractMatch =\n\t\t\t\ttx.contract_id && matchPattern(tx.contract_id, source.contract);\n\n\t\t\t// Function filter\n\t\t\tif (source.function && tx.function_name) {\n\t\t\t\tif (!matchPattern(tx.function_name, source.function)) continue;\n\t\t\t} else if (source.function && !tx.function_name) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst txEvents = eventsByTx.get(tx.tx_id) ?? [];\n\t\t\tlet matchedEvents = txEvents;\n\n\t\t\tif (!txContractMatch) {\n\t\t\t\t// Check if any events match the contract\n\t\t\t\tmatchedEvents = txEvents.filter((e) => {\n\t\t\t\t\tconst data = e.data as Record<string, unknown> | null;\n\t\t\t\t\tconst contractIdentifier = data?.contract_identifier as\n\t\t\t\t\t\t| string\n\t\t\t\t\t\t| undefined;\n\t\t\t\t\treturn (\n\t\t\t\t\t\tcontractIdentifier &&\n\t\t\t\t\t\tmatchPattern(contractIdentifier, source.contract!)\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t\tif (matchedEvents.length === 0) continue;\n\t\t\t}\n\n\t\t\t// Event type / topic filter\n\t\t\tif (source.event) {\n\t\t\t\tmatchedEvents = matchedEvents.filter((e) => {\n\t\t\t\t\tif (matchPattern(e.type, source.event!)) return true;\n\t\t\t\t\tconst data = e.data as Record<string, unknown> | null;\n\t\t\t\t\tconst topic = data?.topic as string | undefined;\n\t\t\t\t\treturn topic ? matchPattern(topic, source.event!) : false;\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (txContractMatch || matchedEvents.length > 0) {\n\t\t\t\tresults.push({ tx, events: matchedEvents, sourceKey: key });\n\t\t\t}\n\t\t}\n\t}\n\n\treturn results;\n}\n\n/**\n * Match all sources against a block's transactions and events.\n * Deduplicates by (txId, sourceKey) — each handler key fires at most once per tx.\n */\nexport function matchSources(\n\tsources: SubgraphSource[],\n\ttransactions: TxRecord[],\n\tevents: EventRecord[],\n): MatchedTx[] {\n\t// Index events by txId\n\tconst eventsByTx = new Map<string, EventRecord[]>();\n\tfor (const event of events) {\n\t\tconst list = eventsByTx.get(event.tx_id) ?? [];\n\t\tlist.push(event);\n\t\teventsByTx.set(event.tx_id, list);\n\t}\n\n\tconst seen = new Set<string>();\n\tconst results: MatchedTx[] = [];\n\n\tfor (const source of sources) {\n\t\tconst matches = matchSource(source, transactions, eventsByTx);\n\t\tfor (const match of matches) {\n\t\t\tconst dedupeKey = `${match.tx.tx_id}:${match.sourceKey}`;\n\t\t\tif (!seen.has(dedupeKey)) {\n\t\t\t\tseen.add(dedupeKey);\n\t\t\t\tresults.push(match);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn results;\n}\n"
|
|
5
|
+
"import type { SubgraphFilter } from \"../types.ts\";\n\nexport interface MatchedTx {\n\ttx: TxRecord;\n\tevents: EventRecord[];\n\t/** Source object key — used for handler dispatch */\n\tsourceName: string;\n}\n\ntype TxRecord = {\n\ttx_id: string;\n\ttype: string;\n\tsender: string;\n\tstatus: string;\n\tcontract_id?: string | null;\n\tfunction_name?: string | null;\n\tfunction_args?: unknown | null;\n\traw_result?: string | null;\n};\n\ntype EventRecord = {\n\tid: string;\n\ttx_id: string;\n\ttype: string;\n\tevent_index: number;\n\tdata: unknown;\n};\n\n// ── Wildcard matching (shared with v1) ──────────────────────────────\n\nconst patternCache = new Map<string, RegExp>();\n\nfunction matchPattern(value: string, pattern: string): boolean {\n\tif (!pattern.includes(\"*\")) return value === pattern;\n\tlet re = patternCache.get(pattern);\n\tif (!re) {\n\t\tconst regex = pattern\n\t\t\t.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\t\t\t.replace(/\\*/g, \".*\");\n\t\tre = new RegExp(`^${regex}$`);\n\t\tpatternCache.set(pattern, re);\n\t}\n\treturn re.test(value);\n}\n\n// ── Per-filter-type matchers ────────────────────────────────────────\n\nfunction matchFilter(\n\tfilter: SubgraphFilter,\n\ttransactions: TxRecord[],\n\teventsByTx: Map<string, EventRecord[]>,\n): { tx: TxRecord; events: EventRecord[] }[] {\n\tconst results: { tx: TxRecord; events: EventRecord[] }[] = [];\n\n\tswitch (filter.type) {\n\t\t// ── STX events ──\n\t\tcase \"stx_transfer\":\n\t\tcase \"stx_mint\":\n\t\tcase \"stx_burn\":\n\t\tcase \"stx_lock\": {\n\t\t\tconst eventType = `${filter.type}_event`;\n\t\t\tfor (const tx of transactions) {\n\t\t\t\tconst txEvents = eventsByTx.get(tx.tx_id) ?? [];\n\t\t\t\tconst matched = txEvents.filter((e) => e.type === eventType);\n\t\t\t\tif (matched.length === 0) continue;\n\n\t\t\t\t// Apply address filters\n\t\t\t\tconst filtered = matched.filter((e) => {\n\t\t\t\t\tconst data = e.data as Record<string, unknown> | null;\n\t\t\t\t\tif (!data) return false;\n\t\t\t\t\tif (\"sender\" in filter && filter.sender) {\n\t\t\t\t\t\tif (!matchPattern(data.sender as string, filter.sender))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (\"recipient\" in filter && filter.recipient) {\n\t\t\t\t\t\tif (!matchPattern(data.recipient as string, filter.recipient))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (\"lockedAddress\" in filter && filter.lockedAddress) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t!matchPattern(\n\t\t\t\t\t\t\t\tdata.locked_address as string,\n\t\t\t\t\t\t\t\tfilter.lockedAddress,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\t// Amount filters\n\t\t\t\t\tif (\"minAmount\" in filter && filter.minAmount !== undefined) {\n\t\t\t\t\t\tconst amount = BigInt((data.amount ?? data.locked_amount ?? \"0\") as string);\n\t\t\t\t\t\tif (amount < filter.minAmount) return false;\n\t\t\t\t\t}\n\t\t\t\t\tif (\"maxAmount\" in filter && (filter as { maxAmount?: bigint }).maxAmount !== undefined) {\n\t\t\t\t\t\tconst amount = BigInt((data.amount ?? \"0\") as string);\n\t\t\t\t\t\tif (amount > (filter as { maxAmount: bigint }).maxAmount) return false;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\n\t\t\t\tif (filtered.length > 0) {\n\t\t\t\t\tresults.push({ tx, events: filtered });\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t// ── FT events ──\n\t\tcase \"ft_transfer\":\n\t\tcase \"ft_mint\":\n\t\tcase \"ft_burn\": {\n\t\t\tconst eventType = `${filter.type}_event`;\n\t\t\tfor (const tx of transactions) {\n\t\t\t\tconst txEvents = eventsByTx.get(tx.tx_id) ?? [];\n\t\t\t\tconst matched = txEvents.filter((e) => {\n\t\t\t\t\tif (e.type !== eventType) return false;\n\t\t\t\t\tconst data = e.data as Record<string, unknown> | null;\n\t\t\t\t\tif (!data) return false;\n\n\t\t\t\t\t// Asset identifier filter\n\t\t\t\t\tif (filter.assetIdentifier) {\n\t\t\t\t\t\tconst assetId = data.asset_identifier as string | undefined;\n\t\t\t\t\t\tif (!assetId || !matchPattern(assetId, filter.assetIdentifier))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\t// Address filters\n\t\t\t\t\tif (\"sender\" in filter && filter.sender) {\n\t\t\t\t\t\tif (!matchPattern(data.sender as string, filter.sender))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (\"recipient\" in filter && filter.recipient) {\n\t\t\t\t\t\tif (!matchPattern(data.recipient as string, filter.recipient))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\t// Amount filter\n\t\t\t\t\tif (filter.minAmount !== undefined) {\n\t\t\t\t\t\tconst amount = BigInt((data.amount ?? \"0\") as string);\n\t\t\t\t\t\tif (amount < filter.minAmount) return false;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\n\t\t\t\tif (matched.length > 0) {\n\t\t\t\t\tresults.push({ tx, events: matched });\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t// ── NFT events ──\n\t\tcase \"nft_transfer\":\n\t\tcase \"nft_mint\":\n\t\tcase \"nft_burn\": {\n\t\t\tconst eventType = `${filter.type}_event`;\n\t\t\tfor (const tx of transactions) {\n\t\t\t\tconst txEvents = eventsByTx.get(tx.tx_id) ?? [];\n\t\t\t\tconst matched = txEvents.filter((e) => {\n\t\t\t\t\tif (e.type !== eventType) return false;\n\t\t\t\t\tconst data = e.data as Record<string, unknown> | null;\n\t\t\t\t\tif (!data) return false;\n\n\t\t\t\t\tif (filter.assetIdentifier) {\n\t\t\t\t\t\tconst assetId = data.asset_identifier as string | undefined;\n\t\t\t\t\t\tif (!assetId || !matchPattern(assetId, filter.assetIdentifier))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (\"sender\" in filter && filter.sender) {\n\t\t\t\t\t\tif (!matchPattern(data.sender as string, filter.sender))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (\"recipient\" in filter && filter.recipient) {\n\t\t\t\t\t\tif (!matchPattern(data.recipient as string, filter.recipient))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\n\t\t\t\tif (matched.length > 0) {\n\t\t\t\t\tresults.push({ tx, events: matched });\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t// ── Contract call ──\n\t\tcase \"contract_call\": {\n\t\t\tfor (const tx of transactions) {\n\t\t\t\tif (tx.type !== \"contract_call\") continue;\n\n\t\t\t\t// Contract filter\n\t\t\t\tif (filter.contractId) {\n\t\t\t\t\tif (!tx.contract_id || !matchPattern(tx.contract_id, filter.contractId))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// Function filter\n\t\t\t\tif (filter.functionName) {\n\t\t\t\t\tif (\n\t\t\t\t\t\t!tx.function_name ||\n\t\t\t\t\t\t!matchPattern(tx.function_name, filter.functionName)\n\t\t\t\t\t)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// Caller filter\n\t\t\t\tif (filter.caller) {\n\t\t\t\t\tif (!matchPattern(tx.sender, filter.caller)) continue;\n\t\t\t\t}\n\n\t\t\t\tconst txEvents = eventsByTx.get(tx.tx_id) ?? [];\n\t\t\t\tresults.push({ tx, events: txEvents });\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t// ── Contract deploy ──\n\t\tcase \"contract_deploy\": {\n\t\t\tfor (const tx of transactions) {\n\t\t\t\tif (tx.type !== \"smart_contract\") continue;\n\n\t\t\t\tif (filter.deployer) {\n\t\t\t\t\tif (!matchPattern(tx.sender, filter.deployer)) continue;\n\t\t\t\t}\n\t\t\t\tif (filter.contractName) {\n\t\t\t\t\tconst name = tx.contract_id?.split(\".\")[1] ?? \"\";\n\t\t\t\t\tif (!matchPattern(name, filter.contractName)) continue;\n\t\t\t\t}\n\n\t\t\t\tconst txEvents = eventsByTx.get(tx.tx_id) ?? [];\n\t\t\t\tresults.push({ tx, events: txEvents });\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t// ── Print event ──\n\t\tcase \"print_event\": {\n\t\t\tfor (const tx of transactions) {\n\t\t\t\tconst txEvents = eventsByTx.get(tx.tx_id) ?? [];\n\t\t\t\tconst matched = txEvents.filter((e) => {\n\t\t\t\t\tif (e.type !== \"smart_contract_event\") return false;\n\t\t\t\t\tconst data = e.data as Record<string, unknown> | null;\n\t\t\t\t\tif (!data) return false;\n\t\t\t\t\tif (data.topic !== \"print\") return false;\n\n\t\t\t\t\t// Contract filter\n\t\t\t\t\tif (filter.contractId) {\n\t\t\t\t\t\tconst contractId = data.contract_identifier as string | undefined;\n\t\t\t\t\t\tif (!contractId || !matchPattern(contractId, filter.contractId))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\t// Topic filter — check the decoded Clarity value's topic field\n\t\t\t\t\t// At this stage data.value is still raw hex; topic filtering happens\n\t\t\t\t\t// after decode in the runner. For now, skip topic filtering here.\n\t\t\t\t\t// The runner will filter by topic after decoding.\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\n\t\t\t\tif (matched.length > 0) {\n\t\t\t\t\tresults.push({ tx, events: matched });\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn results;\n}\n\n/**\n * Match named filters against a block's transactions and events.\n * Returns matches with sourceName = the object key from sources.\n */\nexport function matchSources(\n\tsources: Record<string, SubgraphFilter>,\n\ttransactions: TxRecord[],\n\tevents: EventRecord[],\n): MatchedTx[] {\n\t// Index events by txId\n\tconst eventsByTx = new Map<string, EventRecord[]>();\n\tfor (const event of events) {\n\t\tconst list = eventsByTx.get(event.tx_id) ?? [];\n\t\tlist.push(event);\n\t\teventsByTx.set(event.tx_id, list);\n\t}\n\n\tconst seen = new Set<string>();\n\tconst results: MatchedTx[] = [];\n\n\tfor (const [sourceName, filter] of Object.entries(sources)) {\n\t\tconst matches = matchFilter(filter, transactions, eventsByTx);\n\t\tfor (const match of matches) {\n\t\t\tconst dedupeKey = `${match.tx.tx_id}:${sourceName}`;\n\t\t\tif (!seen.has(dedupeKey)) {\n\t\t\t\tseen.add(dedupeKey);\n\t\t\t\tresults.push({ ...match, sourceName });\n\t\t\t}\n\t\t}\n\t}\n\n\treturn results;\n}\n"
|
|
7
6
|
],
|
|
8
|
-
"mappings": ";;;;
|
|
9
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "F16470C1CAB3E0FE64756E2164756E21",
|
|
10
9
|
"names": []
|
|
11
10
|
}
|
|
@@ -18,19 +18,98 @@ interface SubgraphTable {
|
|
|
18
18
|
}
|
|
19
19
|
/** Subgraph schema — maps table names to table definitions */
|
|
20
20
|
type SubgraphSchema = Record<string, SubgraphTable>;
|
|
21
|
-
/**
|
|
22
|
-
interface
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
event?: string;
|
|
27
|
-
/** Function name to filter on */
|
|
28
|
-
function?: string;
|
|
29
|
-
/** Transaction type filter (e.g., "stx_transfer", "contract_call") */
|
|
30
|
-
type?: string;
|
|
31
|
-
/** Minimum amount filter (for stx_transfer sources) */
|
|
21
|
+
/** STX event filters */
|
|
22
|
+
interface StxTransferFilter {
|
|
23
|
+
type: "stx_transfer";
|
|
24
|
+
sender?: string;
|
|
25
|
+
recipient?: string;
|
|
32
26
|
minAmount?: bigint;
|
|
27
|
+
maxAmount?: bigint;
|
|
33
28
|
}
|
|
29
|
+
interface StxMintFilter {
|
|
30
|
+
type: "stx_mint";
|
|
31
|
+
recipient?: string;
|
|
32
|
+
minAmount?: bigint;
|
|
33
|
+
}
|
|
34
|
+
interface StxBurnFilter {
|
|
35
|
+
type: "stx_burn";
|
|
36
|
+
sender?: string;
|
|
37
|
+
minAmount?: bigint;
|
|
38
|
+
}
|
|
39
|
+
interface StxLockFilter {
|
|
40
|
+
type: "stx_lock";
|
|
41
|
+
lockedAddress?: string;
|
|
42
|
+
minAmount?: bigint;
|
|
43
|
+
}
|
|
44
|
+
/** FT event filters */
|
|
45
|
+
interface FtTransferFilter {
|
|
46
|
+
type: "ft_transfer";
|
|
47
|
+
assetIdentifier?: string;
|
|
48
|
+
sender?: string;
|
|
49
|
+
recipient?: string;
|
|
50
|
+
minAmount?: bigint;
|
|
51
|
+
}
|
|
52
|
+
interface FtMintFilter {
|
|
53
|
+
type: "ft_mint";
|
|
54
|
+
assetIdentifier?: string;
|
|
55
|
+
recipient?: string;
|
|
56
|
+
minAmount?: bigint;
|
|
57
|
+
}
|
|
58
|
+
interface FtBurnFilter {
|
|
59
|
+
type: "ft_burn";
|
|
60
|
+
assetIdentifier?: string;
|
|
61
|
+
sender?: string;
|
|
62
|
+
minAmount?: bigint;
|
|
63
|
+
}
|
|
64
|
+
/** NFT event filters */
|
|
65
|
+
interface NftTransferFilter {
|
|
66
|
+
type: "nft_transfer";
|
|
67
|
+
assetIdentifier?: string;
|
|
68
|
+
sender?: string;
|
|
69
|
+
recipient?: string;
|
|
70
|
+
}
|
|
71
|
+
interface NftMintFilter {
|
|
72
|
+
type: "nft_mint";
|
|
73
|
+
assetIdentifier?: string;
|
|
74
|
+
recipient?: string;
|
|
75
|
+
}
|
|
76
|
+
interface NftBurnFilter {
|
|
77
|
+
type: "nft_burn";
|
|
78
|
+
assetIdentifier?: string;
|
|
79
|
+
sender?: string;
|
|
80
|
+
}
|
|
81
|
+
/** Contract event filters */
|
|
82
|
+
interface ContractCallFilter {
|
|
83
|
+
type: "contract_call";
|
|
84
|
+
contractId?: string;
|
|
85
|
+
functionName?: string;
|
|
86
|
+
caller?: string;
|
|
87
|
+
/** ABI for typed event.args. If omitted, auto-fetched at deploy time. */
|
|
88
|
+
abi?: Record<string, unknown>;
|
|
89
|
+
}
|
|
90
|
+
interface ContractDeployFilter {
|
|
91
|
+
type: "contract_deploy";
|
|
92
|
+
deployer?: string;
|
|
93
|
+
contractName?: string;
|
|
94
|
+
}
|
|
95
|
+
interface PrintEventFilter {
|
|
96
|
+
type: "print_event";
|
|
97
|
+
contractId?: string;
|
|
98
|
+
topic?: string;
|
|
99
|
+
}
|
|
100
|
+
/** All subgraph filter types — discriminated on `type` */
|
|
101
|
+
type SubgraphFilter = StxTransferFilter | StxMintFilter | StxBurnFilter | StxLockFilter | FtTransferFilter | FtMintFilter | FtBurnFilter | NftTransferFilter | NftMintFilter | NftBurnFilter | ContractCallFilter | ContractDeployFilter | PrintEventFilter;
|
|
102
|
+
/** Transaction metadata available in handlers */
|
|
103
|
+
interface TxMeta {
|
|
104
|
+
txId: string;
|
|
105
|
+
sender: string;
|
|
106
|
+
type: string;
|
|
107
|
+
status: string;
|
|
108
|
+
contractId?: string | null;
|
|
109
|
+
functionName?: string | null;
|
|
110
|
+
}
|
|
111
|
+
/** Value or computed function that receives existing row */
|
|
112
|
+
type ComputedValue<T = unknown> = T | ((existing: Record<string, unknown> | null) => T);
|
|
34
113
|
/** Context passed to subgraph handlers during event processing */
|
|
35
114
|
interface SubgraphContext {
|
|
36
115
|
block: {
|
|
@@ -39,18 +118,29 @@ interface SubgraphContext {
|
|
|
39
118
|
timestamp: number
|
|
40
119
|
burnBlockHeight: number
|
|
41
120
|
};
|
|
42
|
-
tx:
|
|
43
|
-
txId: string
|
|
44
|
-
sender: string
|
|
45
|
-
type: string
|
|
46
|
-
status: string
|
|
47
|
-
};
|
|
121
|
+
tx: TxMeta;
|
|
48
122
|
insert(table: string, row: Record<string, unknown>): void;
|
|
49
123
|
update(table: string, where: Record<string, unknown>, set: Record<string, unknown>): void;
|
|
50
124
|
upsert(table: string, key: Record<string, unknown>, row: Record<string, unknown>): void;
|
|
51
125
|
delete(table: string, where: Record<string, unknown>): void;
|
|
126
|
+
/** Partial update — sets only specified fields, preserves others */
|
|
127
|
+
patch(table: string, where: Record<string, unknown>, set: Record<string, unknown>): void;
|
|
128
|
+
/** Find-then-merge-or-insert. Values can be functions: (existing) => newValue */
|
|
129
|
+
patchOrInsert(table: string, key: Record<string, unknown>, row: Record<string, ComputedValue>): Promise<void>;
|
|
52
130
|
findOne(table: string, where: Record<string, unknown>): Promise<Record<string, unknown> | null>;
|
|
53
131
|
findMany(table: string, where: Record<string, unknown>): Promise<Record<string, unknown>[]>;
|
|
132
|
+
/** Format a bigint amount with decimal places */
|
|
133
|
+
formatUnits(value: bigint, decimals: number): string;
|
|
134
|
+
/** Count rows matching filter */
|
|
135
|
+
count(table: string, where?: Record<string, unknown>): Promise<number>;
|
|
136
|
+
/** Sum a numeric column */
|
|
137
|
+
sum(table: string, column: string, where?: Record<string, unknown>): Promise<bigint>;
|
|
138
|
+
/** Min of a numeric column */
|
|
139
|
+
min(table: string, column: string, where?: Record<string, unknown>): Promise<bigint | null>;
|
|
140
|
+
/** Max of a numeric column */
|
|
141
|
+
max(table: string, column: string, where?: Record<string, unknown>): Promise<bigint | null>;
|
|
142
|
+
/** Count distinct values in a column */
|
|
143
|
+
countDistinct(table: string, column: string, where?: Record<string, unknown>): Promise<number>;
|
|
54
144
|
}
|
|
55
145
|
/** Handler function that processes events and writes to the subgraph */
|
|
56
146
|
type SubgraphHandler = (event: Record<string, unknown>, ctx: SubgraphContext) => Promise<void> | void;
|
|
@@ -64,11 +154,11 @@ interface SubgraphDefinition {
|
|
|
64
154
|
description?: string;
|
|
65
155
|
/** Block height to start indexing from (default: 1) */
|
|
66
156
|
startBlock?: number;
|
|
67
|
-
/**
|
|
68
|
-
sources:
|
|
157
|
+
/** Named source filters — keys become handler keys */
|
|
158
|
+
sources: Record<string, SubgraphFilter>;
|
|
69
159
|
/** Tables in this subgraph */
|
|
70
160
|
schema: SubgraphSchema;
|
|
71
|
-
/**
|
|
161
|
+
/** Handler functions — keys must match source names (or "*" for catch-all) */
|
|
72
162
|
handlers: Record<string, SubgraphHandler>;
|
|
73
163
|
}
|
|
74
164
|
interface GeneratedSQL {
|
package/dist/src/schema/index.js
CHANGED
|
@@ -26,18 +26,43 @@ var SubgraphTableSchema = z.object({
|
|
|
26
26
|
uniqueKeys: z.array(z.array(z.string())).optional()
|
|
27
27
|
});
|
|
28
28
|
var SubgraphSchemaSchema = z.record(z.string(), SubgraphTableSchema).refine((s) => Object.keys(s).length > 0, "Schema must have at least one table");
|
|
29
|
-
var
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
var VALID_FILTER_TYPES = [
|
|
30
|
+
"stx_transfer",
|
|
31
|
+
"stx_mint",
|
|
32
|
+
"stx_burn",
|
|
33
|
+
"stx_lock",
|
|
34
|
+
"ft_transfer",
|
|
35
|
+
"ft_mint",
|
|
36
|
+
"ft_burn",
|
|
37
|
+
"nft_transfer",
|
|
38
|
+
"nft_mint",
|
|
39
|
+
"nft_burn",
|
|
40
|
+
"contract_call",
|
|
41
|
+
"contract_deploy",
|
|
42
|
+
"print_event"
|
|
43
|
+
];
|
|
44
|
+
var SubgraphFilterSchema = z.object({
|
|
45
|
+
type: z.enum(VALID_FILTER_TYPES),
|
|
46
|
+
sender: z.string().optional(),
|
|
47
|
+
recipient: z.string().optional(),
|
|
48
|
+
minAmount: z.bigint().optional(),
|
|
49
|
+
maxAmount: z.bigint().optional(),
|
|
50
|
+
assetIdentifier: z.string().optional(),
|
|
51
|
+
contractId: z.string().optional(),
|
|
52
|
+
functionName: z.string().optional(),
|
|
53
|
+
caller: z.string().optional(),
|
|
54
|
+
deployer: z.string().optional(),
|
|
55
|
+
contractName: z.string().optional(),
|
|
56
|
+
topic: z.string().optional(),
|
|
57
|
+
lockedAddress: z.string().optional(),
|
|
58
|
+
abi: z.record(z.string(), z.any()).optional()
|
|
59
|
+
}).passthrough();
|
|
36
60
|
var SubgraphDefinitionSchema = z.object({
|
|
37
61
|
name: SubgraphNameSchema,
|
|
38
62
|
version: z.string().optional(),
|
|
39
63
|
description: z.string().optional(),
|
|
40
|
-
|
|
64
|
+
startBlock: z.number().int().nonnegative().optional(),
|
|
65
|
+
sources: z.record(z.string(), SubgraphFilterSchema).refine((s) => Object.keys(s).length > 0, "Must have at least one source"),
|
|
41
66
|
schema: SubgraphSchemaSchema,
|
|
42
67
|
handlers: z.record(z.string(), z.any())
|
|
43
68
|
});
|
|
@@ -308,5 +333,5 @@ export {
|
|
|
308
333
|
deploySchema
|
|
309
334
|
};
|
|
310
335
|
|
|
311
|
-
//# debugId=
|
|
336
|
+
//# debugId=EFAA4DDCC9F0F5E564756E2164756E21
|
|
312
337
|
//# sourceMappingURL=index.js.map
|