envio 3.6.1 → 3.7.0-svm-alpha.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/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 +1 -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/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 +15 -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 +21 -117
- package/src/sources/SvmHyperSyncSource.res.mjs +8 -121
- package/svm.schema.json +3 -2
package/index.d.ts
CHANGED
|
@@ -725,17 +725,188 @@ export type FuelOnEventWhere<Params, ContractName extends string> =
|
|
|
725
725
|
type _ProjectEvmEvent = EvmEvent extends EventLike ? EvmEvent : never;
|
|
726
726
|
type _ProjectFuelEvent = FuelEvent extends EventLike ? FuelEvent : never;
|
|
727
727
|
|
|
728
|
+
// ============== EVM inline field selection ==============
|
|
729
|
+
|
|
730
|
+
/** Marks a block/transaction field the registration doesn't select. Reading such
|
|
731
|
+
* a field is a type error; the message names the field and where to add it. */
|
|
732
|
+
export type FieldNotSelected<Message extends string> = { readonly __fieldNotSelected: Message };
|
|
733
|
+
|
|
734
|
+
/** Every EVM block field, with the type it has once selected. Declared by hand
|
|
735
|
+
* so the `fields` option types work in a project that never ran codegen; kept
|
|
736
|
+
* in sync with the config-parsing enums by a test in `codegen_templates.rs`. */
|
|
737
|
+
export type EvmAllBlockFields = {
|
|
738
|
+
readonly number: number;
|
|
739
|
+
readonly timestamp: number;
|
|
740
|
+
readonly hash: string;
|
|
741
|
+
readonly parentHash: string;
|
|
742
|
+
readonly nonce: bigint | undefined;
|
|
743
|
+
readonly sha3Uncles: string;
|
|
744
|
+
readonly logsBloom: string;
|
|
745
|
+
readonly transactionsRoot: string;
|
|
746
|
+
readonly stateRoot: string;
|
|
747
|
+
readonly receiptsRoot: string;
|
|
748
|
+
readonly miner: Address;
|
|
749
|
+
readonly difficulty: bigint | undefined;
|
|
750
|
+
readonly totalDifficulty: bigint | undefined;
|
|
751
|
+
readonly extraData: string;
|
|
752
|
+
readonly size: bigint;
|
|
753
|
+
readonly gasLimit: bigint;
|
|
754
|
+
readonly gasUsed: bigint;
|
|
755
|
+
readonly uncles: readonly string[] | undefined;
|
|
756
|
+
readonly baseFeePerGas: bigint | undefined;
|
|
757
|
+
readonly blobGasUsed: bigint | undefined;
|
|
758
|
+
readonly excessBlobGas: bigint | undefined;
|
|
759
|
+
readonly parentBeaconBlockRoot: string | undefined;
|
|
760
|
+
readonly withdrawalsRoot: string | undefined;
|
|
761
|
+
readonly l1BlockNumber: number | undefined;
|
|
762
|
+
readonly sendCount: string | undefined;
|
|
763
|
+
readonly sendRoot: string | undefined;
|
|
764
|
+
readonly mixHash: string | undefined;
|
|
765
|
+
};
|
|
766
|
+
|
|
767
|
+
export type EvmAllTransactionFields = {
|
|
768
|
+
readonly transactionIndex: number;
|
|
769
|
+
readonly hash: string;
|
|
770
|
+
readonly from: Address | undefined;
|
|
771
|
+
readonly to: Address | undefined;
|
|
772
|
+
readonly gas: bigint;
|
|
773
|
+
readonly gasPrice: bigint | undefined;
|
|
774
|
+
readonly maxPriorityFeePerGas: bigint | undefined;
|
|
775
|
+
readonly maxFeePerGas: bigint | undefined;
|
|
776
|
+
readonly cumulativeGasUsed: bigint;
|
|
777
|
+
readonly effectiveGasPrice: bigint;
|
|
778
|
+
readonly gasUsed: bigint;
|
|
779
|
+
readonly input: string;
|
|
780
|
+
readonly nonce: bigint;
|
|
781
|
+
readonly value: bigint;
|
|
782
|
+
readonly v: string | undefined;
|
|
783
|
+
readonly r: string | undefined;
|
|
784
|
+
readonly s: string | undefined;
|
|
785
|
+
readonly contractAddress: Address | undefined;
|
|
786
|
+
readonly logsBloom: string;
|
|
787
|
+
readonly root: string | undefined;
|
|
788
|
+
readonly status: number | undefined;
|
|
789
|
+
readonly yParity: string | undefined;
|
|
790
|
+
readonly accessList: readonly unknown[] | undefined;
|
|
791
|
+
readonly maxFeePerBlobGas: bigint | undefined;
|
|
792
|
+
readonly blobVersionedHashes: readonly string[] | undefined;
|
|
793
|
+
readonly type: number | undefined;
|
|
794
|
+
readonly l1Fee: bigint | undefined;
|
|
795
|
+
readonly l1GasPrice: bigint | undefined;
|
|
796
|
+
readonly l1GasUsed: bigint | undefined;
|
|
797
|
+
readonly l1FeeScalar: number | undefined;
|
|
798
|
+
readonly gasUsedForL1: bigint | undefined;
|
|
799
|
+
readonly authorizationList: readonly unknown[] | undefined;
|
|
800
|
+
};
|
|
801
|
+
|
|
802
|
+
export type EvmBlockFieldName = keyof EvmAllBlockFields & string;
|
|
803
|
+
export type EvmTransactionFieldName = keyof EvmAllTransactionFields & string;
|
|
804
|
+
|
|
805
|
+
/** The `fields` option of `indexer.onEvent` / `indexer.contractRegister`.
|
|
806
|
+
* Names the block and transaction fields this registration reads. Replaces
|
|
807
|
+
* `field_selection` from `config.yaml` for this registration — fields selected
|
|
808
|
+
* there but not listed here are not readable. */
|
|
809
|
+
export type EvmFieldsSelection = {
|
|
810
|
+
readonly block?: readonly EvmBlockFieldName[];
|
|
811
|
+
readonly transaction?: readonly EvmTransactionFieldName[];
|
|
812
|
+
};
|
|
813
|
+
|
|
814
|
+
/** Included in every inline selection without being listed. `block.number` is
|
|
815
|
+
* the item's own key, so it costs nothing extra. */
|
|
816
|
+
type EvmAlwaysSelectedBlockField = "number";
|
|
817
|
+
|
|
818
|
+
type EvmSelectedFields<All, Selected extends string, Knob extends string> = {
|
|
819
|
+
readonly [K in keyof All]: K extends Selected
|
|
820
|
+
? All[K]
|
|
821
|
+
: FieldNotSelected<`Field '${K &
|
|
822
|
+
string}' is not selected for this handler. Add it to fields.${Knob} in the registration options.`>;
|
|
823
|
+
};
|
|
824
|
+
|
|
825
|
+
/** Field names listed under `Knob`, or `never` when the key is absent or lists
|
|
826
|
+
* nothing. Reads the value through `EvmListedArray` for the same reason that
|
|
827
|
+
* type exists: an optional property doesn't satisfy a `Record<Knob, _>`
|
|
828
|
+
* conditional, which would type every listed field as unselected for a
|
|
829
|
+
* selection whose keys are optional. Element access rather than `infer`, which
|
|
830
|
+
* falls back to its `string` constraint on an absent key — naming every field
|
|
831
|
+
* as selected. */
|
|
832
|
+
type EvmListedFields<Fields, Knob extends keyof EvmFieldsSelection> = NonNullable<
|
|
833
|
+
EvmListedArray<Fields, Knob>
|
|
834
|
+
>[number] &
|
|
835
|
+
string;
|
|
836
|
+
|
|
837
|
+
/** The value listed under `Knob`, keeping optional keys (which a `Record<Knob, _>`
|
|
838
|
+
* conditional would miss, since an optional property doesn't satisfy a required
|
|
839
|
+
* one). `never` when the key is absent from the selection entirely. */
|
|
840
|
+
type EvmListedArray<Fields, Knob extends keyof EvmFieldsSelection> = Fields[Knob &
|
|
841
|
+
keyof Fields];
|
|
842
|
+
|
|
843
|
+
/** True for an unbounded `T[]` / `readonly T[]`, false for a literal tuple —
|
|
844
|
+
* and false for an absent key, so a selection that names only one knob passes. */
|
|
845
|
+
type IsWidenedArray<T> = [NonNullable<T>] extends [never]
|
|
846
|
+
? false
|
|
847
|
+
: [NonNullable<T>] extends [readonly unknown[]]
|
|
848
|
+
? number extends NonNullable<T>["length"]
|
|
849
|
+
? true
|
|
850
|
+
: false
|
|
851
|
+
: false;
|
|
852
|
+
|
|
853
|
+
/** Rejected in place of the `fields` option when a selection isn't written as a
|
|
854
|
+
* literal. Reading it as an array type would only tell us the field union, not
|
|
855
|
+
* which fields this registration listed, so every field would type as selected
|
|
856
|
+
* while the runtime selected a subset. */
|
|
857
|
+
export type EvmFieldsMustBeLiteral<Knob extends string> = {
|
|
858
|
+
readonly __fieldsMustBeLiteral: `fields.${Knob} must be a literal array, so its element types name the selected fields. Write it inline, or annotate the variable with 'as const' instead of a field-name array type.`;
|
|
859
|
+
};
|
|
860
|
+
|
|
861
|
+
/** `unknown` (a no-op intersection) when every listed selection is a literal
|
|
862
|
+
* array, otherwise the branded type that rejects the option. */
|
|
863
|
+
type EvmFieldsLiteralCheck<Fields> = [Fields] extends [undefined]
|
|
864
|
+
? unknown
|
|
865
|
+
: IsWidenedArray<EvmListedArray<Fields, "block">> extends true
|
|
866
|
+
? EvmFieldsMustBeLiteral<"block">
|
|
867
|
+
: IsWidenedArray<EvmListedArray<Fields, "transaction">> extends true
|
|
868
|
+
? EvmFieldsMustBeLiteral<"transaction">
|
|
869
|
+
: unknown;
|
|
870
|
+
|
|
871
|
+
/** Rebinds an event's `block`/`transaction` to an inline `fields` selection.
|
|
872
|
+
* With no `fields` option the event type passes through, so the `config.yaml`
|
|
873
|
+
* `field_selection` path is unaffected. */
|
|
874
|
+
export type EvmEventWithFields<Event, Fields> = [Fields] extends [EvmFieldsSelection]
|
|
875
|
+
? Omit<Event, "block" | "transaction"> & {
|
|
876
|
+
readonly block: EvmSelectedFields<
|
|
877
|
+
EvmAllBlockFields,
|
|
878
|
+
EvmAlwaysSelectedBlockField | EvmListedFields<Fields, "block">,
|
|
879
|
+
"block"
|
|
880
|
+
>;
|
|
881
|
+
readonly transaction: EvmSelectedFields<
|
|
882
|
+
EvmAllTransactionFields,
|
|
883
|
+
EvmListedFields<Fields, "transaction">,
|
|
884
|
+
"transaction"
|
|
885
|
+
>;
|
|
886
|
+
}
|
|
887
|
+
: Event;
|
|
888
|
+
|
|
728
889
|
/** Options for registering an EVM onEvent handler. Contract and event literal names are derived from the Event type.
|
|
729
890
|
* The conditional `Event extends EventLike` distributes over union members so that each member's
|
|
730
891
|
* contractName/eventName pair is constrained together — preventing invalid cross-member pairings.
|
|
731
892
|
* The `Params` generic carries the indexed-parameter shape (looked up via `EvmEventFilters[C][E]["params"]`
|
|
732
893
|
* by callers) so the `where` option enforces the same per-event narrowing as the inline handler signature. */
|
|
733
|
-
export type EvmOnEventOptions<
|
|
894
|
+
export type EvmOnEventOptions<
|
|
895
|
+
Event extends EventLike = _ProjectEvmEvent,
|
|
896
|
+
Params = {},
|
|
897
|
+
Fields extends EvmFieldsSelection | undefined = undefined,
|
|
898
|
+
> = Event extends EventLike
|
|
734
899
|
? {
|
|
735
900
|
readonly contract: Event["contractName"];
|
|
736
901
|
readonly event: Event["eventName"];
|
|
737
902
|
readonly wildcard?: boolean;
|
|
738
903
|
readonly where?: EvmOnEventWhere<Params, Event["contractName"] & string>;
|
|
904
|
+
/** Pass the selection's literal type as the `Fields` generic to get the
|
|
905
|
+
* same narrowing `indexer.onEvent` infers from the call site. Defaults to
|
|
906
|
+
* no selection: `EvmFieldsSelection` names no fields in particular, so an
|
|
907
|
+
* options value declared without the generic would carry a selection
|
|
908
|
+
* `onEvent` can only reject. */
|
|
909
|
+
readonly fields?: Fields & EvmFieldsLiteralCheck<Fields>;
|
|
739
910
|
}
|
|
740
911
|
: never;
|
|
741
912
|
|
|
@@ -749,7 +920,8 @@ export type EvmOnEventHandler<
|
|
|
749
920
|
export type EvmContractRegisterOptions<
|
|
750
921
|
Event extends EventLike = _ProjectEvmEvent,
|
|
751
922
|
Params = {},
|
|
752
|
-
|
|
923
|
+
Fields extends EvmFieldsSelection | undefined = undefined,
|
|
924
|
+
> = EvmOnEventOptions<Event, Params, Fields>;
|
|
753
925
|
|
|
754
926
|
/** Handler function for an EVM contractRegister registration. */
|
|
755
927
|
export type EvmContractRegisterHandler<
|
|
@@ -1029,10 +1201,18 @@ export type SvmInstructionBlock = {
|
|
|
1029
1201
|
export type SvmTokenBalance = {
|
|
1030
1202
|
readonly account?: string;
|
|
1031
1203
|
readonly mint?: string;
|
|
1204
|
+
/** Owner at the end of the transaction, falling back to the owner on entry
|
|
1205
|
+
* when the account was closed during it. Pre and post owners differ only
|
|
1206
|
+
* when a `SetAuthority(AccountOwner)` runs mid-transaction. */
|
|
1032
1207
|
readonly owner?: string;
|
|
1033
|
-
/**
|
|
1034
|
-
readonly
|
|
1035
|
-
|
|
1208
|
+
/** Mint decimals, for scaling the raw amounts below. */
|
|
1209
|
+
readonly decimals?: number;
|
|
1210
|
+
/** Raw amount in base units before the transaction. Absent when the token
|
|
1211
|
+
* account was created during it. */
|
|
1212
|
+
readonly preAmount?: bigint;
|
|
1213
|
+
/** Raw amount in base units after the transaction. Absent when the token
|
|
1214
|
+
* account was closed during it. */
|
|
1215
|
+
readonly postAmount?: bigint;
|
|
1036
1216
|
};
|
|
1037
1217
|
|
|
1038
1218
|
export type SvmLog = {
|
|
@@ -1181,7 +1361,8 @@ type EvmEcosystem<Config extends IndexerConfigTypes = GlobalConfig> =
|
|
|
1181
1361
|
/** Register an event handler. */
|
|
1182
1362
|
readonly onEvent: <
|
|
1183
1363
|
C extends keyof Contracts & string,
|
|
1184
|
-
E extends keyof Contracts[C] & string
|
|
1364
|
+
E extends keyof Contracts[C] & string,
|
|
1365
|
+
const F extends EvmFieldsSelection | undefined
|
|
1185
1366
|
>(
|
|
1186
1367
|
options: {
|
|
1187
1368
|
readonly contract: C;
|
|
@@ -1195,13 +1376,17 @@ type EvmEcosystem<Config extends IndexerConfigTypes = GlobalConfig> =
|
|
|
1195
1376
|
: {},
|
|
1196
1377
|
C
|
|
1197
1378
|
>;
|
|
1379
|
+
/** Block and transaction fields this handler reads. Replaces
|
|
1380
|
+
* `field_selection` from `config.yaml` for this registration. */
|
|
1381
|
+
readonly fields?: F & EvmFieldsLiteralCheck<F>;
|
|
1198
1382
|
},
|
|
1199
|
-
handler: EvmOnEventHandler<Contracts[C][E], EvmOnEventContext<Config>>
|
|
1383
|
+
handler: EvmOnEventHandler<EvmEventWithFields<Contracts[C][E], F>, EvmOnEventContext<Config>>
|
|
1200
1384
|
) => void;
|
|
1201
1385
|
/** Register a contract register handler for dynamic contract indexing. */
|
|
1202
1386
|
readonly contractRegister: <
|
|
1203
1387
|
C extends keyof Contracts & string,
|
|
1204
|
-
E extends keyof Contracts[C] & string
|
|
1388
|
+
E extends keyof Contracts[C] & string,
|
|
1389
|
+
const F extends EvmFieldsSelection | undefined
|
|
1205
1390
|
>(
|
|
1206
1391
|
options: {
|
|
1207
1392
|
readonly contract: C;
|
|
@@ -1215,8 +1400,11 @@ type EvmEcosystem<Config extends IndexerConfigTypes = GlobalConfig> =
|
|
|
1215
1400
|
: {},
|
|
1216
1401
|
C
|
|
1217
1402
|
>;
|
|
1403
|
+
/** Block and transaction fields this handler reads. Replaces
|
|
1404
|
+
* `field_selection` from `config.yaml` for this registration. */
|
|
1405
|
+
readonly fields?: F & EvmFieldsLiteralCheck<F>;
|
|
1218
1406
|
},
|
|
1219
|
-
handler: EvmContractRegisterHandler<Contracts[C][E], EvmContractRegisterContext<Config>>
|
|
1407
|
+
handler: EvmContractRegisterHandler<EvmEventWithFields<Contracts[C][E], F>, EvmContractRegisterContext<Config>>
|
|
1220
1408
|
) => void;
|
|
1221
1409
|
}
|
|
1222
1410
|
: {})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.0-svm-alpha.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
|
|
6
6
|
"bin": "./bin.mjs",
|
|
@@ -69,10 +69,10 @@
|
|
|
69
69
|
"tsx": "4.21.0"
|
|
70
70
|
},
|
|
71
71
|
"optionalDependencies": {
|
|
72
|
-
"envio-linux-x64": "3.
|
|
73
|
-
"envio-linux-x64-musl": "3.
|
|
74
|
-
"envio-linux-arm64": "3.
|
|
75
|
-
"envio-darwin-x64": "3.
|
|
76
|
-
"envio-darwin-arm64": "3.
|
|
72
|
+
"envio-linux-x64": "3.7.0-svm-alpha.1",
|
|
73
|
+
"envio-linux-x64-musl": "3.7.0-svm-alpha.1",
|
|
74
|
+
"envio-linux-arm64": "3.7.0-svm-alpha.1",
|
|
75
|
+
"envio-darwin-x64": "3.7.0-svm-alpha.1",
|
|
76
|
+
"envio-darwin-arm64": "3.7.0-svm-alpha.1"
|
|
77
77
|
}
|
|
78
78
|
}
|
package/src/Batch.res
CHANGED
|
@@ -10,9 +10,20 @@ type chainAfterBatch = {
|
|
|
10
10
|
isProgressAtHeadWhenBatchCreated: bool,
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
// A per-chain snapshot of the scanned block hashes still inside the reorg
|
|
14
|
+
// threshold, taken when the batch is assembled. Immutable for the batch's
|
|
15
|
+
// lifetime, unlike the live block store it is read from - so checkpoint hashes
|
|
16
|
+
// can't shift under a concurrent store mutation. `blockNumbers` is ascending;
|
|
17
|
+
// `hashByBlockNumber` is keyed by block number.
|
|
18
|
+
type reorgHashSnapshot = {
|
|
19
|
+
blockNumbers: array<int>,
|
|
20
|
+
hashByBlockNumber: dict<string>,
|
|
21
|
+
}
|
|
22
|
+
|
|
13
23
|
type chainBeforeBatch = {
|
|
14
24
|
fetchState: FetchState.t,
|
|
15
|
-
|
|
25
|
+
scannedHashes: reorgHashSnapshot,
|
|
26
|
+
shouldRollbackOnReorg: bool,
|
|
16
27
|
progressBlockNumber: int,
|
|
17
28
|
sourceBlockNumber: int,
|
|
18
29
|
totalEventsProcessed: float,
|
|
@@ -107,10 +118,7 @@ let getProgressedChainsById = {
|
|
|
107
118
|
)
|
|
108
119
|
} {
|
|
109
120
|
| Some(progressedChain) =>
|
|
110
|
-
progressedChainsById->ChainId.Dict.set(
|
|
111
|
-
chainBeforeBatch.fetchState.chainId,
|
|
112
|
-
progressedChain,
|
|
113
|
-
)
|
|
121
|
+
progressedChainsById->ChainId.Dict.set(chainBeforeBatch.fetchState.chainId, progressedChain)
|
|
114
122
|
| None => ()
|
|
115
123
|
}
|
|
116
124
|
})
|
|
@@ -119,10 +127,27 @@ let getProgressedChainsById = {
|
|
|
119
127
|
}
|
|
120
128
|
}
|
|
121
129
|
|
|
130
|
+
// Index of the first entry of an ascending array strictly above `blockNumber`,
|
|
131
|
+
// or the array length when there is none.
|
|
132
|
+
let seekFirstAbove = (blockNumbers: array<int>, blockNumber) => {
|
|
133
|
+
let low = ref(0)
|
|
134
|
+
let high = ref(blockNumbers->Array.length)
|
|
135
|
+
while low.contents < high.contents {
|
|
136
|
+
let mid = (low.contents + high.contents) / 2
|
|
137
|
+
if blockNumbers->Array.getUnsafe(mid) > blockNumber {
|
|
138
|
+
high := mid
|
|
139
|
+
} else {
|
|
140
|
+
low := mid + 1
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
low.contents
|
|
144
|
+
}
|
|
145
|
+
|
|
122
146
|
@inline
|
|
123
147
|
let addReorgCheckpoints = (
|
|
124
148
|
~prevCheckpointId,
|
|
125
|
-
~
|
|
149
|
+
~scannedHashes: reorgHashSnapshot,
|
|
150
|
+
~shouldRollbackOnReorg,
|
|
126
151
|
~fromBlockExclusive,
|
|
127
152
|
~toBlockExclusive,
|
|
128
153
|
~chainId,
|
|
@@ -132,23 +157,30 @@ let addReorgCheckpoints = (
|
|
|
132
157
|
~mutCheckpointBlockHashes,
|
|
133
158
|
~mutCheckpointEventsProcessed,
|
|
134
159
|
) => {
|
|
135
|
-
if
|
|
136
|
-
reorgDetection.shouldRollbackOnReorg && !(reorgDetection.dataByBlockNumber->Utils.Dict.isEmpty)
|
|
137
|
-
) {
|
|
160
|
+
if shouldRollbackOnReorg {
|
|
138
161
|
let prevCheckpointId = ref(prevCheckpointId)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
162
|
+
// The snapshot already holds only in-threshold scanned hashes, ascending,
|
|
163
|
+
// so seeking to the gap's lower bound gives the gap checkpoints without
|
|
164
|
+
// rescanning the whole snapshot for every gap in the batch.
|
|
165
|
+
let blockNumbers = scannedHashes.blockNumbers
|
|
166
|
+
let length = blockNumbers->Array.length
|
|
167
|
+
let idx = ref(blockNumbers->seekFirstAbove(fromBlockExclusive))
|
|
168
|
+
while idx.contents < length && blockNumbers->Array.getUnsafe(idx.contents) < toBlockExclusive {
|
|
169
|
+
let blockNumber = blockNumbers->Array.getUnsafe(idx.contents)
|
|
170
|
+
let hash =
|
|
171
|
+
scannedHashes.hashByBlockNumber
|
|
172
|
+
->Utils.Dict.dangerouslyGetByIntNonOption(blockNumber)
|
|
173
|
+
->Option.getUnsafe
|
|
174
|
+
let checkpointId = prevCheckpointId.contents->BigInt.add(1n)
|
|
175
|
+
prevCheckpointId := checkpointId
|
|
176
|
+
|
|
177
|
+
mutCheckpointIds->Array.push(checkpointId)
|
|
178
|
+
mutCheckpointChainIds->Array.push(chainId)
|
|
179
|
+
mutCheckpointBlockNumbers->Array.push(blockNumber)
|
|
180
|
+
mutCheckpointBlockHashes->Array.push(Null.Value(hash))
|
|
181
|
+
mutCheckpointEventsProcessed->Array.push(0)
|
|
182
|
+
|
|
183
|
+
idx := idx.contents + 1
|
|
152
184
|
}
|
|
153
185
|
prevCheckpointId.contents
|
|
154
186
|
} else {
|
|
@@ -209,7 +241,8 @@ let prepareBatch = (
|
|
|
209
241
|
prevCheckpointId :=
|
|
210
242
|
addReorgCheckpoints(
|
|
211
243
|
~chainId=fetchState.chainId,
|
|
212
|
-
~
|
|
244
|
+
~scannedHashes=chainBeforeBatch.scannedHashes,
|
|
245
|
+
~shouldRollbackOnReorg=chainBeforeBatch.shouldRollbackOnReorg,
|
|
213
246
|
~prevCheckpointId=prevCheckpointId.contents,
|
|
214
247
|
~fromBlockExclusive=prevBlockNumber.contents,
|
|
215
248
|
~toBlockExclusive=blockNumber,
|
|
@@ -227,7 +260,12 @@ let prepareBatch = (
|
|
|
227
260
|
checkpointBlockNumbers->Array.push(blockNumber)->ignore
|
|
228
261
|
checkpointBlockHashes
|
|
229
262
|
->Array.push(
|
|
230
|
-
chainBeforeBatch.
|
|
263
|
+
switch chainBeforeBatch.scannedHashes.hashByBlockNumber->Utils.Dict.dangerouslyGetByIntNonOption(
|
|
264
|
+
blockNumber,
|
|
265
|
+
) {
|
|
266
|
+
| Some(hash) => Null.Value(hash)
|
|
267
|
+
| None => Null.Null
|
|
268
|
+
},
|
|
231
269
|
)
|
|
232
270
|
->ignore
|
|
233
271
|
checkpointEventsProcessed->Array.push(1)->ignore
|
|
@@ -254,7 +292,8 @@ let prepareBatch = (
|
|
|
254
292
|
prevCheckpointId :=
|
|
255
293
|
addReorgCheckpoints(
|
|
256
294
|
~chainId=fetchState.chainId,
|
|
257
|
-
~
|
|
295
|
+
~scannedHashes=chainBeforeBatch.scannedHashes,
|
|
296
|
+
~shouldRollbackOnReorg=chainBeforeBatch.shouldRollbackOnReorg,
|
|
258
297
|
~prevCheckpointId=prevCheckpointId.contents,
|
|
259
298
|
~fromBlockExclusive=prevBlockNumber.contents,
|
|
260
299
|
~toBlockExclusive=progressBlockNumberAfterBatch + 1, // Make it inclusive
|
package/src/Batch.res.mjs
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as Utils from "./Utils.res.mjs";
|
|
4
3
|
import * as ChainId from "./ChainId.res.mjs";
|
|
5
4
|
import * as FetchState from "./FetchState.res.mjs";
|
|
6
|
-
import * as ReorgDetection from "./ReorgDetection.res.mjs";
|
|
7
5
|
|
|
8
6
|
function getChainAfterBatchIfProgressed(chainBeforeBatch, progressBlockNumberAfterBatch, fetchStateAfterBatch, batchSize) {
|
|
9
7
|
if (chainBeforeBatch.progressBlockNumber < progressBlockNumberAfterBatch) {
|
|
@@ -40,23 +38,40 @@ function getProgressedChainsById(chainsBeforeBatch, batchSizePerChain, progressB
|
|
|
40
38
|
return progressedChainsById;
|
|
41
39
|
}
|
|
42
40
|
|
|
43
|
-
function
|
|
44
|
-
|
|
41
|
+
function seekFirstAbove(blockNumbers, blockNumber) {
|
|
42
|
+
let low = 0;
|
|
43
|
+
let high = blockNumbers.length;
|
|
44
|
+
while (low < high) {
|
|
45
|
+
let mid = (low + high) / 2 | 0;
|
|
46
|
+
if (blockNumbers[mid] > blockNumber) {
|
|
47
|
+
high = mid;
|
|
48
|
+
} else {
|
|
49
|
+
low = mid + 1;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
return low;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function addReorgCheckpoints(prevCheckpointId, scannedHashes, shouldRollbackOnReorg, fromBlockExclusive, toBlockExclusive, chainId, mutCheckpointIds, mutCheckpointChainIds, mutCheckpointBlockNumbers, mutCheckpointBlockHashes, mutCheckpointEventsProcessed) {
|
|
56
|
+
if (!shouldRollbackOnReorg) {
|
|
45
57
|
return prevCheckpointId;
|
|
46
58
|
}
|
|
47
59
|
let prevCheckpointId$1 = prevCheckpointId;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
let blockNumbers = scannedHashes.blockNumbers;
|
|
61
|
+
let length = blockNumbers.length;
|
|
62
|
+
let idx = seekFirstAbove(blockNumbers, fromBlockExclusive);
|
|
63
|
+
while (idx < length && blockNumbers[idx] < toBlockExclusive) {
|
|
64
|
+
let blockNumber = blockNumbers[idx];
|
|
65
|
+
let hash = scannedHashes.hashByBlockNumber[blockNumber];
|
|
66
|
+
let checkpointId = prevCheckpointId$1 + 1n;
|
|
67
|
+
prevCheckpointId$1 = checkpointId;
|
|
68
|
+
mutCheckpointIds.push(checkpointId);
|
|
69
|
+
mutCheckpointChainIds.push(chainId);
|
|
70
|
+
mutCheckpointBlockNumbers.push(blockNumber);
|
|
71
|
+
mutCheckpointBlockHashes.push(hash);
|
|
72
|
+
mutCheckpointEventsProcessed.push(0);
|
|
73
|
+
idx = idx + 1;
|
|
74
|
+
};
|
|
60
75
|
return prevCheckpointId$1;
|
|
61
76
|
}
|
|
62
77
|
|
|
@@ -86,23 +101,26 @@ function prepareBatch(checkpointIdBeforeBatch, chainsBeforeBatch, batchSizeTarge
|
|
|
86
101
|
if (blockNumber !== prevBlockNumber) {
|
|
87
102
|
let chainId = fetchState.chainId;
|
|
88
103
|
let fromBlockExclusive = prevBlockNumber;
|
|
89
|
-
let
|
|
104
|
+
let scannedHashes = chainBeforeBatch.scannedHashes;
|
|
90
105
|
let prevCheckpointId$1 = prevCheckpointId;
|
|
91
106
|
let tmp;
|
|
92
|
-
if (
|
|
107
|
+
if (chainBeforeBatch.shouldRollbackOnReorg) {
|
|
93
108
|
let prevCheckpointId$2 = prevCheckpointId$1;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
109
|
+
let blockNumbers = scannedHashes.blockNumbers;
|
|
110
|
+
let length = blockNumbers.length;
|
|
111
|
+
let idx$1 = seekFirstAbove(blockNumbers, fromBlockExclusive);
|
|
112
|
+
while (idx$1 < length && blockNumbers[idx$1] < blockNumber) {
|
|
113
|
+
let blockNumber$1 = blockNumbers[idx$1];
|
|
114
|
+
let hash = scannedHashes.hashByBlockNumber[blockNumber$1];
|
|
115
|
+
let checkpointId = prevCheckpointId$2 + 1n;
|
|
116
|
+
prevCheckpointId$2 = checkpointId;
|
|
117
|
+
checkpointIds.push(checkpointId);
|
|
118
|
+
checkpointChainIds.push(chainId);
|
|
119
|
+
checkpointBlockNumbers.push(blockNumber$1);
|
|
120
|
+
checkpointBlockHashes.push(hash);
|
|
121
|
+
checkpointEventsProcessed.push(0);
|
|
122
|
+
idx$1 = idx$1 + 1;
|
|
123
|
+
};
|
|
106
124
|
tmp = prevCheckpointId$2;
|
|
107
125
|
} else {
|
|
108
126
|
tmp = prevCheckpointId$1;
|
|
@@ -112,7 +130,8 @@ function prepareBatch(checkpointIdBeforeBatch, chainsBeforeBatch, batchSizeTarge
|
|
|
112
130
|
checkpointIds.push(checkpointId$1);
|
|
113
131
|
checkpointChainIds.push(fetchState.chainId);
|
|
114
132
|
checkpointBlockNumbers.push(blockNumber);
|
|
115
|
-
|
|
133
|
+
let hash$1 = chainBeforeBatch.scannedHashes.hashByBlockNumber[blockNumber];
|
|
134
|
+
checkpointBlockHashes.push(hash$1 !== undefined ? hash$1 : null);
|
|
116
135
|
checkpointEventsProcessed.push(1);
|
|
117
136
|
prevBlockNumber = blockNumber;
|
|
118
137
|
prevCheckpointId = checkpointId$1;
|
|
@@ -129,23 +148,26 @@ function prepareBatch(checkpointIdBeforeBatch, chainsBeforeBatch, batchSizeTarge
|
|
|
129
148
|
let chainId$1 = fetchState.chainId;
|
|
130
149
|
let toBlockExclusive = progressBlockNumberAfterBatch + 1;
|
|
131
150
|
let fromBlockExclusive$1 = prevBlockNumber;
|
|
132
|
-
let
|
|
151
|
+
let scannedHashes$1 = chainBeforeBatch.scannedHashes;
|
|
133
152
|
let prevCheckpointId$3 = prevCheckpointId;
|
|
134
153
|
let tmp$1;
|
|
135
|
-
if (
|
|
154
|
+
if (chainBeforeBatch.shouldRollbackOnReorg) {
|
|
136
155
|
let prevCheckpointId$4 = prevCheckpointId$3;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
156
|
+
let blockNumbers$1 = scannedHashes$1.blockNumbers;
|
|
157
|
+
let length$1 = blockNumbers$1.length;
|
|
158
|
+
let idx$2 = seekFirstAbove(blockNumbers$1, fromBlockExclusive$1);
|
|
159
|
+
while (idx$2 < length$1 && blockNumbers$1[idx$2] < toBlockExclusive) {
|
|
160
|
+
let blockNumber$2 = blockNumbers$1[idx$2];
|
|
161
|
+
let hash$2 = scannedHashes$1.hashByBlockNumber[blockNumber$2];
|
|
162
|
+
let checkpointId$2 = prevCheckpointId$4 + 1n;
|
|
163
|
+
prevCheckpointId$4 = checkpointId$2;
|
|
164
|
+
checkpointIds.push(checkpointId$2);
|
|
165
|
+
checkpointChainIds.push(chainId$1);
|
|
166
|
+
checkpointBlockNumbers.push(blockNumber$2);
|
|
167
|
+
checkpointBlockHashes.push(hash$2);
|
|
168
|
+
checkpointEventsProcessed.push(0);
|
|
169
|
+
idx$2 = idx$2 + 1;
|
|
170
|
+
};
|
|
149
171
|
tmp$1 = prevCheckpointId$4;
|
|
150
172
|
} else {
|
|
151
173
|
tmp$1 = prevCheckpointId$3;
|
|
@@ -200,10 +222,11 @@ function findLastEventItem(batch, chainId) {
|
|
|
200
222
|
|
|
201
223
|
export {
|
|
202
224
|
getProgressedChainsById,
|
|
225
|
+
seekFirstAbove,
|
|
203
226
|
addReorgCheckpoints,
|
|
204
227
|
prepareBatch,
|
|
205
228
|
make,
|
|
206
229
|
findFirstEventBlockNumber,
|
|
207
230
|
findLastEventItem,
|
|
208
231
|
}
|
|
209
|
-
/*
|
|
232
|
+
/* ChainId Not a pure module */
|