envio 3.6.1-subgraph → 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.
Files changed (109) hide show
  1. package/index.d.ts +197 -9
  2. package/package.json +6 -6
  3. package/src/Batch.res +64 -25
  4. package/src/Batch.res.mjs +69 -46
  5. package/src/ChainFetching.res +13 -17
  6. package/src/ChainFetching.res.mjs +9 -14
  7. package/src/ChainState.res +160 -119
  8. package/src/ChainState.res.mjs +75 -46
  9. package/src/ChainState.resi +15 -12
  10. package/src/Config.res +9 -14
  11. package/src/Config.res.mjs +6 -12
  12. package/src/Core.res +1 -15
  13. package/src/Core.res.mjs +0 -11
  14. package/src/Ecosystem.res +0 -1
  15. package/src/Envio.res +16 -3
  16. package/src/EventConfigBuilder.res +155 -40
  17. package/src/EventConfigBuilder.res.mjs +76 -24
  18. package/src/EventProcessing.res +10 -14
  19. package/src/EventProcessing.res.mjs +10 -12
  20. package/src/EventUtils.res +3 -47
  21. package/src/FetchState.res +124 -104
  22. package/src/FetchState.res.mjs +116 -100
  23. package/src/HandlerLoader.res +8 -20
  24. package/src/HandlerLoader.res.mjs +2 -11
  25. package/src/HandlerRegister.res +108 -2
  26. package/src/HandlerRegister.res.mjs +59 -10
  27. package/src/HandlerRegister.resi +4 -0
  28. package/src/Internal.res +84 -24
  29. package/src/Internal.res.mjs +33 -2
  30. package/src/LazyLoader.res +12 -11
  31. package/src/LazyLoader.res.mjs +13 -7
  32. package/src/Main.res +22 -9
  33. package/src/Main.res.mjs +18 -5
  34. package/src/MemoryStorage.res +724 -0
  35. package/src/MemoryStorage.res.mjs +601 -0
  36. package/src/PgStorage.res +18 -7
  37. package/src/PgStorage.res.mjs +14 -11
  38. package/src/ReorgDetection.res +0 -222
  39. package/src/ReorgDetection.res.mjs +0 -163
  40. package/src/Rollback.res +14 -15
  41. package/src/Rollback.res.mjs +4 -5
  42. package/src/SimulateItems.res +2 -2
  43. package/src/UserContext.res +51 -358
  44. package/src/UserContext.res.mjs +35 -271
  45. package/src/Utils.res +3 -0
  46. package/src/bindings/Vitest.res +1 -1
  47. package/src/sources/BlockStore.res +115 -5
  48. package/src/sources/BlockStore.res.mjs +25 -0
  49. package/src/sources/Evm.res +0 -1
  50. package/src/sources/Evm.res.mjs +0 -1
  51. package/src/sources/EvmHyperSyncSource.res +15 -84
  52. package/src/sources/EvmHyperSyncSource.res.mjs +24 -60
  53. package/src/sources/Fuel.res +24 -4
  54. package/src/sources/Fuel.res.mjs +23 -3
  55. package/src/sources/FuelHyperSync.res +8 -3
  56. package/src/sources/FuelHyperSync.res.mjs +5 -4
  57. package/src/sources/FuelHyperSync.resi +3 -1
  58. package/src/sources/FuelHyperSyncClient.res +7 -10
  59. package/src/sources/FuelHyperSyncSource.res +18 -45
  60. package/src/sources/FuelHyperSyncSource.res.mjs +18 -35
  61. package/src/sources/HyperSync.res +49 -229
  62. package/src/sources/HyperSync.res.mjs +74 -191
  63. package/src/sources/HyperSync.resi +11 -35
  64. package/src/sources/HyperSyncClient.res +9 -79
  65. package/src/sources/HyperSyncClient.res.mjs +2 -6
  66. package/src/sources/RequestStat.res +5 -0
  67. package/src/sources/RequestStat.res.mjs +2 -0
  68. package/src/sources/RpcSource.res +149 -43
  69. package/src/sources/RpcSource.res.mjs +104 -41
  70. package/src/sources/SimulateSource.res +8 -5
  71. package/src/sources/SimulateSource.res.mjs +6 -6
  72. package/src/sources/Source.res +89 -16
  73. package/src/sources/Source.res.mjs +50 -1
  74. package/src/sources/SourceManager.res +255 -50
  75. package/src/sources/SourceManager.res.mjs +149 -55
  76. package/src/sources/SourceManager.resi +2 -6
  77. package/src/sources/Svm.res +0 -7
  78. package/src/sources/Svm.res.mjs +0 -8
  79. package/src/sources/SvmHyperSyncClient.res +21 -16
  80. package/src/sources/SvmHyperSyncClient.res.mjs +3 -4
  81. package/src/sources/SvmHyperSyncSource.res +21 -117
  82. package/src/sources/SvmHyperSyncSource.res.mjs +8 -121
  83. package/svm.schema.json +3 -2
  84. package/src/subgraph/blocks.ts +0 -176
  85. package/src/subgraph/calls.ts +0 -213
  86. package/src/subgraph/conformance.ts +0 -99
  87. package/src/subgraph/division.ts +0 -100
  88. package/src/subgraph/errors.ts +0 -90
  89. package/src/subgraph/graph-ts-types/VERSION +0 -2
  90. package/src/subgraph/graph-ts-types/chain/arweave.d.ts +0 -70
  91. package/src/subgraph/graph-ts-types/chain/cosmos.d.ts +0 -327
  92. package/src/subgraph/graph-ts-types/chain/ethereum.d.ts +0 -233
  93. package/src/subgraph/graph-ts-types/chain/near.d.ts +0 -253
  94. package/src/subgraph/graph-ts-types/chain/starknet.d.ts +0 -32
  95. package/src/subgraph/graph-ts-types/common/collections.d.ts +0 -136
  96. package/src/subgraph/graph-ts-types/common/conversion.d.ts +0 -11
  97. package/src/subgraph/graph-ts-types/common/datasource.d.ts +0 -30
  98. package/src/subgraph/graph-ts-types/common/eager-offset.d.ts +0 -0
  99. package/src/subgraph/graph-ts-types/common/json.d.ts +0 -17
  100. package/src/subgraph/graph-ts-types/common/numbers.d.ts +0 -120
  101. package/src/subgraph/graph-ts-types/common/value.d.ts +0 -120
  102. package/src/subgraph/graph-ts-types/common/yaml.d.ts +0 -90
  103. package/src/subgraph/graph-ts-types/global/global.d.ts +0 -194
  104. package/src/subgraph/graph-ts-types/helper-functions.d.ts +0 -22
  105. package/src/subgraph/graph-ts-types/index.d.ts +0 -102
  106. package/src/subgraph/graph-ts.ts +0 -1695
  107. package/src/subgraph/hosts.ts +0 -148
  108. package/src/subgraph/runtime.ts +0 -827
  109. package/src/subgraph/scope.ts +0 -63
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<Event extends EventLike = _ProjectEvmEvent, Params = {}> = Event extends EventLike
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
- > = EvmOnEventOptions<Event, Params>;
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
- /** u64 decimal string. Cast with BigInt(...) for arithmetic. */
1034
- readonly preAmount?: string;
1035
- readonly postAmount?: string;
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.6.1-subgraph",
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.6.1-subgraph",
73
- "envio-linux-x64-musl": "3.6.1-subgraph",
74
- "envio-linux-arm64": "3.6.1-subgraph",
75
- "envio-darwin-x64": "3.6.1-subgraph",
76
- "envio-darwin-arm64": "3.6.1-subgraph"
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
- reorgDetection: ReorgDetection.t,
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
- ~reorgDetection: ReorgDetection.t,
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
- for blockNumber in fromBlockExclusive + 1 to toBlockExclusive - 1 {
140
- switch reorgDetection->ReorgDetection.getHashByBlockNumber(~blockNumber) {
141
- | Null.Value(hash) =>
142
- let checkpointId = prevCheckpointId.contents->BigInt.add(1n)
143
- prevCheckpointId := checkpointId
144
-
145
- mutCheckpointIds->Array.push(checkpointId)
146
- mutCheckpointChainIds->Array.push(chainId)
147
- mutCheckpointBlockNumbers->Array.push(blockNumber)
148
- mutCheckpointBlockHashes->Array.push(Null.Value(hash))
149
- mutCheckpointEventsProcessed->Array.push(0)
150
- | Null.Null => ()
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
- ~reorgDetection=chainBeforeBatch.reorgDetection,
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.reorgDetection->ReorgDetection.getHashByBlockNumber(~blockNumber),
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
- ~reorgDetection=chainBeforeBatch.reorgDetection,
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 addReorgCheckpoints(prevCheckpointId, reorgDetection, fromBlockExclusive, toBlockExclusive, chainId, mutCheckpointIds, mutCheckpointChainIds, mutCheckpointBlockNumbers, mutCheckpointBlockHashes, mutCheckpointEventsProcessed) {
44
- if (!(reorgDetection.shouldRollbackOnReorg && !Utils.Dict.isEmpty(reorgDetection.dataByBlockNumber))) {
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
- for (let blockNumber = fromBlockExclusive + 1, blockNumber_finish = toBlockExclusive - 1; blockNumber <= blockNumber_finish; ++blockNumber) {
49
- let hash = ReorgDetection.getHashByBlockNumber(reorgDetection, blockNumber);
50
- if (hash !== null) {
51
- let checkpointId = prevCheckpointId$1 + 1n;
52
- prevCheckpointId$1 = checkpointId;
53
- mutCheckpointIds.push(checkpointId);
54
- mutCheckpointChainIds.push(chainId);
55
- mutCheckpointBlockNumbers.push(blockNumber);
56
- mutCheckpointBlockHashes.push(hash);
57
- mutCheckpointEventsProcessed.push(0);
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 reorgDetection = chainBeforeBatch.reorgDetection;
104
+ let scannedHashes = chainBeforeBatch.scannedHashes;
90
105
  let prevCheckpointId$1 = prevCheckpointId;
91
106
  let tmp;
92
- if (reorgDetection.shouldRollbackOnReorg && !Utils.Dict.isEmpty(reorgDetection.dataByBlockNumber)) {
107
+ if (chainBeforeBatch.shouldRollbackOnReorg) {
93
108
  let prevCheckpointId$2 = prevCheckpointId$1;
94
- for (let blockNumber$1 = fromBlockExclusive + 1, blockNumber_finish = blockNumber - 1; blockNumber$1 <= blockNumber_finish; ++blockNumber$1) {
95
- let hash = ReorgDetection.getHashByBlockNumber(reorgDetection, blockNumber$1);
96
- if (hash !== null) {
97
- let checkpointId = prevCheckpointId$2 + 1n;
98
- prevCheckpointId$2 = checkpointId;
99
- checkpointIds.push(checkpointId);
100
- checkpointChainIds.push(chainId);
101
- checkpointBlockNumbers.push(blockNumber$1);
102
- checkpointBlockHashes.push(hash);
103
- checkpointEventsProcessed.push(0);
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
- checkpointBlockHashes.push(ReorgDetection.getHashByBlockNumber(chainBeforeBatch.reorgDetection, blockNumber));
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 reorgDetection$1 = chainBeforeBatch.reorgDetection;
151
+ let scannedHashes$1 = chainBeforeBatch.scannedHashes;
133
152
  let prevCheckpointId$3 = prevCheckpointId;
134
153
  let tmp$1;
135
- if (reorgDetection$1.shouldRollbackOnReorg && !Utils.Dict.isEmpty(reorgDetection$1.dataByBlockNumber)) {
154
+ if (chainBeforeBatch.shouldRollbackOnReorg) {
136
155
  let prevCheckpointId$4 = prevCheckpointId$3;
137
- for (let blockNumber$2 = fromBlockExclusive$1 + 1, blockNumber_finish$1 = toBlockExclusive - 1; blockNumber$2 <= blockNumber_finish$1; ++blockNumber$2) {
138
- let hash$1 = ReorgDetection.getHashByBlockNumber(reorgDetection$1, blockNumber$2);
139
- if (hash$1 !== null) {
140
- let checkpointId$2 = prevCheckpointId$4 + 1n;
141
- prevCheckpointId$4 = checkpointId$2;
142
- checkpointIds.push(checkpointId$2);
143
- checkpointChainIds.push(chainId$1);
144
- checkpointBlockNumbers.push(blockNumber$2);
145
- checkpointBlockHashes.push(hash$1);
146
- checkpointEventsProcessed.push(0);
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
- /* Utils Not a pure module */
232
+ /* ChainId Not a pure module */