envio 3.6.1 → 3.7.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/index.d.ts +197 -9
- package/package.json +6 -6
- package/src/Batch.res +64 -25
- package/src/Batch.res.mjs +69 -46
- package/src/ChainFetching.res +13 -17
- package/src/ChainFetching.res.mjs +9 -14
- package/src/ChainState.res +160 -119
- package/src/ChainState.res.mjs +75 -46
- package/src/ChainState.resi +15 -12
- package/src/Config.res +9 -2
- package/src/Config.res.mjs +4 -4
- package/src/Core.res +5 -0
- package/src/Ecosystem.res +0 -1
- package/src/Envio.res +16 -3
- package/src/EventConfigBuilder.res +155 -40
- package/src/EventConfigBuilder.res.mjs +76 -24
- package/src/EventProcessing.res +4 -8
- package/src/EventProcessing.res.mjs +4 -4
- package/src/EventUtils.res +3 -47
- package/src/FetchState.res +124 -104
- package/src/FetchState.res.mjs +116 -100
- package/src/HandlerRegister.res +108 -2
- package/src/HandlerRegister.res.mjs +59 -10
- package/src/HandlerRegister.resi +4 -0
- package/src/Internal.res +84 -24
- package/src/Internal.res.mjs +33 -2
- package/src/LazyLoader.res +12 -11
- package/src/LazyLoader.res.mjs +13 -7
- package/src/Main.res +22 -9
- package/src/Main.res.mjs +18 -5
- package/src/MemoryStorage.res +724 -0
- package/src/MemoryStorage.res.mjs +601 -0
- package/src/PgStorage.res +18 -7
- package/src/PgStorage.res.mjs +14 -11
- package/src/ReorgDetection.res +0 -222
- package/src/ReorgDetection.res.mjs +0 -163
- package/src/Rollback.res +14 -15
- package/src/Rollback.res.mjs +4 -5
- package/src/SimulateItems.res +2 -2
- package/src/Utils.res +3 -0
- package/src/bindings/ClickHouse.res +16 -11
- package/src/bindings/ClickHouse.res.mjs +10 -10
- package/src/bindings/Vitest.res +1 -1
- package/src/sources/BlockStore.res +115 -5
- package/src/sources/BlockStore.res.mjs +25 -0
- package/src/sources/Evm.res +0 -1
- package/src/sources/Evm.res.mjs +0 -1
- package/src/sources/EvmHyperSyncSource.res +19 -84
- package/src/sources/EvmHyperSyncSource.res.mjs +24 -60
- package/src/sources/Fuel.res +24 -4
- package/src/sources/Fuel.res.mjs +23 -3
- package/src/sources/FuelHyperSync.res +8 -3
- package/src/sources/FuelHyperSync.res.mjs +5 -4
- package/src/sources/FuelHyperSync.resi +3 -1
- package/src/sources/FuelHyperSyncClient.res +7 -10
- package/src/sources/FuelHyperSyncSource.res +18 -45
- package/src/sources/FuelHyperSyncSource.res.mjs +18 -35
- package/src/sources/HyperSync.res +49 -229
- package/src/sources/HyperSync.res.mjs +74 -191
- package/src/sources/HyperSync.resi +11 -35
- package/src/sources/HyperSyncClient.res +9 -79
- package/src/sources/HyperSyncClient.res.mjs +2 -6
- package/src/sources/RequestStat.res +5 -0
- package/src/sources/RequestStat.res.mjs +2 -0
- package/src/sources/RpcSource.res +149 -43
- package/src/sources/RpcSource.res.mjs +104 -41
- package/src/sources/SimulateSource.res +8 -5
- package/src/sources/SimulateSource.res.mjs +6 -6
- package/src/sources/Source.res +89 -16
- package/src/sources/Source.res.mjs +50 -1
- package/src/sources/SourceManager.res +255 -50
- package/src/sources/SourceManager.res.mjs +149 -55
- package/src/sources/SourceManager.resi +2 -6
- package/src/sources/Svm.res +0 -7
- package/src/sources/Svm.res.mjs +0 -8
- package/src/sources/SvmHyperSyncClient.res +21 -16
- package/src/sources/SvmHyperSyncClient.res.mjs +3 -4
- package/src/sources/SvmHyperSyncSource.res +25 -117
- package/src/sources/SvmHyperSyncSource.res.mjs +8 -121
- package/svm.schema.json +3 -2
|
@@ -15,7 +15,6 @@ type cfg = {
|
|
|
15
15
|
/** Milliseconds to wait for a response before timing out. Default: 30000. */
|
|
16
16
|
httpReqTimeoutMillis?: int,
|
|
17
17
|
/** Number of retries to attempt before returning error. Default: 12. */
|
|
18
|
-
maxNumRetries?: int,
|
|
19
18
|
/** Milliseconds that would be used for retry backoff increasing. Default: 500. */
|
|
20
19
|
retryBackoffMs?: int,
|
|
21
20
|
/** Initial wait time for request backoff. Default: 200. */
|
|
@@ -169,66 +168,8 @@ module QueryTypes = {
|
|
|
169
168
|
}
|
|
170
169
|
}
|
|
171
170
|
|
|
172
|
-
module ResponseTypes = {
|
|
173
|
-
type withdrawal = {
|
|
174
|
-
index?: string,
|
|
175
|
-
validatorIndex?: string,
|
|
176
|
-
address?: Address.t,
|
|
177
|
-
amount?: string,
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
type block = {
|
|
181
|
-
number?: int,
|
|
182
|
-
hash?: string,
|
|
183
|
-
parentHash?: string,
|
|
184
|
-
nonce?: bigint,
|
|
185
|
-
sha3Uncles?: string,
|
|
186
|
-
logsBloom?: string,
|
|
187
|
-
transactionsRoot?: string,
|
|
188
|
-
stateRoot?: string,
|
|
189
|
-
receiptsRoot?: string,
|
|
190
|
-
miner?: Address.t,
|
|
191
|
-
difficulty?: bigint,
|
|
192
|
-
totalDifficulty?: bigint,
|
|
193
|
-
extraData?: string,
|
|
194
|
-
size?: bigint,
|
|
195
|
-
gasLimit?: bigint,
|
|
196
|
-
gasUsed?: bigint,
|
|
197
|
-
timestamp?: int,
|
|
198
|
-
uncles?: array<string>,
|
|
199
|
-
baseFeePerGas?: bigint,
|
|
200
|
-
blobGasUsed?: bigint,
|
|
201
|
-
excessBlobGas?: bigint,
|
|
202
|
-
parentBeaconBlockRoot?: string,
|
|
203
|
-
withdrawalsRoot?: string,
|
|
204
|
-
withdrawals?: array<withdrawal>,
|
|
205
|
-
l1BlockNumber?: int,
|
|
206
|
-
sendCount?: string,
|
|
207
|
-
sendRoot?: string,
|
|
208
|
-
mixHash?: string,
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
type rollbackGuard = {
|
|
212
|
-
blockNumber: int,
|
|
213
|
-
timestamp: int,
|
|
214
|
-
hash: string,
|
|
215
|
-
firstBlockNumber: int,
|
|
216
|
-
firstParentHash: string,
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
171
|
type query = QueryTypes.query
|
|
221
172
|
|
|
222
|
-
type queryResponseData = {blocks: array<ResponseTypes.block>}
|
|
223
|
-
|
|
224
|
-
type queryResponse = {
|
|
225
|
-
archiveHeight: option<int>,
|
|
226
|
-
nextBlock: int,
|
|
227
|
-
totalExecutionTime: int,
|
|
228
|
-
data: queryResponseData,
|
|
229
|
-
rollbackGuard: option<ResponseTypes.rollbackGuard>,
|
|
230
|
-
}
|
|
231
|
-
|
|
232
173
|
module Registration = {
|
|
233
174
|
// One topic position of the resolved `where`: static topic values, or
|
|
234
175
|
// `None` — the "currently registered addresses of this contract" marker,
|
|
@@ -295,12 +236,12 @@ module Registration = {
|
|
|
295
236
|
}),
|
|
296
237
|
// Capitalized to match the Rust BlockField/TransactionField string
|
|
297
238
|
// enums.
|
|
298
|
-
blockFields:
|
|
239
|
+
blockFields: reg.fieldSelection.blockFields
|
|
299
240
|
->Utils.Set.toArray
|
|
300
|
-
->Array.map(
|
|
301
|
-
transactionFields:
|
|
241
|
+
->Array.map(Utils.String.capitalize),
|
|
242
|
+
transactionFields: reg.fieldSelection.transactionFields
|
|
302
243
|
->Utils.Set.toArray
|
|
303
|
-
->Array.map(
|
|
244
|
+
->Array.map(Utils.String.capitalize),
|
|
304
245
|
}
|
|
305
246
|
})
|
|
306
247
|
}
|
|
@@ -338,28 +279,19 @@ module EventItems = {
|
|
|
338
279
|
params: Internal.eventParams,
|
|
339
280
|
}
|
|
340
281
|
|
|
341
|
-
// The always-needed block fields, one per block number. The block's remaining
|
|
342
|
-
// fields live raw in the block store and are materialised on demand.
|
|
343
|
-
type blockHeader = {
|
|
344
|
-
number: int,
|
|
345
|
-
timestamp: int,
|
|
346
|
-
hash: string,
|
|
347
|
-
}
|
|
348
|
-
|
|
349
282
|
type response = {
|
|
350
283
|
archiveHeight: option<int>,
|
|
351
284
|
nextBlock: int,
|
|
352
|
-
// One header per returned block number, including blocks no item
|
|
353
|
-
// references — reorg detection reads them all. The block store keeps only
|
|
354
|
-
// the ones items reference.
|
|
355
|
-
blocks: array<blockHeader>,
|
|
356
285
|
items: array<item>,
|
|
357
|
-
rollbackGuard: option<ResponseTypes.rollbackGuard>,
|
|
358
286
|
}
|
|
359
287
|
}
|
|
360
288
|
|
|
361
289
|
type t = {
|
|
362
|
-
|
|
290
|
+
// Block-hash query construction and pagination live in Rust; only the
|
|
291
|
+
// aggregate response store crosses the boundary.
|
|
292
|
+
getBlockHashes: (
|
|
293
|
+
~blockNumbers: array<int>,
|
|
294
|
+
) => promise<(BlockStore.t, array<RequestStat.t>)>,
|
|
363
295
|
// Returns the response plus page stores owning this page's raw transactions
|
|
364
296
|
// and blocks.
|
|
365
297
|
getEventItems: (
|
|
@@ -414,8 +346,6 @@ let make = (
|
|
|
414
346
|
enableChecksumAddresses,
|
|
415
347
|
apiToken,
|
|
416
348
|
httpReqTimeoutMillis,
|
|
417
|
-
// Retries are handled internally by the indexer, not the binary client
|
|
418
|
-
maxNumRetries: 0,
|
|
419
349
|
?serializationFormat,
|
|
420
350
|
?enableQueryCaching,
|
|
421
351
|
?retryBaseMs,
|
|
@@ -34,8 +34,6 @@ let QueryTypes = {
|
|
|
34
34
|
makeLogSelection: makeLogSelection
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
let ResponseTypes = {};
|
|
38
|
-
|
|
39
37
|
function toTopicFilterInput(filter) {
|
|
40
38
|
if (filter.TAG === "Values") {
|
|
41
39
|
return filter._0;
|
|
@@ -61,8 +59,8 @@ function fromOnEventRegistrations(onEventRegistrations) {
|
|
|
61
59
|
topic2: toTopicFilterInput(ts.topic2),
|
|
62
60
|
topic3: toTopicFilterInput(ts.topic3)
|
|
63
61
|
})),
|
|
64
|
-
blockFields: Array.from(
|
|
65
|
-
transactionFields: Array.from(
|
|
62
|
+
blockFields: Array.from(reg.fieldSelection.blockFields).map(Utils.$$String.capitalize),
|
|
63
|
+
transactionFields: Array.from(reg.fieldSelection.transactionFields).map(Utils.$$String.capitalize)
|
|
66
64
|
};
|
|
67
65
|
});
|
|
68
66
|
}
|
|
@@ -108,7 +106,6 @@ function make(url, apiToken, httpReqTimeoutMillis, eventRegistrations, enableChe
|
|
|
108
106
|
url: url,
|
|
109
107
|
apiToken: apiToken,
|
|
110
108
|
httpReqTimeoutMillis: httpReqTimeoutMillis,
|
|
111
|
-
maxNumRetries: 0,
|
|
112
109
|
retryBackoffMs: retryBackoffMs,
|
|
113
110
|
retryBaseMs: retryBaseMs,
|
|
114
111
|
retryCeilingMs: retryCeilingMs,
|
|
@@ -122,7 +119,6 @@ function make(url, apiToken, httpReqTimeoutMillis, eventRegistrations, enableChe
|
|
|
122
119
|
export {
|
|
123
120
|
serializationFormatSchema,
|
|
124
121
|
QueryTypes,
|
|
125
|
-
ResponseTypes,
|
|
126
122
|
Registration,
|
|
127
123
|
EventItems,
|
|
128
124
|
makeWithAgent,
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// A single backend request a source method made, with the time it took. Kept
|
|
2
|
+
// in its own leaf module (rather than on `Source`) so the client FFI bindings
|
|
3
|
+
// can name it without importing `Source` — which would form a dependency cycle
|
|
4
|
+
// through `Env → HyperSyncClient → Source → FetchState → Env`.
|
|
5
|
+
type t = {method: string, seconds: float}
|
|
@@ -463,6 +463,21 @@ let makeFieldRegistry = (addressSchema: S.t<JSON.t>): Utils.Record.t<
|
|
|
463
463
|
let fieldRegistryLowercase = makeFieldRegistry(lowercaseAddressSchema)
|
|
464
464
|
let fieldRegistryChecksum = makeFieldRegistry(checksumAddressSchema)
|
|
465
465
|
|
|
466
|
+
// Whether an RPC source can populate a transaction field. The getter skips a
|
|
467
|
+
// field it can't parse, so this is what separates "absent because the chain has
|
|
468
|
+
// none" from "absent because RPC never provides it". `hash` and
|
|
469
|
+
// `transactionIndex` come off the log itself, so they have no registry entry.
|
|
470
|
+
// Blocks need no equivalent: `eth_getBlockByNumber` carries every block field,
|
|
471
|
+
// which `RpcFieldSelection_test.res` holds the registry to.
|
|
472
|
+
let isRpcTransactionField = (name: string) =>
|
|
473
|
+
switch name {
|
|
474
|
+
| "transactionIndex" | "hash" => true
|
|
475
|
+
| _ =>
|
|
476
|
+
fieldRegistryChecksum
|
|
477
|
+
->Utils.Record.get(name->(Utils.magic: string => Internal.evmTransactionField))
|
|
478
|
+
->Option.isSome
|
|
479
|
+
}
|
|
480
|
+
|
|
466
481
|
type fetchStrategy = NoRpc | TransactionOnly | ReceiptOnly | TransactionAndReceipt
|
|
467
482
|
|
|
468
483
|
// Parse fields from a raw JSON object into a result dict.
|
|
@@ -487,6 +502,30 @@ let parseFieldsFromJson = (
|
|
|
487
502
|
})
|
|
488
503
|
}
|
|
489
504
|
|
|
505
|
+
let effectiveGasPriceKey = (Internal.EffectiveGasPrice: Internal.evmTransactionField :> string)
|
|
506
|
+
|
|
507
|
+
// Pre-EIP-1559 receipts carry no `effectiveGasPrice` — every Optimism block
|
|
508
|
+
// below the Bedrock migration at 105235063, for one. Those chains price every
|
|
509
|
+
// transaction the legacy way, so the transaction's `gasPrice` is the effective
|
|
510
|
+
// price, which is the same substitution HyperSync serves for those blocks.
|
|
511
|
+
// Reads `gasPrice` but stores under `effectiveGasPrice`.
|
|
512
|
+
let effectiveGasPriceFallbackDef = {
|
|
513
|
+
location: EffectiveGasPrice,
|
|
514
|
+
jsonKey: "gasPrice",
|
|
515
|
+
schema: Rpc.hexBigintSchema->toFieldSchema,
|
|
516
|
+
source: TransactionOnly,
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
let fillEffectiveGasPriceOrThrow = (mutTransactionAcc: dict<JSON.t>, txJson: JSON.t) => {
|
|
520
|
+
let gasPrice =
|
|
521
|
+
txJson->(Utils.magic: JSON.t => dict<JSON.t>)->Utils.Dict.dangerouslyGetNonOption("gasPrice")
|
|
522
|
+
switch gasPrice {
|
|
523
|
+
| None =>
|
|
524
|
+
JsError.throwWithMessage(`Neither "effectiveGasPrice" nor "gasPrice" is present in the RPC response for the transaction. Remove "effectiveGasPrice" from the field selection, or index this chain via HyperSync.`)
|
|
525
|
+
| Some(_) => parseFieldsFromJson(mutTransactionAcc, [effectiveGasPriceFallbackDef], txJson)
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
490
529
|
let makeThrowingGetEventTransaction = (
|
|
491
530
|
~getTransactionJson: string => promise<JSON.t>,
|
|
492
531
|
~getReceiptJson: string => promise<JSON.t>,
|
|
@@ -507,6 +546,7 @@ let makeThrowingGetEventTransaction = (
|
|
|
507
546
|
// Classify fields: log-derived vs RPC fields
|
|
508
547
|
let hasTransactionIndex = ref(false)
|
|
509
548
|
let hasHash = ref(false)
|
|
549
|
+
let hasEffectiveGasPrice = ref(false)
|
|
510
550
|
let txFields: array<fieldDef> = []
|
|
511
551
|
let receiptFields: array<fieldDef> = []
|
|
512
552
|
let bothFields: array<fieldDef> = []
|
|
@@ -518,6 +558,15 @@ let makeThrowingGetEventTransaction = (
|
|
|
518
558
|
| _ =>
|
|
519
559
|
switch fieldRegistry->Utils.Record.get(fieldName) {
|
|
520
560
|
| Some(def) =>
|
|
561
|
+
// Absent from the receipt means fall back to the transaction's
|
|
562
|
+
// `gasPrice`, so this one field parses leniently rather than
|
|
563
|
+
// throwing on a receipt that omits it.
|
|
564
|
+
let def = if fieldName === EffectiveGasPrice {
|
|
565
|
+
hasEffectiveGasPrice := true
|
|
566
|
+
{...def, schema: S.nullable(def.schema)->toFieldSchema}
|
|
567
|
+
} else {
|
|
568
|
+
def
|
|
569
|
+
}
|
|
521
570
|
switch def.source {
|
|
522
571
|
| TransactionOnly => txFields->Array.push(def)->ignore
|
|
523
572
|
| ReceiptOnly => receiptFields->Array.push(def)->ignore
|
|
@@ -580,10 +629,8 @@ let makeThrowingGetEventTransaction = (
|
|
|
580
629
|
| _ => Promise.resolve(None)
|
|
581
630
|
}
|
|
582
631
|
|
|
583
|
-
Promise.all2((txJsonPromise, receiptJsonPromise))
|
|
584
|
-
|
|
585
|
-
receiptJson,
|
|
586
|
-
)) => {
|
|
632
|
+
Promise.all2((txJsonPromise, receiptJsonPromise))
|
|
633
|
+
->Promise.then(((txJson, receiptJson)) => {
|
|
587
634
|
let mutTransactionAcc = Dict.make()
|
|
588
635
|
setLogFields(mutTransactionAcc, log)
|
|
589
636
|
|
|
@@ -596,6 +643,30 @@ let makeThrowingGetEventTransaction = (
|
|
|
596
643
|
| None => ()
|
|
597
644
|
}
|
|
598
645
|
|
|
646
|
+
// Only the chains that omit it pay for the extra request, and
|
|
647
|
+
// only when the receipt has already come back without it.
|
|
648
|
+
if (
|
|
649
|
+
hasEffectiveGasPrice.contents &&
|
|
650
|
+
mutTransactionAcc
|
|
651
|
+
->Utils.Dict.dangerouslyGetNonOption(effectiveGasPriceKey)
|
|
652
|
+
->Option.isNone
|
|
653
|
+
) {
|
|
654
|
+
switch txJson {
|
|
655
|
+
| Some(json) => {
|
|
656
|
+
fillEffectiveGasPriceOrThrow(mutTransactionAcc, json)
|
|
657
|
+
Promise.resolve(mutTransactionAcc)
|
|
658
|
+
}
|
|
659
|
+
| None =>
|
|
660
|
+
getTransactionJson(log.transactionHash)->Promise.thenResolve(json => {
|
|
661
|
+
fillEffectiveGasPriceOrThrow(mutTransactionAcc, json)
|
|
662
|
+
mutTransactionAcc
|
|
663
|
+
})
|
|
664
|
+
}
|
|
665
|
+
} else {
|
|
666
|
+
Promise.resolve(mutTransactionAcc)
|
|
667
|
+
}
|
|
668
|
+
})
|
|
669
|
+
->Promise.thenResolve(mutTransactionAcc => {
|
|
599
670
|
mutTransactionAcc->(Utils.magic: dict<JSON.t> => 'a)
|
|
600
671
|
})
|
|
601
672
|
}
|
|
@@ -768,6 +839,12 @@ let make = (
|
|
|
768
839
|
let transactionLoader = ref(makeTransactionLoader())
|
|
769
840
|
let receiptLoader = ref(makeReceiptLoader())
|
|
770
841
|
|
|
842
|
+
let resetCachedLoaders = () => {
|
|
843
|
+
blockLoader := makeBlockLoader()
|
|
844
|
+
transactionLoader := makeTransactionLoader()
|
|
845
|
+
receiptLoader := makeReceiptLoader()
|
|
846
|
+
}
|
|
847
|
+
|
|
771
848
|
let getEventBlockOrThrow = makeThrowingGetEventBlock(
|
|
772
849
|
~getBlockJson=blockNumber => blockLoader.contents->LazyLoader.get(blockNumber),
|
|
773
850
|
~lowercaseAddresses,
|
|
@@ -815,10 +892,16 @@ let make = (
|
|
|
815
892
|
| None => knownHeight
|
|
816
893
|
}
|
|
817
894
|
|
|
818
|
-
|
|
819
|
-
|
|
895
|
+
// The seam block (`fromBlock - 1`) is the only block this range shares with
|
|
896
|
+
// what the store already scanned, so it is where a reorg at the boundary
|
|
897
|
+
// shows up. Reading it directly would answer from the block cache — it was
|
|
898
|
+
// the previous range's `toBlock`, so it is always cached, and always on the
|
|
899
|
+
// chain that range saw. Read `fromBlock` instead, which no earlier range
|
|
900
|
+
// touched, and take the seam's hash from its `parentHash`.
|
|
901
|
+
let firstBlockPromise =
|
|
902
|
+
fromBlock > 0 && fromBlock <= toBlock
|
|
820
903
|
? blockLoader.contents
|
|
821
|
-
->LazyLoader.get(fromBlock
|
|
904
|
+
->LazyLoader.get(fromBlock)
|
|
822
905
|
->Promise.thenResolve(json => Some(parseBlockInfo(json)))
|
|
823
906
|
: Promise.resolve(None)
|
|
824
907
|
|
|
@@ -885,9 +968,13 @@ let make = (
|
|
|
885
968
|
(
|
|
886
969
|
async () => {
|
|
887
970
|
let (block, transaction) = try await Promise.all2((
|
|
888
|
-
log->getEventBlockOrThrow(
|
|
971
|
+
log->getEventBlockOrThrow(
|
|
972
|
+
~selectedBlockFields=onEventRegistration.fieldSelection.blockFields->(
|
|
973
|
+
Utils.magic: Utils.Set.t<string> => Utils.Set.t<Internal.evmBlockField>
|
|
974
|
+
),
|
|
975
|
+
),
|
|
889
976
|
log->getEventTransactionOrThrow(
|
|
890
|
-
~selectedTransactionFields=
|
|
977
|
+
~selectedTransactionFields=onEventRegistration.fieldSelection.transactionFields->(
|
|
891
978
|
Utils.magic: Utils.Set.t<string> => Utils.Set.t<Internal.evmTransactionField>
|
|
892
979
|
),
|
|
893
980
|
),
|
|
@@ -931,7 +1018,7 @@ let make = (
|
|
|
931
1018
|
payload: {
|
|
932
1019
|
contractName: eventConfig.contractName,
|
|
933
1020
|
eventName: eventConfig.name,
|
|
934
|
-
chainId
|
|
1021
|
+
chainId,
|
|
935
1022
|
params: decoded,
|
|
936
1023
|
block,
|
|
937
1024
|
transaction,
|
|
@@ -944,55 +1031,66 @@ let make = (
|
|
|
944
1031
|
})
|
|
945
1032
|
->Promise.all
|
|
946
1033
|
|
|
947
|
-
let
|
|
1034
|
+
let optFirstBlock = await firstBlockPromise
|
|
948
1035
|
|
|
949
1036
|
let totalTimeElapsed = startFetchingBatchTimeRef->Performance.secondsSince
|
|
950
1037
|
|
|
951
1038
|
// Every fetched block carries `hash` and `parentHash`, so each one yields
|
|
952
1039
|
// two confirmed (number, hash) pairs for reorg detection at no extra cost.
|
|
953
|
-
|
|
1040
|
+
// Both these blocks and the logs' own `blockHash` come from this range's
|
|
1041
|
+
// responses, never from the block cache's older view of the chain. They go
|
|
1042
|
+
// into a hash-only page store merged into the chain store, where hash
|
|
1043
|
+
// comparison happens. The block data itself stays inline on the payload.
|
|
1044
|
+
let observedBlocks: array<BlockStore.inputBlock> = []
|
|
954
1045
|
let pushBlockInfo = (b: blockInfo) => {
|
|
955
|
-
|
|
1046
|
+
observedBlocks
|
|
1047
|
+
->Array.push({
|
|
1048
|
+
BlockStore.blockNumber: b.number,
|
|
1049
|
+
blockHash: b.hash,
|
|
1050
|
+
blockTimestamp: b.timestamp,
|
|
1051
|
+
})
|
|
1052
|
+
->ignore
|
|
956
1053
|
if b.number > 0 {
|
|
957
|
-
|
|
958
|
-
->Array.push({
|
|
1054
|
+
observedBlocks
|
|
1055
|
+
->Array.push({BlockStore.blockNumber: b.number - 1, blockHash: b.parentHash})
|
|
959
1056
|
->ignore
|
|
960
1057
|
}
|
|
961
1058
|
}
|
|
962
1059
|
pushBlockInfo(latestFetchedBlockInfo)
|
|
963
|
-
switch
|
|
1060
|
+
switch optFirstBlock {
|
|
964
1061
|
| Some(b) => pushBlockInfo(b)
|
|
965
1062
|
| None => ()
|
|
966
1063
|
}
|
|
967
1064
|
items->Array.forEach(({log}) =>
|
|
968
|
-
|
|
969
|
-
->Array.push({
|
|
1065
|
+
observedBlocks
|
|
1066
|
+
->Array.push({BlockStore.blockNumber: log.blockNumber, blockHash: log.blockHash})
|
|
970
1067
|
->ignore
|
|
971
1068
|
)
|
|
972
1069
|
|
|
973
1070
|
{
|
|
974
|
-
latestFetchedBlockTimestamp: latestFetchedBlockInfo.timestamp,
|
|
975
1071
|
latestFetchedBlockNumber: latestFetchedBlockInfo.number,
|
|
976
1072
|
parsedQueueItems,
|
|
977
|
-
// RPC keeps the transaction and block inline on the payload; no
|
|
1073
|
+
// RPC keeps the transaction and block inline on the payload; no
|
|
1074
|
+
// transaction page, and the block page carries only observed hashes.
|
|
978
1075
|
transactionStore: None,
|
|
979
|
-
blockStore:
|
|
1076
|
+
blockStore: BlockStore.fromJs(
|
|
1077
|
+
observedBlocks,
|
|
1078
|
+
~ecosystem=Evm,
|
|
1079
|
+
~shouldChecksum=!lowercaseAddresses,
|
|
1080
|
+
),
|
|
980
1081
|
stats: {
|
|
981
1082
|
totalTimeElapsed: totalTimeElapsed,
|
|
982
1083
|
},
|
|
983
1084
|
knownHeight,
|
|
984
|
-
blockHashes,
|
|
985
1085
|
fromBlockQueried: fromBlock,
|
|
986
1086
|
requestStats: drainRequestStats(),
|
|
987
1087
|
}
|
|
988
1088
|
}
|
|
989
1089
|
|
|
990
|
-
let onReorg = (
|
|
991
|
-
// Drop cached block/transaction/receipt data — after a reorg
|
|
992
|
-
//
|
|
993
|
-
|
|
994
|
-
transactionLoader := makeTransactionLoader()
|
|
995
|
-
receiptLoader := makeReceiptLoader()
|
|
1090
|
+
let onReorg = () => {
|
|
1091
|
+
// Drop cached block/transaction/receipt data — after a reorg or an
|
|
1092
|
+
// internally inconsistent response these may refer to orphaned-chain values.
|
|
1093
|
+
resetCachedLoaders()
|
|
996
1094
|
}
|
|
997
1095
|
|
|
998
1096
|
let getBlockHashes = (~blockNumbers, ~logger as _currentlyUnusedLogger) => {
|
|
@@ -1000,21 +1098,29 @@ let make = (
|
|
|
1000
1098
|
->Array.map(blockNum => blockLoader.contents->LazyLoader.get(blockNum))
|
|
1001
1099
|
->Promise.all
|
|
1002
1100
|
->Promise.thenResolve(rawBlocks => {
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1101
|
+
// Each block is fetched in its own request, so responses can mix forks.
|
|
1102
|
+
// The parent hash becomes a minimal extra row: when consecutive blocks
|
|
1103
|
+
// are requested, the page's own hash-collision check cross-validates the
|
|
1104
|
+
// separately fetched responses.
|
|
1105
|
+
let observedBlocks: array<BlockStore.inputBlock> = []
|
|
1106
|
+
rawBlocks->Array.forEach(json => {
|
|
1107
|
+
let b = parseBlockInfo(json)
|
|
1108
|
+
observedBlocks
|
|
1109
|
+
->Array.push({
|
|
1110
|
+
BlockStore.blockNumber: b.number,
|
|
1111
|
+
blockHash: b.hash,
|
|
1112
|
+
blockTimestamp: b.timestamp,
|
|
1015
1113
|
})
|
|
1016
|
-
->
|
|
1017
|
-
|
|
1114
|
+
->ignore
|
|
1115
|
+
if b.number > 0 {
|
|
1116
|
+
observedBlocks
|
|
1117
|
+
->Array.push({BlockStore.blockNumber: b.number - 1, blockHash: b.parentHash})
|
|
1118
|
+
->ignore
|
|
1119
|
+
}
|
|
1120
|
+
})
|
|
1121
|
+
let blockStore =
|
|
1122
|
+
observedBlocks->BlockStore.fromJs(~ecosystem=Evm, ~shouldChecksum=!lowercaseAddresses)
|
|
1123
|
+
{Source.result: Ok(blockStore), requestStats: drainRequestStats()}
|
|
1018
1124
|
})
|
|
1019
1125
|
->Promise.catch(exn =>
|
|
1020
1126
|
{Source.result: Error(exn), requestStats: drainRequestStats()}->Promise.resolve
|
|
@@ -1023,7 +1129,7 @@ let make = (
|
|
|
1023
1129
|
|
|
1024
1130
|
let createHeightSubscription =
|
|
1025
1131
|
ws->Option.map(wsUrl =>
|
|
1026
|
-
(~onHeight) => RpcWebSocketHeightStream.subscribe(~wsUrl, ~chainId
|
|
1132
|
+
(~onHeight) => RpcWebSocketHeightStream.subscribe(~wsUrl, ~chainId, ~onHeight)
|
|
1027
1133
|
)
|
|
1028
1134
|
|
|
1029
1135
|
{
|