envio 3.1.1 → 3.2.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/evm.schema.json +83 -11
- package/fuel.schema.json +83 -11
- package/index.d.ts +184 -3
- package/package.json +6 -6
- package/src/Batch.res +2 -2
- package/src/ChainFetcher.res +27 -3
- package/src/ChainFetcher.res.mjs +17 -3
- package/src/ChainManager.res +163 -0
- package/src/ChainManager.res.mjs +136 -0
- package/src/Config.res +213 -30
- package/src/Config.res.mjs +102 -41
- package/src/Core.res +16 -10
- package/src/Ecosystem.res +0 -3
- package/src/Env.res +2 -2
- package/src/Env.res.mjs +2 -2
- package/src/Envio.res +101 -2
- package/src/Envio.res.mjs +2 -3
- package/src/EventConfigBuilder.res +52 -0
- package/src/EventConfigBuilder.res.mjs +32 -0
- package/src/EventUtils.res +2 -2
- package/src/FetchState.res +126 -71
- package/src/FetchState.res.mjs +73 -51
- package/src/GlobalState.res +219 -363
- package/src/GlobalState.res.mjs +314 -491
- package/src/GlobalStateManager.res +49 -59
- package/src/GlobalStateManager.res.mjs +5 -4
- package/src/GlobalStateManager.resi +1 -1
- package/src/HandlerLoader.res +12 -1
- package/src/HandlerLoader.res.mjs +6 -1
- package/src/HandlerRegister.res +9 -9
- package/src/HandlerRegister.res.mjs +9 -9
- package/src/Hasura.res +102 -32
- package/src/Hasura.res.mjs +88 -34
- package/src/InMemoryStore.res +10 -1
- package/src/InMemoryStore.res.mjs +4 -1
- package/src/InMemoryTable.res +83 -136
- package/src/InMemoryTable.res.mjs +57 -86
- package/src/Internal.res +54 -5
- package/src/Internal.res.mjs +2 -8
- package/src/LazyLoader.res +2 -2
- package/src/LazyLoader.res.mjs +3 -3
- package/src/LoadLayer.res +47 -60
- package/src/LoadLayer.res.mjs +28 -50
- package/src/LoadLayer.resi +2 -5
- package/src/LogSelection.res +4 -4
- package/src/LogSelection.res.mjs +5 -7
- package/src/Logging.res +1 -1
- package/src/Main.res +61 -2
- package/src/Main.res.mjs +37 -1
- package/src/Persistence.res +3 -16
- package/src/PgStorage.res +125 -114
- package/src/PgStorage.res.mjs +112 -95
- package/src/Ports.res +5 -0
- package/src/Ports.res.mjs +9 -0
- package/src/Prometheus.res +3 -3
- package/src/Prometheus.res.mjs +4 -4
- package/src/ReorgDetection.res +4 -4
- package/src/ReorgDetection.res.mjs +4 -5
- package/src/SafeCheckpointTracking.res +16 -16
- package/src/SafeCheckpointTracking.res.mjs +2 -2
- package/src/SimulateItems.res +10 -14
- package/src/SimulateItems.res.mjs +5 -2
- package/src/Sink.res +1 -1
- package/src/Sink.res.mjs +1 -2
- package/src/SvmTypes.res +9 -0
- package/src/SvmTypes.res.mjs +14 -0
- package/src/TestIndexer.res +17 -57
- package/src/TestIndexer.res.mjs +14 -48
- package/src/TestIndexerProxyStorage.res +23 -23
- package/src/TestIndexerProxyStorage.res.mjs +12 -15
- package/src/Throttler.res +2 -2
- package/src/Time.res +2 -2
- package/src/Time.res.mjs +2 -2
- package/src/UserContext.res +19 -118
- package/src/UserContext.res.mjs +10 -66
- package/src/Utils.res +15 -15
- package/src/Utils.res.mjs +7 -8
- package/src/adapters/MarkBatchProcessedAdapter.res +5 -0
- package/src/adapters/MarkBatchProcessedAdapter.res.mjs +14 -0
- package/src/bindings/BigDecimal.res +1 -1
- package/src/bindings/BigDecimal.res.mjs +2 -2
- package/src/bindings/ClickHouse.res +8 -6
- package/src/bindings/ClickHouse.res.mjs +5 -5
- package/src/bindings/Hrtime.res +1 -1
- package/src/bindings/Pino.res +2 -2
- package/src/bindings/Pino.res.mjs +3 -4
- package/src/db/EntityFilter.res +410 -0
- package/src/db/EntityFilter.res.mjs +424 -0
- package/src/db/EntityHistory.res +1 -1
- package/src/db/EntityHistory.res.mjs +1 -1
- package/src/db/InternalTable.res +10 -10
- package/src/db/InternalTable.res.mjs +41 -45
- package/src/db/Schema.res +2 -2
- package/src/db/Schema.res.mjs +3 -3
- package/src/db/Table.res +106 -22
- package/src/db/Table.res.mjs +84 -35
- package/src/sources/EventRouter.res +67 -2
- package/src/sources/EventRouter.res.mjs +45 -3
- package/src/sources/Evm.res +0 -7
- package/src/sources/Evm.res.mjs +0 -15
- package/src/sources/EvmChain.res +1 -1
- package/src/sources/EvmChain.res.mjs +1 -2
- package/src/sources/EvmRpcClient.res +42 -0
- package/src/sources/EvmRpcClient.res.mjs +64 -0
- package/src/sources/Fuel.res +0 -7
- package/src/sources/Fuel.res.mjs +0 -15
- package/src/sources/HyperFuelSource.res +5 -4
- package/src/sources/HyperFuelSource.res.mjs +2 -2
- package/src/sources/HyperSyncClient.res +9 -5
- package/src/sources/HyperSyncClient.res.mjs +2 -2
- package/src/sources/HyperSyncHeightStream.res +2 -2
- package/src/sources/HyperSyncHeightStream.res.mjs +2 -2
- package/src/sources/HyperSyncSource.res +10 -9
- package/src/sources/HyperSyncSource.res.mjs +4 -4
- package/src/sources/Rpc.res +1 -5
- package/src/sources/Rpc.res.mjs +1 -9
- package/src/sources/RpcSource.res +57 -21
- package/src/sources/RpcSource.res.mjs +47 -20
- package/src/sources/RpcWebSocketHeightStream.res +1 -1
- package/src/sources/SourceManager.res +3 -2
- package/src/sources/SourceManager.res.mjs +1 -1
- package/src/sources/Svm.res +3 -10
- package/src/sources/Svm.res.mjs +4 -18
- package/src/sources/SvmHyperSyncClient.res +265 -0
- package/src/sources/SvmHyperSyncClient.res.mjs +28 -0
- package/src/sources/SvmHyperSyncSource.res +638 -0
- package/src/sources/SvmHyperSyncSource.res.mjs +557 -0
- package/src/tui/Tui.res +9 -2
- package/src/tui/Tui.res.mjs +18 -3
- package/src/tui/components/BufferedProgressBar.res +2 -2
- package/src/tui/components/TuiData.res +3 -0
- package/svm.schema.json +523 -14
- package/src/TableIndices.res +0 -115
- package/src/TableIndices.res.mjs +0 -144
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Hrtime from "../bindings/Hrtime.res.mjs";
|
|
4
|
+
import * as Source from "./Source.res.mjs";
|
|
5
|
+
import * as Prometheus from "../Prometheus.res.mjs";
|
|
6
|
+
import * as EventRouter from "./EventRouter.res.mjs";
|
|
7
|
+
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
8
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
9
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
10
|
+
import * as SvmHyperSyncClient from "./SvmHyperSyncClient.res.mjs";
|
|
11
|
+
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
12
|
+
|
|
13
|
+
function buildInstructionSelections(eventConfigs) {
|
|
14
|
+
return eventConfigs.flatMap(cfg => {
|
|
15
|
+
let programIdString = cfg.programId;
|
|
16
|
+
if (programIdString === "") {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
let match = cfg.discriminator;
|
|
20
|
+
let match$1 = cfg.discriminatorByteLen;
|
|
21
|
+
let match$2;
|
|
22
|
+
if (match !== undefined) {
|
|
23
|
+
switch (match$1) {
|
|
24
|
+
case 1 :
|
|
25
|
+
match$2 = [
|
|
26
|
+
[match],
|
|
27
|
+
undefined,
|
|
28
|
+
undefined,
|
|
29
|
+
undefined
|
|
30
|
+
];
|
|
31
|
+
break;
|
|
32
|
+
case 2 :
|
|
33
|
+
match$2 = [
|
|
34
|
+
undefined,
|
|
35
|
+
[match],
|
|
36
|
+
undefined,
|
|
37
|
+
undefined
|
|
38
|
+
];
|
|
39
|
+
break;
|
|
40
|
+
case 4 :
|
|
41
|
+
match$2 = [
|
|
42
|
+
undefined,
|
|
43
|
+
undefined,
|
|
44
|
+
[match],
|
|
45
|
+
undefined
|
|
46
|
+
];
|
|
47
|
+
break;
|
|
48
|
+
case 8 :
|
|
49
|
+
match$2 = [
|
|
50
|
+
undefined,
|
|
51
|
+
undefined,
|
|
52
|
+
undefined,
|
|
53
|
+
[match]
|
|
54
|
+
];
|
|
55
|
+
break;
|
|
56
|
+
default:
|
|
57
|
+
match$2 = [
|
|
58
|
+
undefined,
|
|
59
|
+
undefined,
|
|
60
|
+
undefined,
|
|
61
|
+
undefined
|
|
62
|
+
];
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
match$2 = [
|
|
66
|
+
undefined,
|
|
67
|
+
undefined,
|
|
68
|
+
undefined,
|
|
69
|
+
undefined
|
|
70
|
+
];
|
|
71
|
+
}
|
|
72
|
+
let d8 = match$2[3];
|
|
73
|
+
let d4 = match$2[2];
|
|
74
|
+
let d2 = match$2[1];
|
|
75
|
+
let d1 = match$2[0];
|
|
76
|
+
let gs = cfg.accountFilters;
|
|
77
|
+
let groups = gs.length !== 0 ? gs : [[]];
|
|
78
|
+
return groups.map(group => {
|
|
79
|
+
let pick = position => Stdlib_Array.filterMap(group, f => {
|
|
80
|
+
if (f.position === position) {
|
|
81
|
+
return f.values;
|
|
82
|
+
}
|
|
83
|
+
})[0];
|
|
84
|
+
return {
|
|
85
|
+
programId: [programIdString],
|
|
86
|
+
d1: d1,
|
|
87
|
+
d2: d2,
|
|
88
|
+
d4: d4,
|
|
89
|
+
d8: d8,
|
|
90
|
+
a0: pick(0),
|
|
91
|
+
a1: pick(1),
|
|
92
|
+
a2: pick(2),
|
|
93
|
+
a3: pick(3),
|
|
94
|
+
a4: pick(4),
|
|
95
|
+
a5: pick(5),
|
|
96
|
+
isInner: cfg.isInner
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function synthLogIndex(instr) {
|
|
103
|
+
let tx = instr.transactionIndex;
|
|
104
|
+
let addrSum = Stdlib_Array.reduce(instr.instructionAddress, 0, (acc, n) => ((acc << 10) + n | 0) + 1 | 0);
|
|
105
|
+
return (tx << 16) + addrSum | 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function serializeInstructionAddress(addr) {
|
|
109
|
+
return addr.map(n => n.toString()).join(",");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function buildProgramSchemas(eventConfigs) {
|
|
113
|
+
let descriptorsByProgram = {};
|
|
114
|
+
eventConfigs.forEach(ec => {
|
|
115
|
+
let programIdString = ec.programId;
|
|
116
|
+
if (programIdString === "") {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
let hasSchema = ec.accounts.length !== 0 || ec.args !== null;
|
|
120
|
+
let discriminator = Stdlib_Option.getOr(ec.discriminator, "");
|
|
121
|
+
if (!(hasSchema && discriminator !== "")) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
let other = ec.definedTypes;
|
|
125
|
+
let definedTypes;
|
|
126
|
+
definedTypes = other === null ? ({}) : other;
|
|
127
|
+
let existing = descriptorsByProgram[programIdString];
|
|
128
|
+
let descriptor = existing !== undefined ? Primitive_option.valFromOption(existing) : ({
|
|
129
|
+
programId: programIdString,
|
|
130
|
+
definedTypes: definedTypes,
|
|
131
|
+
instructions: []
|
|
132
|
+
});
|
|
133
|
+
let instruction = {
|
|
134
|
+
name: ec.name,
|
|
135
|
+
discriminator: discriminator,
|
|
136
|
+
accounts: ec.accounts,
|
|
137
|
+
args: ec.args
|
|
138
|
+
};
|
|
139
|
+
descriptorsByProgram[programIdString] = {
|
|
140
|
+
programId: descriptor.programId,
|
|
141
|
+
definedTypes: descriptor.definedTypes,
|
|
142
|
+
instructions: descriptor.instructions.concat([instruction])
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
return Object.values(descriptorsByProgram).map(descriptor => JSON.stringify(descriptor));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function parseDecoded(d) {
|
|
149
|
+
let args;
|
|
150
|
+
try {
|
|
151
|
+
args = JSON.parse(d.argsJson);
|
|
152
|
+
} catch (exn) {
|
|
153
|
+
args = {};
|
|
154
|
+
}
|
|
155
|
+
let accounts;
|
|
156
|
+
try {
|
|
157
|
+
accounts = JSON.parse(d.accountsJson);
|
|
158
|
+
} catch (exn$1) {
|
|
159
|
+
accounts = {};
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
name: d.name,
|
|
163
|
+
args: args,
|
|
164
|
+
accounts: accounts,
|
|
165
|
+
extraAccounts: d.extraAccounts
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function toSvmInstruction(instr, programName, instructionName, transaction, logs, block) {
|
|
170
|
+
return {
|
|
171
|
+
programName: programName,
|
|
172
|
+
instructionName: instructionName,
|
|
173
|
+
programId: instr.programId,
|
|
174
|
+
data: instr.data,
|
|
175
|
+
accounts: instr.accounts,
|
|
176
|
+
instructionAddress: instr.instructionAddress,
|
|
177
|
+
isInner: instr.isInner,
|
|
178
|
+
d1: instr.d1,
|
|
179
|
+
d2: instr.d2,
|
|
180
|
+
d4: instr.d4,
|
|
181
|
+
d8: instr.d8,
|
|
182
|
+
params: Stdlib_Option.map(instr.decoded, parseDecoded),
|
|
183
|
+
transaction: transaction,
|
|
184
|
+
logs: logs,
|
|
185
|
+
block: block
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function toSvmTransaction(tx) {
|
|
190
|
+
return {
|
|
191
|
+
signatures: tx.signatures,
|
|
192
|
+
feePayer: Stdlib_Option.map(tx.feePayer, prim => prim),
|
|
193
|
+
success: tx.success,
|
|
194
|
+
err: tx.err,
|
|
195
|
+
fee: Stdlib_Option.map(tx.fee, prim => BigInt(prim)),
|
|
196
|
+
computeUnitsConsumed: Stdlib_Option.map(tx.computeUnitsConsumed, prim => BigInt(prim)),
|
|
197
|
+
accountKeys: tx.accountKeys,
|
|
198
|
+
recentBlockhash: tx.recentBlockhash,
|
|
199
|
+
version: tx.version
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function toSvmTokenBalance(tb) {
|
|
204
|
+
return {
|
|
205
|
+
account: Stdlib_Option.map(tb.account, prim => prim),
|
|
206
|
+
mint: Stdlib_Option.map(tb.mint, prim => prim),
|
|
207
|
+
owner: Stdlib_Option.map(tb.owner, prim => prim),
|
|
208
|
+
preAmount: tb.preAmount,
|
|
209
|
+
postAmount: tb.postAmount
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function probeRouter(router, programId, instr, byteLengthsDesc, contractAddress, indexingAddresses) {
|
|
214
|
+
let probe = dN => {
|
|
215
|
+
let tag = EventRouter.getSvmEventId(programId, dN);
|
|
216
|
+
return EventRouter.get(router, tag, contractAddress, instr.slot, indexingAddresses);
|
|
217
|
+
};
|
|
218
|
+
let result = Stdlib_Array.reduce(byteLengthsDesc, undefined, (acc, len) => {
|
|
219
|
+
if (acc !== undefined) {
|
|
220
|
+
return acc;
|
|
221
|
+
}
|
|
222
|
+
let candidate;
|
|
223
|
+
switch (len) {
|
|
224
|
+
case 1 :
|
|
225
|
+
candidate = instr.d1;
|
|
226
|
+
break;
|
|
227
|
+
case 2 :
|
|
228
|
+
candidate = instr.d2;
|
|
229
|
+
break;
|
|
230
|
+
case 4 :
|
|
231
|
+
candidate = instr.d4;
|
|
232
|
+
break;
|
|
233
|
+
case 8 :
|
|
234
|
+
candidate = instr.d8;
|
|
235
|
+
break;
|
|
236
|
+
default:
|
|
237
|
+
candidate = undefined;
|
|
238
|
+
}
|
|
239
|
+
if (candidate !== undefined) {
|
|
240
|
+
return probe(candidate);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
if (result !== undefined) {
|
|
244
|
+
return result;
|
|
245
|
+
} else {
|
|
246
|
+
return probe(undefined);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function make(param) {
|
|
251
|
+
let eventConfigs = param.eventConfigs;
|
|
252
|
+
let chain = param.chain;
|
|
253
|
+
let name = "SvmHyperSync";
|
|
254
|
+
let programSchemas = buildProgramSchemas(eventConfigs);
|
|
255
|
+
let client = SvmHyperSyncClient.make(param.endpointUrl, param.apiToken, param.clientTimeoutMillis, undefined, undefined, undefined, programSchemas.length !== 0 ? programSchemas : undefined);
|
|
256
|
+
let match = EventRouter.fromSvmEventConfigsOrThrow(eventConfigs, chain);
|
|
257
|
+
let eventRouter = match[0];
|
|
258
|
+
let orderingByProgram = {};
|
|
259
|
+
match[1].forEach(o => {
|
|
260
|
+
orderingByProgram[o.programId] = o.byteLengthsDesc;
|
|
261
|
+
});
|
|
262
|
+
let needsTransactions = eventConfigs.some(cfg => cfg.includeTransaction);
|
|
263
|
+
let needsLogs = eventConfigs.some(cfg => cfg.includeLogs);
|
|
264
|
+
let needsTokenBalances = eventConfigs.some(cfg => cfg.includeTokenBalances);
|
|
265
|
+
let getItemsOrThrow = async (fromBlock, toBlock, param, indexingAddresses, knownHeight, param$1, param$2, retry, logger) => {
|
|
266
|
+
let totalTimeRef = Hrtime.makeTimer();
|
|
267
|
+
let pageFetchRef = Hrtime.makeTimer();
|
|
268
|
+
let instructionSelections = buildInstructionSelections(eventConfigs);
|
|
269
|
+
let fields_block = [
|
|
270
|
+
"slot",
|
|
271
|
+
"blockhash",
|
|
272
|
+
"block_time"
|
|
273
|
+
];
|
|
274
|
+
let fields_transaction = needsTransactions ? [
|
|
275
|
+
"slot",
|
|
276
|
+
"transaction_index",
|
|
277
|
+
"signatures",
|
|
278
|
+
"fee_payer",
|
|
279
|
+
"success",
|
|
280
|
+
"err",
|
|
281
|
+
"fee",
|
|
282
|
+
"compute_units_consumed",
|
|
283
|
+
"account_keys",
|
|
284
|
+
"recent_blockhash",
|
|
285
|
+
"version"
|
|
286
|
+
] : undefined;
|
|
287
|
+
let fields_log = needsLogs ? [
|
|
288
|
+
"slot",
|
|
289
|
+
"transaction_index",
|
|
290
|
+
"instruction_address",
|
|
291
|
+
"kind",
|
|
292
|
+
"message"
|
|
293
|
+
] : undefined;
|
|
294
|
+
let fields_tokenBalance = needsTokenBalances ? [
|
|
295
|
+
"slot",
|
|
296
|
+
"transaction_index",
|
|
297
|
+
"account",
|
|
298
|
+
"mint",
|
|
299
|
+
"owner",
|
|
300
|
+
"pre_amount",
|
|
301
|
+
"post_amount"
|
|
302
|
+
] : undefined;
|
|
303
|
+
let fields = {
|
|
304
|
+
block: fields_block,
|
|
305
|
+
transaction: fields_transaction,
|
|
306
|
+
log: fields_log,
|
|
307
|
+
tokenBalance: fields_tokenBalance
|
|
308
|
+
};
|
|
309
|
+
let query_toSlot = Stdlib_Option.map(toBlock, toBlock => toBlock + 1 | 0);
|
|
310
|
+
let query_instructions = instructionSelections;
|
|
311
|
+
let query_fields = fields;
|
|
312
|
+
let query = {
|
|
313
|
+
fromSlot: fromBlock,
|
|
314
|
+
toSlot: query_toSlot,
|
|
315
|
+
instructions: query_instructions,
|
|
316
|
+
fields: query_fields
|
|
317
|
+
};
|
|
318
|
+
Prometheus.SourceRequestCount.increment(name, chain, "getInstructions");
|
|
319
|
+
let resp;
|
|
320
|
+
try {
|
|
321
|
+
resp = await client.get(query);
|
|
322
|
+
} catch (raw_exn) {
|
|
323
|
+
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
324
|
+
throw {
|
|
325
|
+
RE_EXN_ID: Source.GetItemsError,
|
|
326
|
+
_1: {
|
|
327
|
+
TAG: "FailedGettingItems",
|
|
328
|
+
exn: exn,
|
|
329
|
+
attemptedToBlock: Stdlib_Option.getOr(toBlock, knownHeight),
|
|
330
|
+
retry: {
|
|
331
|
+
TAG: "WithBackoff",
|
|
332
|
+
message: `Unexpected issue while fetching instructions from SVM HyperSync. Attempt a retry.`,
|
|
333
|
+
backoffMillis: retry !== 0 ? 1000 * retry | 0 : 500
|
|
334
|
+
}
|
|
335
|
+
},
|
|
336
|
+
Error: new Error()
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
let pageFetchTime = Hrtime.toSecondsFloat(Hrtime.timeSince(pageFetchRef));
|
|
340
|
+
let parsingRef = Hrtime.makeTimer();
|
|
341
|
+
let blockTimeBySlot = {};
|
|
342
|
+
resp.data.blocks.forEach(b => {
|
|
343
|
+
let t = b.blockTime;
|
|
344
|
+
if (t !== undefined) {
|
|
345
|
+
blockTimeBySlot[b.slot.toString()] = t;
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
let txByKey = {};
|
|
350
|
+
resp.data.transactions.forEach(tx => {
|
|
351
|
+
let key = tx.slot.toString() + ":" + tx.transactionIndex.toString();
|
|
352
|
+
txByKey[key] = tx;
|
|
353
|
+
});
|
|
354
|
+
let logsByKey = {};
|
|
355
|
+
resp.data.logs.forEach(log => {
|
|
356
|
+
let match = log.transactionIndex;
|
|
357
|
+
let match$1 = log.instructionAddress;
|
|
358
|
+
if (match === undefined) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
if (match$1 === undefined) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
let key = log.slot.toString() + ":" + match.toString() + ":" + serializeInstructionAddress(match$1);
|
|
365
|
+
let existing = logsByKey[key];
|
|
366
|
+
if (existing !== undefined) {
|
|
367
|
+
existing.push(log);
|
|
368
|
+
} else {
|
|
369
|
+
logsByKey[key] = [log];
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
let tokenBalancesByTx = {};
|
|
373
|
+
if (needsTokenBalances) {
|
|
374
|
+
resp.data.tokenBalances.forEach(tb => {
|
|
375
|
+
let txIdx = tb.transactionIndex;
|
|
376
|
+
if (txIdx === undefined) {
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
let key = tb.slot.toString() + ":" + txIdx.toString();
|
|
380
|
+
let existing = tokenBalancesByTx[key];
|
|
381
|
+
if (existing !== undefined) {
|
|
382
|
+
existing.push(tb);
|
|
383
|
+
} else {
|
|
384
|
+
tokenBalancesByTx[key] = [tb];
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
let parsedQueueItems = [];
|
|
389
|
+
resp.data.instructions.forEach(instr => {
|
|
390
|
+
let programId = instr.programId;
|
|
391
|
+
let byteLengths = Stdlib_Option.getOr(orderingByProgram[instr.programId], []);
|
|
392
|
+
let contractAddress = instr.programId;
|
|
393
|
+
let maybeConfig = probeRouter(eventRouter, programId, instr, byteLengths, contractAddress, indexingAddresses);
|
|
394
|
+
if (maybeConfig !== undefined) {
|
|
395
|
+
let txKey = instr.slot.toString() + ":" + instr.transactionIndex.toString();
|
|
396
|
+
let maybeTx = Stdlib_Option.map(txByKey[txKey], toSvmTransaction);
|
|
397
|
+
let maybeTx$1 = maybeConfig.includeTokenBalances ? Stdlib_Option.map(maybeTx, tx => {
|
|
398
|
+
let maybeBalances = Stdlib_Option.map(tokenBalancesByTx[txKey], bals => bals.map(toSvmTokenBalance));
|
|
399
|
+
let newrecord = {...tx};
|
|
400
|
+
newrecord.tokenBalances = maybeBalances;
|
|
401
|
+
return newrecord;
|
|
402
|
+
}) : maybeTx;
|
|
403
|
+
let logKey = instr.slot.toString() + ":" + instr.transactionIndex.toString() + ":" + serializeInstructionAddress(instr.instructionAddress);
|
|
404
|
+
let maybeLogs = Stdlib_Option.map(logsByKey[logKey], logs => logs.map(log => ({
|
|
405
|
+
kind: Stdlib_Option.getOr(log.kind, ""),
|
|
406
|
+
message: Stdlib_Option.getOr(log.message, "")
|
|
407
|
+
})));
|
|
408
|
+
let slotKey = instr.slot.toString();
|
|
409
|
+
let blockTime = blockTimeBySlot[slotKey];
|
|
410
|
+
let payload = toSvmInstruction(instr, maybeConfig.contractName, maybeConfig.name, maybeConfig.includeTransaction ? maybeTx$1 : undefined, maybeConfig.includeLogs ? maybeLogs : undefined, {
|
|
411
|
+
slot: instr.slot,
|
|
412
|
+
time: Stdlib_Option.getOr(blockTime, 0),
|
|
413
|
+
hash: ""
|
|
414
|
+
});
|
|
415
|
+
parsedQueueItems.push({
|
|
416
|
+
kind: 0,
|
|
417
|
+
eventConfig: maybeConfig,
|
|
418
|
+
timestamp: Stdlib_Option.getOr(blockTime, 0),
|
|
419
|
+
chain: chain,
|
|
420
|
+
blockNumber: instr.slot,
|
|
421
|
+
blockHash: "",
|
|
422
|
+
logIndex: synthLogIndex(instr),
|
|
423
|
+
event: payload
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
let parsingTimeElapsed = Hrtime.toSecondsFloat(Hrtime.timeSince(parsingRef));
|
|
428
|
+
let highestSlot = resp.nextSlot - 1 | 0;
|
|
429
|
+
let latestBlockTime = Stdlib_Option.getOr(blockTimeBySlot[highestSlot.toString()], 0);
|
|
430
|
+
let blockHashes = resp.data.blocks.map(b => ({
|
|
431
|
+
blockHash: b.blockhash,
|
|
432
|
+
blockNumber: b.slot
|
|
433
|
+
}));
|
|
434
|
+
let totalTimeElapsed = Hrtime.toSecondsFloat(Hrtime.timeSince(totalTimeRef));
|
|
435
|
+
return {
|
|
436
|
+
knownHeight: knownHeight,
|
|
437
|
+
blockHashes: blockHashes,
|
|
438
|
+
parsedQueueItems: parsedQueueItems,
|
|
439
|
+
fromBlockQueried: fromBlock,
|
|
440
|
+
latestFetchedBlockNumber: highestSlot,
|
|
441
|
+
latestFetchedBlockTimestamp: latestBlockTime,
|
|
442
|
+
stats: {
|
|
443
|
+
"total time elapsed (s)": totalTimeElapsed,
|
|
444
|
+
"parsing time (s)": parsingTimeElapsed,
|
|
445
|
+
"page fetch time (s)": pageFetchTime
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
};
|
|
449
|
+
let queryBlockDataRange = async (fromSlot, toSlot) => {
|
|
450
|
+
let blockDatas = [];
|
|
451
|
+
let fromRef = fromSlot;
|
|
452
|
+
let keepGoing = true;
|
|
453
|
+
while (keepGoing) {
|
|
454
|
+
let query_toSlot = toSlot + 1 | 0;
|
|
455
|
+
let query_includeAllBlocks = true;
|
|
456
|
+
let query_fields = {
|
|
457
|
+
block: [
|
|
458
|
+
"slot",
|
|
459
|
+
"blockhash",
|
|
460
|
+
"block_time"
|
|
461
|
+
]
|
|
462
|
+
};
|
|
463
|
+
let query_maxNumBlocks = 1000;
|
|
464
|
+
let query = {
|
|
465
|
+
fromSlot: fromRef,
|
|
466
|
+
toSlot: query_toSlot,
|
|
467
|
+
includeAllBlocks: query_includeAllBlocks,
|
|
468
|
+
fields: query_fields,
|
|
469
|
+
maxNumBlocks: query_maxNumBlocks
|
|
470
|
+
};
|
|
471
|
+
Prometheus.SourceRequestCount.increment(name, chain, "getBlockHashes");
|
|
472
|
+
let resp = await client.get(query);
|
|
473
|
+
resp.data.blocks.forEach(b => {
|
|
474
|
+
blockDatas.push({
|
|
475
|
+
blockHash: b.blockhash,
|
|
476
|
+
blockNumber: b.slot,
|
|
477
|
+
blockTimestamp: Stdlib_Option.getOr(b.blockTime, 0)
|
|
478
|
+
});
|
|
479
|
+
});
|
|
480
|
+
if (resp.nextSlot > toSlot || resp.nextSlot <= fromRef) {
|
|
481
|
+
keepGoing = false;
|
|
482
|
+
} else {
|
|
483
|
+
fromRef = resp.nextSlot;
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
return blockDatas;
|
|
487
|
+
};
|
|
488
|
+
let getBlockHashes = async (blockNumbers, param) => {
|
|
489
|
+
let firstSlot = blockNumbers[0];
|
|
490
|
+
if (firstSlot === undefined) {
|
|
491
|
+
return {
|
|
492
|
+
TAG: "Ok",
|
|
493
|
+
_0: []
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
try {
|
|
497
|
+
let minSlot = {
|
|
498
|
+
contents: firstSlot
|
|
499
|
+
};
|
|
500
|
+
let maxSlot = {
|
|
501
|
+
contents: firstSlot
|
|
502
|
+
};
|
|
503
|
+
let requested = new Set();
|
|
504
|
+
blockNumbers.forEach(slot => {
|
|
505
|
+
if (slot < minSlot.contents) {
|
|
506
|
+
minSlot.contents = slot;
|
|
507
|
+
}
|
|
508
|
+
if (slot > maxSlot.contents) {
|
|
509
|
+
maxSlot.contents = slot;
|
|
510
|
+
}
|
|
511
|
+
requested.add(slot);
|
|
512
|
+
});
|
|
513
|
+
let blockDatas = await queryBlockDataRange(minSlot.contents, maxSlot.contents);
|
|
514
|
+
return {
|
|
515
|
+
TAG: "Ok",
|
|
516
|
+
_0: blockDatas.filter(data => requested.delete(data.blockNumber))
|
|
517
|
+
};
|
|
518
|
+
} catch (raw_exn) {
|
|
519
|
+
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
520
|
+
return {
|
|
521
|
+
TAG: "Error",
|
|
522
|
+
_0: exn
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
return {
|
|
527
|
+
name: name,
|
|
528
|
+
sourceFor: "Sync",
|
|
529
|
+
chain: chain,
|
|
530
|
+
poweredByHyperSync: true,
|
|
531
|
+
pollingInterval: 1000,
|
|
532
|
+
getBlockHashes: getBlockHashes,
|
|
533
|
+
getHeightOrThrow: async () => {
|
|
534
|
+
let timer = Hrtime.makeTimer();
|
|
535
|
+
let h = await client.getHeight();
|
|
536
|
+
let seconds = Hrtime.toSecondsFloat(Hrtime.timeSince(timer));
|
|
537
|
+
Prometheus.SourceRequestCount.increment(name, chain, "getHeight");
|
|
538
|
+
Prometheus.SourceRequestCount.addSeconds(name, chain, "getHeight", seconds);
|
|
539
|
+
return h;
|
|
540
|
+
},
|
|
541
|
+
getItemsOrThrow: getItemsOrThrow
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
export {
|
|
546
|
+
buildInstructionSelections,
|
|
547
|
+
synthLogIndex,
|
|
548
|
+
serializeInstructionAddress,
|
|
549
|
+
buildProgramSchemas,
|
|
550
|
+
parseDecoded,
|
|
551
|
+
toSvmInstruction,
|
|
552
|
+
toSvmTransaction,
|
|
553
|
+
toSvmTokenBalance,
|
|
554
|
+
probeRouter,
|
|
555
|
+
make,
|
|
556
|
+
}
|
|
557
|
+
/* Prometheus Not a pure module */
|
package/src/tui/Tui.res
CHANGED
|
@@ -13,6 +13,7 @@ module ChainLine = {
|
|
|
13
13
|
~endBlock,
|
|
14
14
|
~poweredByHyperSync,
|
|
15
15
|
~eventsProcessed,
|
|
16
|
+
~blockUnit: string,
|
|
16
17
|
) => {
|
|
17
18
|
let chainsWidth = Pervasives.min(stdoutColumns - 2, 60)
|
|
18
19
|
let headerWidth = maxChainIdLength + 10 // 10 for additional text
|
|
@@ -27,9 +28,10 @@ module ChainLine = {
|
|
|
27
28
|
let toBlockStr = toBlock->TuiData.formatLocaleString
|
|
28
29
|
let eventsStr = eventsProcessed->TuiData.formatFloatLocaleString
|
|
29
30
|
|
|
31
|
+
let endLabel = ` (End ${blockUnit})`
|
|
30
32
|
let blocksText =
|
|
31
|
-
|
|
32
|
-
(endBlock->Option.isSome ?
|
|
33
|
+
`${blockUnit}s: ${progressBlockStr} / ${toBlockStr}` ++
|
|
34
|
+
(endBlock->Option.isSome ? endLabel : "") ++ ` `
|
|
33
35
|
let eventsText = `Events: ${eventsStr}`
|
|
34
36
|
|
|
35
37
|
let fitsSameLine = blocksText->String.length + eventsText->String.length <= chainsWidth
|
|
@@ -213,6 +215,10 @@ module App = {
|
|
|
213
215
|
poweredByHyperSync: (
|
|
214
216
|
cf.sourceManager->SourceManager.getActiveSource
|
|
215
217
|
).poweredByHyperSync,
|
|
218
|
+
blockUnit: switch state.ctx.config.ecosystem.name {
|
|
219
|
+
| Svm => "Slot"
|
|
220
|
+
| Evm | Fuel => "Block"
|
|
221
|
+
},
|
|
216
222
|
rateLimitTimeMs: cf.sourceManager->SourceManager.getRateLimitTimeMs,
|
|
217
223
|
isRateLimited: cf.sourceManager->SourceManager.isRateLimited,
|
|
218
224
|
rateLimitResetInMs: cf.sourceManager->SourceManager.getRateLimitResetInMs,
|
|
@@ -255,6 +261,7 @@ module App = {
|
|
|
255
261
|
stdoutColumns={stdoutColumns}
|
|
256
262
|
poweredByHyperSync={chainData.poweredByHyperSync}
|
|
257
263
|
eventsProcessed={chainData.eventsProcessed}
|
|
264
|
+
blockUnit={chainData.blockUnit}
|
|
258
265
|
/>
|
|
259
266
|
})
|
|
260
267
|
->React.array}
|
package/src/tui/Tui.res.mjs
CHANGED
|
@@ -20,6 +20,7 @@ import * as JsxRuntime from "react/jsx-runtime";
|
|
|
20
20
|
import * as BufferedProgressBar from "./components/BufferedProgressBar.res.mjs";
|
|
21
21
|
|
|
22
22
|
function Tui$ChainLine(props) {
|
|
23
|
+
let blockUnit = props.blockUnit;
|
|
23
24
|
let poweredByHyperSync = props.poweredByHyperSync;
|
|
24
25
|
let endBlock = props.endBlock;
|
|
25
26
|
let startBlock = props.startBlock;
|
|
@@ -34,8 +35,9 @@ function Tui$ChainLine(props) {
|
|
|
34
35
|
let progressBlockStr = TuiData.formatLocaleString(progressBlock);
|
|
35
36
|
let toBlockStr = TuiData.formatLocaleString(toBlock);
|
|
36
37
|
let eventsStr = TuiData.formatFloatLocaleString(props.eventsProcessed);
|
|
37
|
-
let
|
|
38
|
-
|
|
38
|
+
let endLabel = ` (End ` + blockUnit + `)`;
|
|
39
|
+
let blocksText = blockUnit + `s: ` + progressBlockStr + ` / ` + toBlockStr + (
|
|
40
|
+
Stdlib_Option.isSome(endBlock) ? endLabel : ""
|
|
39
41
|
) + ` `;
|
|
40
42
|
let eventsText = `Events: ` + eventsStr;
|
|
41
43
|
let fitsSameLine = (blocksText.length + eventsText.length | 0) <= chainsWidth;
|
|
@@ -243,6 +245,17 @@ function Tui$App(props) {
|
|
|
243
245
|
})
|
|
244
246
|
) : "SearchingForEvents";
|
|
245
247
|
}
|
|
248
|
+
let match$1 = state.ctx.config.ecosystem.name;
|
|
249
|
+
let tmp;
|
|
250
|
+
switch (match$1) {
|
|
251
|
+
case "evm" :
|
|
252
|
+
case "fuel" :
|
|
253
|
+
tmp = "Block";
|
|
254
|
+
break;
|
|
255
|
+
case "svm" :
|
|
256
|
+
tmp = "Slot";
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
246
259
|
return {
|
|
247
260
|
chainId: cf.chainConfig.id.toString(),
|
|
248
261
|
eventsProcessed: numEventsProcessed,
|
|
@@ -256,6 +269,7 @@ function Tui$App(props) {
|
|
|
256
269
|
progress: progress,
|
|
257
270
|
latestFetchedBlockNumber: latestFetchedBlockNumber,
|
|
258
271
|
knownHeight: knownHeight,
|
|
272
|
+
blockUnit: tmp,
|
|
259
273
|
rateLimitTimeMs: SourceManager.getRateLimitTimeMs(cf.sourceManager),
|
|
260
274
|
isRateLimited: SourceManager.isRateLimited(cf.sourceManager),
|
|
261
275
|
rateLimitResetInMs: SourceManager.getRateLimitResetInMs(cf.sourceManager)
|
|
@@ -339,7 +353,8 @@ function Tui$App(props) {
|
|
|
339
353
|
startBlock: chainData.startBlock,
|
|
340
354
|
endBlock: chainData.endBlock,
|
|
341
355
|
poweredByHyperSync: chainData.poweredByHyperSync,
|
|
342
|
-
eventsProcessed: chainData.eventsProcessed
|
|
356
|
+
eventsProcessed: chainData.eventsProcessed,
|
|
357
|
+
blockUnit: chainData.blockUnit
|
|
343
358
|
}, i.toString())),
|
|
344
359
|
JsxRuntime.jsx(Tui$TotalEventsProcessed, {
|
|
345
360
|
totalEventsProcessed: totalEventsProcessed,
|
|
@@ -6,14 +6,14 @@ let make = (~loaded, ~buffered=?, ~outOf, ~barWidth=36, ~loadingColor=Style.Seco
|
|
|
6
6
|
|
|
7
7
|
let loadedFraction = loaded->Int.toFloat /. outOf->Int.toFloat
|
|
8
8
|
let loadedCount = Pervasives.min(
|
|
9
|
-
Math.floor(maxCount->Int.toFloat *. loadedFraction)->
|
|
9
|
+
Math.floor(maxCount->Int.toFloat *. loadedFraction)->Float.toInt,
|
|
10
10
|
maxCount,
|
|
11
11
|
)
|
|
12
12
|
|
|
13
13
|
let bufferedCount = buffered->Option.mapOr(loadedCount, buffered => {
|
|
14
14
|
let bufferedFraction = buffered->Int.toFloat /. outOf->Int.toFloat
|
|
15
15
|
Pervasives.min(
|
|
16
|
-
Math.floor(maxCount->Int.toFloat *. bufferedFraction)->
|
|
16
|
+
Math.floor(maxCount->Int.toFloat *. bufferedFraction)->Float.toInt,
|
|
17
17
|
maxCount,
|
|
18
18
|
)
|
|
19
19
|
})
|
|
@@ -30,6 +30,9 @@ type chain = {
|
|
|
30
30
|
progress: progress,
|
|
31
31
|
latestFetchedBlockNumber: int,
|
|
32
32
|
knownHeight: int,
|
|
33
|
+
/** Localized unit noun for this chain's progress. `"Slots"` on SVM,
|
|
34
|
+
`"Blocks"` everywhere else. Drives the per-chain progress label. */
|
|
35
|
+
blockUnit: string,
|
|
33
36
|
rateLimitTimeMs: float,
|
|
34
37
|
isRateLimited: bool,
|
|
35
38
|
rateLimitResetInMs: option<float>,
|