envio 3.3.0-alpha.0 → 3.3.0-alpha.2
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/package.json +6 -6
- package/src/Batch.res +0 -6
- package/src/Batch.res.mjs +0 -11
- package/src/BatchProcessing.res +2 -12
- package/src/BatchProcessing.res.mjs +2 -7
- package/src/ChainFetching.res +22 -13
- package/src/ChainFetching.res.mjs +13 -13
- package/src/ChainMetadata.res +1 -11
- package/src/ChainMetadata.res.mjs +1 -10
- package/src/ChainState.res +176 -2
- package/src/ChainState.res.mjs +153 -9
- package/src/ChainState.resi +53 -1
- package/src/Config.res +3 -6
- package/src/Config.res.mjs +2 -3
- package/src/Core.res +7 -0
- package/src/CrossChainState.res +99 -91
- package/src/CrossChainState.res.mjs +72 -57
- package/src/CrossChainState.resi +1 -9
- package/src/Ecosystem.res +6 -1
- package/src/Env.res +0 -7
- package/src/Env.res.mjs +0 -6
- package/src/Envio.res +3 -2
- package/src/EventConfigBuilder.res +19 -5
- package/src/EventConfigBuilder.res.mjs +6 -4
- package/src/EventProcessing.res +26 -0
- package/src/EventProcessing.res.mjs +20 -0
- package/src/FetchState.res +92 -22
- package/src/FetchState.res.mjs +87 -14
- package/src/IndexerLoop.res +2 -3
- package/src/IndexerLoop.res.mjs +1 -1
- package/src/IndexerState.res +0 -8
- package/src/IndexerState.res.mjs +4 -8
- package/src/IndexerState.resi +0 -4
- package/src/Internal.res +43 -4
- package/src/Internal.res.mjs +18 -0
- package/src/Main.res +3 -50
- package/src/Main.res.mjs +3 -24
- package/src/Prometheus.res +0 -11
- package/src/Prometheus.res.mjs +83 -98
- package/src/Rollback.res +1 -1
- package/src/Rollback.res.mjs +1 -1
- package/src/SimulateItems.res +3 -0
- package/src/SimulateItems.res.mjs +2 -1
- package/src/sources/Evm.res +52 -38
- package/src/sources/Evm.res.mjs +48 -36
- package/src/sources/Fuel.res +3 -1
- package/src/sources/Fuel.res.mjs +1 -0
- package/src/sources/HyperFuelSource.res +5 -0
- package/src/sources/HyperFuelSource.res.mjs +2 -0
- package/src/sources/HyperSync.res +4 -1
- package/src/sources/HyperSync.res.mjs +5 -3
- package/src/sources/HyperSync.resi +1 -0
- package/src/sources/HyperSyncClient.res +9 -78
- package/src/sources/HyperSyncClient.res.mjs +1 -22
- package/src/sources/HyperSyncSource.res +3 -4
- package/src/sources/HyperSyncSource.res.mjs +2 -1
- package/src/sources/RpcSource.res +7 -2
- package/src/sources/RpcSource.res.mjs +3 -1
- package/src/sources/SimulateSource.res +3 -1
- package/src/sources/SimulateSource.res.mjs +1 -0
- package/src/sources/Source.res +4 -0
- package/src/sources/SourceManager.res +9 -8
- package/src/sources/SourceManager.res.mjs +21 -26
- package/src/sources/SourceManager.resi +2 -3
- package/src/sources/Svm.res +25 -2
- package/src/sources/Svm.res.mjs +27 -2
- package/src/sources/SvmHyperSyncClient.res +3 -29
- package/src/sources/SvmHyperSyncSource.res +60 -84
- package/src/sources/SvmHyperSyncSource.res.mjs +59 -78
- package/src/sources/TransactionStore.res +111 -0
- package/src/sources/TransactionStore.res.mjs +77 -0
- package/src/tui/Tui.res +13 -16
- package/src/tui/Tui.res.mjs +12 -14
package/src/sources/Svm.res
CHANGED
|
@@ -4,10 +4,32 @@ let cleanUpRawEventFieldsInPlace: JSON.t => unit = %raw(`fields => {
|
|
|
4
4
|
delete fields.time
|
|
5
5
|
}`)
|
|
6
6
|
|
|
7
|
+
// Ordered transaction field names. The index of each is the field code shared
|
|
8
|
+
// with the Rust store (`SvmTxField`) — keep this order in sync.
|
|
9
|
+
let transactionFields = [
|
|
10
|
+
"transactionIndex",
|
|
11
|
+
"signatures",
|
|
12
|
+
"feePayer",
|
|
13
|
+
"success",
|
|
14
|
+
"err",
|
|
15
|
+
"fee",
|
|
16
|
+
"computeUnitsConsumed",
|
|
17
|
+
"accountKeys",
|
|
18
|
+
"recentBlockhash",
|
|
19
|
+
"version",
|
|
20
|
+
"tokenBalances",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
// Field name → bit index (the code shared with the Rust store), built once.
|
|
24
|
+
let transactionFieldCodes = TransactionStore.fieldCodes(transactionFields)
|
|
25
|
+
|
|
26
|
+
let transactionFieldMask = (eventConfigs: array<Internal.eventConfig>): float =>
|
|
27
|
+
eventConfigs->TransactionStore.mask(~codes=transactionFieldCodes)
|
|
28
|
+
|
|
7
29
|
let make = (~logger: Pino.t): Ecosystem.t => {
|
|
8
30
|
name: Svm,
|
|
9
31
|
blockFields: ["slot"],
|
|
10
|
-
transactionFields
|
|
32
|
+
transactionFields,
|
|
11
33
|
blockNumberName: "height",
|
|
12
34
|
blockTimestampName: "time",
|
|
13
35
|
blockHashName: "hash",
|
|
@@ -20,7 +42,8 @@ let make = (~logger: Pino.t): Ecosystem.t => {
|
|
|
20
42
|
// parse. The schema is a no-op object that always surfaces `None`.
|
|
21
43
|
onEventBlockFilterSchema: S.object(_ => None),
|
|
22
44
|
logger,
|
|
23
|
-
|
|
45
|
+
transactionFieldMask,
|
|
46
|
+
toEvent: eventItem => eventItem.payload->(Utils.magic: Internal.eventPayload => Internal.event),
|
|
24
47
|
toEventLogger: eventItem => {
|
|
25
48
|
let instruction =
|
|
26
49
|
eventItem.payload->(Utils.magic: Internal.eventPayload => Envio.svmInstruction)
|
package/src/sources/Svm.res.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import * as Logging from "../Logging.res.mjs";
|
|
|
8
8
|
import * as Prometheus from "../Prometheus.res.mjs";
|
|
9
9
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
10
10
|
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
11
|
+
import * as TransactionStore from "./TransactionStore.res.mjs";
|
|
11
12
|
|
|
12
13
|
let cleanUpRawEventFieldsInPlace = (fields => {
|
|
13
14
|
delete fields.hash
|
|
@@ -15,11 +16,31 @@ let cleanUpRawEventFieldsInPlace = (fields => {
|
|
|
15
16
|
delete fields.time
|
|
16
17
|
});
|
|
17
18
|
|
|
19
|
+
let transactionFields = [
|
|
20
|
+
"transactionIndex",
|
|
21
|
+
"signatures",
|
|
22
|
+
"feePayer",
|
|
23
|
+
"success",
|
|
24
|
+
"err",
|
|
25
|
+
"fee",
|
|
26
|
+
"computeUnitsConsumed",
|
|
27
|
+
"accountKeys",
|
|
28
|
+
"recentBlockhash",
|
|
29
|
+
"version",
|
|
30
|
+
"tokenBalances"
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
let transactionFieldCodes = TransactionStore.fieldCodes(transactionFields);
|
|
34
|
+
|
|
35
|
+
function transactionFieldMask(eventConfigs) {
|
|
36
|
+
return TransactionStore.mask(eventConfigs, transactionFieldCodes);
|
|
37
|
+
}
|
|
38
|
+
|
|
18
39
|
function make(logger) {
|
|
19
40
|
return {
|
|
20
41
|
name: "svm",
|
|
21
42
|
blockFields: ["slot"],
|
|
22
|
-
transactionFields:
|
|
43
|
+
transactionFields: transactionFields,
|
|
23
44
|
blockNumberName: "height",
|
|
24
45
|
blockTimestampName: "time",
|
|
25
46
|
blockHashName: "hash",
|
|
@@ -29,6 +50,7 @@ function make(logger) {
|
|
|
29
50
|
onEventBlockFilterSchema: S$RescriptSchema.object(param => {}),
|
|
30
51
|
logger: logger,
|
|
31
52
|
toEvent: eventItem => eventItem.payload,
|
|
53
|
+
transactionFieldMask: transactionFieldMask,
|
|
32
54
|
toEventLogger: eventItem => {
|
|
33
55
|
let instruction = eventItem.payload;
|
|
34
56
|
return Logging.createChildFrom(logger, {
|
|
@@ -80,8 +102,11 @@ function makeRPCSource(chain, rpc, sourceForOpt) {
|
|
|
80
102
|
|
|
81
103
|
export {
|
|
82
104
|
cleanUpRawEventFieldsInPlace,
|
|
105
|
+
transactionFields,
|
|
106
|
+
transactionFieldCodes,
|
|
107
|
+
transactionFieldMask,
|
|
83
108
|
make,
|
|
84
109
|
GetFinalizedSlot,
|
|
85
110
|
makeRPCSource,
|
|
86
111
|
}
|
|
87
|
-
/*
|
|
112
|
+
/* transactionFieldCodes Not a pure module */
|
|
@@ -147,22 +147,6 @@ module ResponseTypes = {
|
|
|
147
147
|
blockHeight?: int,
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
type transaction = {
|
|
151
|
-
slot: int,
|
|
152
|
-
transactionIndex: int,
|
|
153
|
-
signatures: array<string>,
|
|
154
|
-
feePayer?: string,
|
|
155
|
-
success?: bool,
|
|
156
|
-
err?: string,
|
|
157
|
-
fee?: int,
|
|
158
|
-
computeUnitsConsumed?: int,
|
|
159
|
-
accountKeys: array<string>,
|
|
160
|
-
recentBlockhash?: string,
|
|
161
|
-
version?: string,
|
|
162
|
-
loadedAddressesWritable: array<string>,
|
|
163
|
-
loadedAddressesReadonly: array<string>,
|
|
164
|
-
}
|
|
165
|
-
|
|
166
150
|
/// Borsh-decoded view attached by the Rust client. `argsJson`/`accountsJson`
|
|
167
151
|
/// are stringified to side-step napi-rs's lack of native JSON passthrough.
|
|
168
152
|
/** Solana instruction record.
|
|
@@ -204,22 +188,10 @@ module ResponseTypes = {
|
|
|
204
188
|
message?: string,
|
|
205
189
|
}
|
|
206
190
|
|
|
207
|
-
type tokenBalance = {
|
|
208
|
-
slot: int,
|
|
209
|
-
transactionIndex?: int,
|
|
210
|
-
account?: string,
|
|
211
|
-
mint?: string,
|
|
212
|
-
owner?: string,
|
|
213
|
-
preAmount?: string,
|
|
214
|
-
postAmount?: string,
|
|
215
|
-
}
|
|
216
|
-
|
|
217
191
|
type queryResponseData = {
|
|
218
192
|
blocks: array<block>,
|
|
219
|
-
transactions: array<transaction>,
|
|
220
193
|
instructions: array<instruction>,
|
|
221
194
|
logs: array<log>,
|
|
222
|
-
tokenBalances: array<tokenBalance>,
|
|
223
195
|
}
|
|
224
196
|
|
|
225
197
|
type queryResponse = {
|
|
@@ -234,7 +206,9 @@ type queryResponse = ResponseTypes.queryResponse
|
|
|
234
206
|
|
|
235
207
|
type t = {
|
|
236
208
|
getHeight: unit => promise<int>,
|
|
237
|
-
|
|
209
|
+
// Returns the response plus a page of raw transactions (kept in Rust),
|
|
210
|
+
// keyed by (slot, transactionIndex), materialised at batch prep.
|
|
211
|
+
get: (~query: query) => promise<(queryResponse, TransactionStore.t)>,
|
|
238
212
|
}
|
|
239
213
|
|
|
240
214
|
@send
|
|
@@ -188,7 +188,6 @@ let toSvmInstruction = (
|
|
|
188
188
|
instr: SvmHyperSyncClient.ResponseTypes.instruction,
|
|
189
189
|
~programName,
|
|
190
190
|
~instructionName,
|
|
191
|
-
~transaction,
|
|
192
191
|
~logs,
|
|
193
192
|
~block,
|
|
194
193
|
): Envio.svmInstruction => {
|
|
@@ -204,35 +203,10 @@ let toSvmInstruction = (
|
|
|
204
203
|
d4: ?instr.d4,
|
|
205
204
|
d8: ?instr.d8,
|
|
206
205
|
params: ?(instr.decoded->Option.map(parseDecoded)),
|
|
207
|
-
?transaction,
|
|
208
206
|
?logs,
|
|
209
207
|
block,
|
|
210
208
|
}
|
|
211
209
|
|
|
212
|
-
let toSvmTransaction = (tx: SvmHyperSyncClient.ResponseTypes.transaction): Envio.svmTransaction => {
|
|
213
|
-
signatures: tx.signatures,
|
|
214
|
-
accountKeys: tx.accountKeys->SvmTypes.Pubkey.fromStringsUnsafe,
|
|
215
|
-
feePayer: ?(tx.feePayer->Option.map(SvmTypes.Pubkey.fromStringUnsafe)),
|
|
216
|
-
success: ?tx.success,
|
|
217
|
-
err: ?tx.err,
|
|
218
|
-
// u64 lamports / compute units arrive as `int` over napi. Convert to
|
|
219
|
-
// `bigint` so the public type stays defensible even for pathological values.
|
|
220
|
-
fee: ?(tx.fee->Option.map(BigInt.fromInt)),
|
|
221
|
-
computeUnitsConsumed: ?(tx.computeUnitsConsumed->Option.map(BigInt.fromInt)),
|
|
222
|
-
recentBlockhash: ?tx.recentBlockhash,
|
|
223
|
-
version: ?tx.version,
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
let toSvmTokenBalance = (
|
|
227
|
-
tb: SvmHyperSyncClient.ResponseTypes.tokenBalance,
|
|
228
|
-
): Envio.svmTokenBalance => {
|
|
229
|
-
account: ?(tb.account->Option.map(SvmTypes.Pubkey.fromStringUnsafe)),
|
|
230
|
-
mint: ?(tb.mint->Option.map(SvmTypes.Pubkey.fromStringUnsafe)),
|
|
231
|
-
owner: ?(tb.owner->Option.map(SvmTypes.Pubkey.fromStringUnsafe)),
|
|
232
|
-
preAmount: ?tb.preAmount,
|
|
233
|
-
postAmount: ?tb.postAmount,
|
|
234
|
-
}
|
|
235
|
-
|
|
236
210
|
// Probe the discriminator byte-length ordering longest-first. Stops at the
|
|
237
211
|
// first router hit. Falls back to the `_none` key (program-wide handler) when
|
|
238
212
|
// no discriminator-keyed handler matches.
|
|
@@ -273,6 +247,27 @@ let probeRouter = (
|
|
|
273
247
|
}
|
|
274
248
|
}
|
|
275
249
|
|
|
250
|
+
// Map a selected transaction field to the extra query-side column it needs.
|
|
251
|
+
// `transactionIndex` is always fetched as the store key, and `tokenBalances`
|
|
252
|
+
// lives in a separate table (requested via `needsTokenBalances`), so neither
|
|
253
|
+
// adds a transaction column here.
|
|
254
|
+
let toQueryTxField = (field: Internal.svmTransactionField): option<
|
|
255
|
+
SvmHyperSyncClient.QueryTypes.transactionField,
|
|
256
|
+
> =>
|
|
257
|
+
switch field {
|
|
258
|
+
| TransactionIndex => None
|
|
259
|
+
| Signatures => Some(Signatures)
|
|
260
|
+
| FeePayer => Some(FeePayer)
|
|
261
|
+
| Success => Some(Success)
|
|
262
|
+
| Err => Some(Err)
|
|
263
|
+
| Fee => Some(Fee)
|
|
264
|
+
| ComputeUnitsConsumed => Some(ComputeUnitsConsumed)
|
|
265
|
+
| AccountKeys => Some(AccountKeys)
|
|
266
|
+
| RecentBlockhash => Some(RecentBlockhash)
|
|
267
|
+
| Version => Some(Version)
|
|
268
|
+
| TokenBalances => None
|
|
269
|
+
}
|
|
270
|
+
|
|
276
271
|
let make = ({chain, endpointUrl, apiToken, eventConfigs, clientTimeoutMillis}: options): t => {
|
|
277
272
|
let name = "SvmHyperSync"
|
|
278
273
|
let chainId = chain->ChainMap.Chain.toChainId
|
|
@@ -298,9 +293,38 @@ let make = ({chain, endpointUrl, apiToken, eventConfigs, clientTimeoutMillis}: o
|
|
|
298
293
|
orderingByProgram->Dict.set(o.programId->SvmTypes.Pubkey.toString, o.byteLengthsDesc)
|
|
299
294
|
)
|
|
300
295
|
|
|
301
|
-
let needsTransactions = eventConfigs->Array.some(cfg => cfg.includeTransaction)
|
|
302
296
|
let needsLogs = eventConfigs->Array.some(cfg => cfg.includeLogs)
|
|
303
|
-
|
|
297
|
+
|
|
298
|
+
// Union of selected transaction fields across the chain's events. Drives both
|
|
299
|
+
// the query column selection (fetch only what's used) and the materialisation
|
|
300
|
+
// mask. `slot`/`transactionIndex` are always fetched as the store key.
|
|
301
|
+
let selectedTxFields = Utils.Set.make()
|
|
302
|
+
eventConfigs->Array.forEach(cfg =>
|
|
303
|
+
cfg.selectedTransactionFields
|
|
304
|
+
->(Utils.magic: Utils.Set.t<string> => Utils.Set.t<Internal.svmTransactionField>)
|
|
305
|
+
->Utils.Set.forEach(field => selectedTxFields->Utils.Set.add(field)->ignore)
|
|
306
|
+
)
|
|
307
|
+
let needsTokenBalances = selectedTxFields->Utils.Set.has(Internal.TokenBalances)
|
|
308
|
+
let txQueryFields = {
|
|
309
|
+
// Slot + TransactionIndex are always fetched so the store can be keyed by
|
|
310
|
+
// (slot, transactionIndex).
|
|
311
|
+
let fields: array<SvmHyperSyncClient.QueryTypes.transactionField> = [Slot, TransactionIndex]
|
|
312
|
+
selectedTxFields
|
|
313
|
+
->Utils.Set.toArray
|
|
314
|
+
->Array.forEach(field =>
|
|
315
|
+
switch toQueryTxField(field) {
|
|
316
|
+
| Some(queryField) => fields->Array.push(queryField)
|
|
317
|
+
| None => ()
|
|
318
|
+
}
|
|
319
|
+
)
|
|
320
|
+
fields
|
|
321
|
+
}
|
|
322
|
+
// Every selected field except `tokenBalances` (its own table) is read off a
|
|
323
|
+
// stored transaction record, so the transaction table must be fetched to
|
|
324
|
+
// populate the store — even when only `transactionIndex` (a key column) is
|
|
325
|
+
// selected.
|
|
326
|
+
let txFieldCount = selectedTxFields->Utils.Set.size
|
|
327
|
+
let needsTransactions = txFieldCount > 1 || (txFieldCount === 1 && !needsTokenBalances)
|
|
304
328
|
|
|
305
329
|
let getItemsOrThrow = async (
|
|
306
330
|
~fromBlock,
|
|
@@ -323,23 +347,7 @@ let make = ({chain, endpointUrl, apiToken, eventConfigs, clientTimeoutMillis}: o
|
|
|
323
347
|
// opted-into table needs its columns spelled out here.
|
|
324
348
|
let fields: SvmHyperSyncClient.QueryTypes.fieldSelection = {
|
|
325
349
|
block: [Slot, Blockhash, BlockTime],
|
|
326
|
-
transaction: ?(
|
|
327
|
-
needsTransactions
|
|
328
|
-
? Some([
|
|
329
|
-
Slot,
|
|
330
|
-
TransactionIndex,
|
|
331
|
-
Signatures,
|
|
332
|
-
FeePayer,
|
|
333
|
-
Success,
|
|
334
|
-
Err,
|
|
335
|
-
Fee,
|
|
336
|
-
ComputeUnitsConsumed,
|
|
337
|
-
AccountKeys,
|
|
338
|
-
RecentBlockhash,
|
|
339
|
-
Version,
|
|
340
|
-
])
|
|
341
|
-
: None
|
|
342
|
-
),
|
|
350
|
+
transaction: ?(needsTransactions ? Some(txQueryFields) : None),
|
|
343
351
|
log: ?(needsLogs ? Some([Slot, TransactionIndex, InstructionAddress, Kind, Message]) : None),
|
|
344
352
|
tokenBalance: ?(
|
|
345
353
|
needsTokenBalances
|
|
@@ -358,7 +366,7 @@ let make = ({chain, endpointUrl, apiToken, eventConfigs, clientTimeoutMillis}: o
|
|
|
358
366
|
|
|
359
367
|
Prometheus.SourceRequestCount.increment(~sourceName=name, ~chainId, ~method="getInstructions")
|
|
360
368
|
|
|
361
|
-
let resp = try await client.get(~query) catch {
|
|
369
|
+
let (resp, transactionStore) = try await client.get(~query) catch {
|
|
362
370
|
| exn =>
|
|
363
371
|
throw(
|
|
364
372
|
Source.GetItemsError(
|
|
@@ -390,13 +398,6 @@ let make = ({chain, endpointUrl, apiToken, eventConfigs, clientTimeoutMillis}: o
|
|
|
390
398
|
}
|
|
391
399
|
})
|
|
392
400
|
|
|
393
|
-
// Per (slot, transaction_index) lookup for parent transactions.
|
|
394
|
-
let txByKey = Dict.make()
|
|
395
|
-
resp.data.transactions->Array.forEach(tx => {
|
|
396
|
-
let key = tx.slot->Int.toString ++ ":" ++ tx.transactionIndex->Int.toString
|
|
397
|
-
txByKey->Dict.set(key, tx)
|
|
398
|
-
})
|
|
399
|
-
|
|
400
401
|
// Per (slot, transaction_index, instruction_address) lookup for logs
|
|
401
402
|
// scoped to a single instruction. `instructionAddress: None` logs are
|
|
402
403
|
// attached to no instruction (rare; usually only system messages).
|
|
@@ -418,21 +419,6 @@ let make = ({chain, endpointUrl, apiToken, eventConfigs, clientTimeoutMillis}: o
|
|
|
418
419
|
}
|
|
419
420
|
})
|
|
420
421
|
|
|
421
|
-
let tokenBalancesByTx = Dict.make()
|
|
422
|
-
if needsTokenBalances {
|
|
423
|
-
resp.data.tokenBalances->Array.forEach(tb => {
|
|
424
|
-
switch tb.transactionIndex {
|
|
425
|
-
| Some(txIdx) =>
|
|
426
|
-
let key = tb.slot->Int.toString ++ ":" ++ txIdx->Int.toString
|
|
427
|
-
switch tokenBalancesByTx->Dict.get(key) {
|
|
428
|
-
| Some(existing) => existing->Array.push(tb)
|
|
429
|
-
| None => tokenBalancesByTx->Dict.set(key, [tb])
|
|
430
|
-
}
|
|
431
|
-
| None => ()
|
|
432
|
-
}
|
|
433
|
-
})
|
|
434
|
-
}
|
|
435
|
-
|
|
436
422
|
let parsedQueueItems = []
|
|
437
423
|
resp.data.instructions->Array.forEach(instr => {
|
|
438
424
|
let programId = instr.programId->SvmTypes.Pubkey.fromStringUnsafe
|
|
@@ -454,20 +440,6 @@ let make = ({chain, endpointUrl, apiToken, eventConfigs, clientTimeoutMillis}: o
|
|
|
454
440
|
switch maybeConfig {
|
|
455
441
|
| None => ()
|
|
456
442
|
| Some(eventConfig) =>
|
|
457
|
-
let txKey = instr.slot->Int.toString ++ ":" ++ instr.transactionIndex->Int.toString
|
|
458
|
-
let maybeTx =
|
|
459
|
-
txByKey->Utils.Dict.dangerouslyGetNonOption(txKey)->Option.map(toSvmTransaction)
|
|
460
|
-
let maybeTx = if eventConfig.includeTokenBalances {
|
|
461
|
-
maybeTx->Option.map(tx => {
|
|
462
|
-
let maybeBalances =
|
|
463
|
-
tokenBalancesByTx
|
|
464
|
-
->Utils.Dict.dangerouslyGetNonOption(txKey)
|
|
465
|
-
->Option.map(bals => bals->Array.map(toSvmTokenBalance))
|
|
466
|
-
{...tx, tokenBalances: ?maybeBalances}
|
|
467
|
-
})
|
|
468
|
-
} else {
|
|
469
|
-
maybeTx
|
|
470
|
-
}
|
|
471
443
|
let logKey =
|
|
472
444
|
instr.slot->Int.toString ++
|
|
473
445
|
":" ++
|
|
@@ -492,7 +464,6 @@ let make = ({chain, endpointUrl, apiToken, eventConfigs, clientTimeoutMillis}: o
|
|
|
492
464
|
instr,
|
|
493
465
|
~programName=eventConfig.contractName,
|
|
494
466
|
~instructionName=eventConfig.name,
|
|
495
|
-
~transaction=eventConfig.includeTransaction ? maybeTx : None,
|
|
496
467
|
~logs=eventConfig.includeLogs ? maybeLogs : None,
|
|
497
468
|
~block={
|
|
498
469
|
slot: instr.slot,
|
|
@@ -510,6 +481,8 @@ let make = ({chain, endpointUrl, apiToken, eventConfigs, clientTimeoutMillis}: o
|
|
|
510
481
|
blockNumber: instr.slot,
|
|
511
482
|
blockHash: "",
|
|
512
483
|
logIndex: synthLogIndex(instr),
|
|
484
|
+
// The parent transaction is materialised from the store at batch prep.
|
|
485
|
+
transactionIndex: instr.transactionIndex,
|
|
513
486
|
payload: payload->(Utils.magic: Envio.svmInstruction => Internal.eventPayload),
|
|
514
487
|
}),
|
|
515
488
|
)
|
|
@@ -539,6 +512,8 @@ let make = ({chain, endpointUrl, apiToken, eventConfigs, clientTimeoutMillis}: o
|
|
|
539
512
|
{
|
|
540
513
|
latestFetchedBlockTimestamp: latestBlockTime,
|
|
541
514
|
parsedQueueItems,
|
|
515
|
+
// Raw transactions kept in Rust; materialised (selected fields) at batch prep.
|
|
516
|
+
transactionStore: Some(transactionStore),
|
|
542
517
|
latestFetchedBlockNumber: highestSlot,
|
|
543
518
|
stats: {totalTimeElapsed, parsingTimeElapsed, pageFetchTime},
|
|
544
519
|
knownHeight,
|
|
@@ -563,7 +538,8 @@ let make = ({chain, endpointUrl, apiToken, eventConfigs, clientTimeoutMillis}: o
|
|
|
563
538
|
maxNumBlocks: 1000,
|
|
564
539
|
}
|
|
565
540
|
Prometheus.SourceRequestCount.increment(~sourceName=name, ~chainId, ~method="getBlockHashes")
|
|
566
|
-
|
|
541
|
+
// Block-only query; the store page is empty.
|
|
542
|
+
let (resp, _) = await client.get(~query)
|
|
567
543
|
resp.data.blocks->Array.forEach(b =>
|
|
568
544
|
blockDatas
|
|
569
545
|
->Array.push({
|
|
@@ -166,7 +166,7 @@ function parseDecoded(d) {
|
|
|
166
166
|
};
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
function toSvmInstruction(instr, programName, instructionName,
|
|
169
|
+
function toSvmInstruction(instr, programName, instructionName, logs, block) {
|
|
170
170
|
return {
|
|
171
171
|
programName: programName,
|
|
172
172
|
instructionName: instructionName,
|
|
@@ -180,36 +180,11 @@ function toSvmInstruction(instr, programName, instructionName, transaction, logs
|
|
|
180
180
|
d4: instr.d4,
|
|
181
181
|
d8: instr.d8,
|
|
182
182
|
params: Stdlib_Option.map(instr.decoded, parseDecoded),
|
|
183
|
-
transaction: transaction,
|
|
184
183
|
logs: logs,
|
|
185
184
|
block: block
|
|
186
185
|
};
|
|
187
186
|
}
|
|
188
187
|
|
|
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
188
|
function probeRouter(router, programId, instr, byteLengthsDesc, contractAddress, indexingAddresses) {
|
|
214
189
|
let probe = dN => {
|
|
215
190
|
let tag = EventRouter.getSvmEventId(programId, dN);
|
|
@@ -247,6 +222,32 @@ function probeRouter(router, programId, instr, byteLengthsDesc, contractAddress,
|
|
|
247
222
|
}
|
|
248
223
|
}
|
|
249
224
|
|
|
225
|
+
function toQueryTxField(field) {
|
|
226
|
+
switch (field) {
|
|
227
|
+
case "signatures" :
|
|
228
|
+
return "signatures";
|
|
229
|
+
case "feePayer" :
|
|
230
|
+
return "fee_payer";
|
|
231
|
+
case "success" :
|
|
232
|
+
return "success";
|
|
233
|
+
case "err" :
|
|
234
|
+
return "err";
|
|
235
|
+
case "fee" :
|
|
236
|
+
return "fee";
|
|
237
|
+
case "computeUnitsConsumed" :
|
|
238
|
+
return "compute_units_consumed";
|
|
239
|
+
case "accountKeys" :
|
|
240
|
+
return "account_keys";
|
|
241
|
+
case "recentBlockhash" :
|
|
242
|
+
return "recent_blockhash";
|
|
243
|
+
case "version" :
|
|
244
|
+
return "version";
|
|
245
|
+
case "transactionIndex" :
|
|
246
|
+
case "tokenBalances" :
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
250
251
|
function make(param) {
|
|
251
252
|
let eventConfigs = param.eventConfigs;
|
|
252
253
|
let chain = param.chain;
|
|
@@ -259,9 +260,27 @@ function make(param) {
|
|
|
259
260
|
match[1].forEach(o => {
|
|
260
261
|
orderingByProgram[o.programId] = o.byteLengthsDesc;
|
|
261
262
|
});
|
|
262
|
-
let needsTransactions = eventConfigs.some(cfg => cfg.includeTransaction);
|
|
263
263
|
let needsLogs = eventConfigs.some(cfg => cfg.includeLogs);
|
|
264
|
-
let
|
|
264
|
+
let selectedTxFields = new Set();
|
|
265
|
+
eventConfigs.forEach(cfg => {
|
|
266
|
+
cfg.selectedTransactionFields.forEach(field => {
|
|
267
|
+
selectedTxFields.add(field);
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
let needsTokenBalances = selectedTxFields.has("tokenBalances");
|
|
271
|
+
let fields = [
|
|
272
|
+
"slot",
|
|
273
|
+
"transaction_index"
|
|
274
|
+
];
|
|
275
|
+
Array.from(selectedTxFields).forEach(field => {
|
|
276
|
+
let queryField = toQueryTxField(field);
|
|
277
|
+
if (queryField !== undefined) {
|
|
278
|
+
fields.push(queryField);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
let txFieldCount = selectedTxFields.size;
|
|
283
|
+
let needsTransactions = txFieldCount > 1 || txFieldCount === 1 && !needsTokenBalances;
|
|
265
284
|
let getItemsOrThrow = async (fromBlock, toBlock, param, indexingAddresses, knownHeight, param$1, param$2, retry, logger) => {
|
|
266
285
|
let totalTimeRef = Hrtime.makeTimer();
|
|
267
286
|
let pageFetchRef = Hrtime.makeTimer();
|
|
@@ -271,19 +290,7 @@ function make(param) {
|
|
|
271
290
|
"blockhash",
|
|
272
291
|
"block_time"
|
|
273
292
|
];
|
|
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;
|
|
293
|
+
let fields_transaction = needsTransactions ? fields : undefined;
|
|
287
294
|
let fields_log = needsLogs ? [
|
|
288
295
|
"slot",
|
|
289
296
|
"transaction_index",
|
|
@@ -300,7 +307,7 @@ function make(param) {
|
|
|
300
307
|
"pre_amount",
|
|
301
308
|
"post_amount"
|
|
302
309
|
] : undefined;
|
|
303
|
-
let fields = {
|
|
310
|
+
let fields$1 = {
|
|
304
311
|
block: fields_block,
|
|
305
312
|
transaction: fields_transaction,
|
|
306
313
|
log: fields_log,
|
|
@@ -308,7 +315,7 @@ function make(param) {
|
|
|
308
315
|
};
|
|
309
316
|
let query_toSlot = Stdlib_Option.map(toBlock, toBlock => toBlock + 1 | 0);
|
|
310
317
|
let query_instructions = instructionSelections;
|
|
311
|
-
let query_fields = fields;
|
|
318
|
+
let query_fields = fields$1;
|
|
312
319
|
let query = {
|
|
313
320
|
fromSlot: fromBlock,
|
|
314
321
|
toSlot: query_toSlot,
|
|
@@ -316,9 +323,9 @@ function make(param) {
|
|
|
316
323
|
fields: query_fields
|
|
317
324
|
};
|
|
318
325
|
Prometheus.SourceRequestCount.increment(name, chain, "getInstructions");
|
|
319
|
-
let
|
|
326
|
+
let match;
|
|
320
327
|
try {
|
|
321
|
-
|
|
328
|
+
match = await client.get(query);
|
|
322
329
|
} catch (raw_exn) {
|
|
323
330
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
324
331
|
throw {
|
|
@@ -336,6 +343,7 @@ function make(param) {
|
|
|
336
343
|
Error: new Error()
|
|
337
344
|
};
|
|
338
345
|
}
|
|
346
|
+
let resp = match[0];
|
|
339
347
|
let pageFetchTime = Hrtime.toSecondsFloat(Hrtime.timeSince(pageFetchRef));
|
|
340
348
|
let parsingRef = Hrtime.makeTimer();
|
|
341
349
|
let blockTimeBySlot = {};
|
|
@@ -346,11 +354,6 @@ function make(param) {
|
|
|
346
354
|
return;
|
|
347
355
|
}
|
|
348
356
|
});
|
|
349
|
-
let txByKey = {};
|
|
350
|
-
resp.data.transactions.forEach(tx => {
|
|
351
|
-
let key = tx.slot.toString() + ":" + tx.transactionIndex.toString();
|
|
352
|
-
txByKey[key] = tx;
|
|
353
|
-
});
|
|
354
357
|
let logsByKey = {};
|
|
355
358
|
resp.data.logs.forEach(log => {
|
|
356
359
|
let match = log.transactionIndex;
|
|
@@ -369,22 +372,6 @@ function make(param) {
|
|
|
369
372
|
logsByKey[key] = [log];
|
|
370
373
|
}
|
|
371
374
|
});
|
|
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
375
|
let parsedQueueItems = [];
|
|
389
376
|
resp.data.instructions.forEach(instr => {
|
|
390
377
|
let programId = instr.programId;
|
|
@@ -392,14 +379,6 @@ function make(param) {
|
|
|
392
379
|
let contractAddress = instr.programId;
|
|
393
380
|
let maybeConfig = probeRouter(eventRouter, programId, instr, byteLengths, contractAddress, indexingAddresses);
|
|
394
381
|
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
382
|
let logKey = instr.slot.toString() + ":" + instr.transactionIndex.toString() + ":" + serializeInstructionAddress(instr.instructionAddress);
|
|
404
383
|
let maybeLogs = Stdlib_Option.map(logsByKey[logKey], logs => logs.map(log => ({
|
|
405
384
|
kind: Stdlib_Option.getOr(log.kind, ""),
|
|
@@ -407,7 +386,7 @@ function make(param) {
|
|
|
407
386
|
})));
|
|
408
387
|
let slotKey = instr.slot.toString();
|
|
409
388
|
let blockTime = blockTimeBySlot[slotKey];
|
|
410
|
-
let payload = toSvmInstruction(instr, maybeConfig.contractName, maybeConfig.name, maybeConfig.
|
|
389
|
+
let payload = toSvmInstruction(instr, maybeConfig.contractName, maybeConfig.name, maybeConfig.includeLogs ? maybeLogs : undefined, {
|
|
411
390
|
slot: instr.slot,
|
|
412
391
|
time: Stdlib_Option.getOr(blockTime, 0),
|
|
413
392
|
hash: ""
|
|
@@ -420,6 +399,7 @@ function make(param) {
|
|
|
420
399
|
blockNumber: instr.slot,
|
|
421
400
|
blockHash: "",
|
|
422
401
|
logIndex: synthLogIndex(instr),
|
|
402
|
+
transactionIndex: instr.transactionIndex,
|
|
423
403
|
payload: payload
|
|
424
404
|
});
|
|
425
405
|
}
|
|
@@ -436,6 +416,7 @@ function make(param) {
|
|
|
436
416
|
knownHeight: knownHeight,
|
|
437
417
|
blockHashes: blockHashes,
|
|
438
418
|
parsedQueueItems: parsedQueueItems,
|
|
419
|
+
transactionStore: Primitive_option.some(match[1]),
|
|
439
420
|
fromBlockQueried: fromBlock,
|
|
440
421
|
latestFetchedBlockNumber: highestSlot,
|
|
441
422
|
latestFetchedBlockTimestamp: latestBlockTime,
|
|
@@ -469,7 +450,8 @@ function make(param) {
|
|
|
469
450
|
maxNumBlocks: query_maxNumBlocks
|
|
470
451
|
};
|
|
471
452
|
Prometheus.SourceRequestCount.increment(name, chain, "getBlockHashes");
|
|
472
|
-
let
|
|
453
|
+
let match = await client.get(query);
|
|
454
|
+
let resp = match[0];
|
|
473
455
|
resp.data.blocks.forEach(b => {
|
|
474
456
|
blockDatas.push({
|
|
475
457
|
blockHash: b.blockhash,
|
|
@@ -549,9 +531,8 @@ export {
|
|
|
549
531
|
buildProgramSchemas,
|
|
550
532
|
parseDecoded,
|
|
551
533
|
toSvmInstruction,
|
|
552
|
-
toSvmTransaction,
|
|
553
|
-
toSvmTokenBalance,
|
|
554
534
|
probeRouter,
|
|
535
|
+
toQueryTxField,
|
|
555
536
|
make,
|
|
556
537
|
}
|
|
557
538
|
/* Prometheus Not a pure module */
|