envio 3.7.0 → 3.9.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.
Files changed (79) hide show
  1. package/README.md +2 -2
  2. package/index.d.ts +399 -156
  3. package/package.json +6 -6
  4. package/src/AddressRows.res +121 -0
  5. package/src/AddressRows.res.mjs +143 -0
  6. package/src/Batch.res +2 -0
  7. package/src/Batch.res.mjs +2 -1
  8. package/src/ChainState.res +82 -68
  9. package/src/ChainState.res.mjs +81 -65
  10. package/src/ChainState.resi +14 -9
  11. package/src/Config.res +41 -91
  12. package/src/Config.res.mjs +34 -68
  13. package/src/ContractMapping.res +66 -0
  14. package/src/ContractMapping.res.mjs +73 -0
  15. package/src/Core.res +20 -0
  16. package/src/Core.res.mjs +4 -0
  17. package/src/Envio.res +54 -76
  18. package/src/EventConfigBuilder.res +157 -30
  19. package/src/EventConfigBuilder.res.mjs +118 -24
  20. package/src/FetchState.res +59 -67
  21. package/src/FetchState.res.mjs +34 -44
  22. package/src/HandlerRegister.res +20 -11
  23. package/src/HandlerRegister.res.mjs +22 -5
  24. package/src/Hasura.res +30 -22
  25. package/src/Hasura.res.mjs +11 -5
  26. package/src/InMemoryStore.res +18 -36
  27. package/src/InMemoryStore.res.mjs +12 -26
  28. package/src/IndexerState.res +21 -1
  29. package/src/IndexerState.res.mjs +9 -3
  30. package/src/IndexerState.resi +1 -0
  31. package/src/Internal.res +33 -13
  32. package/src/Internal.res.mjs +12 -16
  33. package/src/Main.res +27 -20
  34. package/src/Main.res.mjs +19 -14
  35. package/src/MemoryStorage.res +78 -73
  36. package/src/MemoryStorage.res.mjs +66 -81
  37. package/src/Metrics.res +15 -1
  38. package/src/Metrics.res.mjs +5 -1
  39. package/src/Persistence.res +34 -18
  40. package/src/Persistence.res.mjs +8 -5
  41. package/src/PgStorage.res +189 -66
  42. package/src/PgStorage.res.mjs +93 -59
  43. package/src/Rollback.res +14 -10
  44. package/src/Rollback.res.mjs +4 -2
  45. package/src/SimulateItems.res +254 -9
  46. package/src/SimulateItems.res.mjs +214 -47
  47. package/src/TestIndexer.res +119 -121
  48. package/src/TestIndexer.res.mjs +95 -100
  49. package/src/Utils.res +7 -0
  50. package/src/Utils.res.mjs +9 -2
  51. package/src/Writing.res +2 -0
  52. package/src/Writing.res.mjs +2 -1
  53. package/src/bindings/ClickHouse.res +39 -6
  54. package/src/bindings/ClickHouse.res.mjs +29 -4
  55. package/src/bindings/NodeJs.res +9 -0
  56. package/src/bindings/NodeJs.res.mjs +8 -1
  57. package/src/bindings/Postgres.res +9 -0
  58. package/src/bindings/Postgres.res.mjs +3 -0
  59. package/src/db/EntityHistory.res +12 -18
  60. package/src/db/EntityHistory.res.mjs +3 -14
  61. package/src/db/InternalTable.res +182 -65
  62. package/src/db/InternalTable.res.mjs +183 -73
  63. package/src/db/Table.res +24 -0
  64. package/src/db/Table.res.mjs +20 -1
  65. package/src/sources/AddressSet.res +0 -22
  66. package/src/sources/AddressStore.res +48 -88
  67. package/src/sources/AddressStore.res.mjs +11 -22
  68. package/src/sources/EvmHyperSyncSource.res +3 -2
  69. package/src/sources/SimulateSource.res +8 -4
  70. package/src/sources/SimulateSource.res.mjs +7 -3
  71. package/src/sources/Svm.res +34 -7
  72. package/src/sources/Svm.res.mjs +32 -4
  73. package/src/sources/SvmHyperSyncClient.res +24 -30
  74. package/src/sources/SvmHyperSyncClient.res.mjs +3 -2
  75. package/src/sources/SvmHyperSyncSource.res +88 -36
  76. package/src/sources/SvmHyperSyncSource.res.mjs +71 -39
  77. package/src/sources/TransactionStore.res +38 -0
  78. package/src/sources/TransactionStore.res.mjs +5 -0
  79. package/svm.schema.json +37 -82
@@ -1469,7 +1469,6 @@ let warnAddressRegistration = (
1469
1469
 
1470
1470
  // A rejected registration is simply absent from every partition, so without a
1471
1471
  // warning the user sees a contract that never indexes and nothing saying why.
1472
- // Shared by config-time registration in `make` and by dynamic registration.
1473
1472
  let warnRejectedRegistration = (
1474
1473
  verdict: AddressStore.verdict,
1475
1474
  ~chainId: ChainId.t,
@@ -1477,20 +1476,7 @@ let warnRejectedRegistration = (
1477
1476
  ~contractName: string,
1478
1477
  ) =>
1479
1478
  switch verdict {
1480
- | Conflict({existingContractName}) =>
1481
- warnAddressRegistration(
1482
- ~chainId,
1483
- ~contractAddress,
1484
- ~params={
1485
- "existingContractType": existingContractName,
1486
- "newContractType": contractName,
1487
- },
1488
- `Skipping contract registration: Contract address is already registered for one contract and cannot be registered for another contract.`,
1489
- )
1490
1479
  | Duplicate({effectiveStartBlock, existingEffectiveStartBlock}) =>
1491
- // FIXME: Instead of filtering out duplicates, we should check the block
1492
- // number first. If a new registration has an earlier block number we
1493
- // should register it for the missing block range.
1494
1480
  if existingEffectiveStartBlock > effectiveStartBlock {
1495
1481
  warnAddressRegistration(
1496
1482
  ~chainId,
@@ -1499,7 +1485,7 @@ let warnRejectedRegistration = (
1499
1485
  "existingBlockNumber": existingEffectiveStartBlock,
1500
1486
  "newBlockNumber": effectiveStartBlock,
1501
1487
  },
1502
- `Skipping contract registration: Contract address is already registered at a later block number. Currently registration of the same contract address is not supported by Envio. Reach out to us if it's a problem for you.`,
1488
+ `Skipping same-contract re-registration: the address is already registered for this contract. The start block does not move earlier.`,
1503
1489
  )
1504
1490
  }
1505
1491
  | Invalid =>
@@ -1723,9 +1709,10 @@ let registerDynamicContracts = (
1723
1709
  // exactly what this batch adds.
1724
1710
  let idCursor = addressStore->AddressStore.nextId
1725
1711
  // The store resolves each address against both what it already holds and the
1726
- // batch's own earlier entries, so two contracts claiming one address inside a
1727
- // single batch conflict the same way as across batches. It also decides which
1728
- // additions this chain fetches for, since it's what holds the contract list.
1712
+ // batch's own earlier entries, so the same address registered twice for one
1713
+ // contract inside a single batch is a duplicate just as it is across batches.
1714
+ // It also decides which additions this chain fetches for, since it's what
1715
+ // holds the contract list.
1729
1716
  let verdicts = addressStore->AddressStore.registerBatch(registrations)
1730
1717
 
1731
1718
  let registeringContractNames = []
@@ -1741,7 +1728,7 @@ let registerDynamicContracts = (
1741
1728
  // no partition to build. The address is still stored and persisted, so a
1742
1729
  // config that later adds address-dependent events picks it up on restart.
1743
1730
  | Added({fetchable: false}) => ()
1744
- | Conflict(_) | Duplicate(_) | Invalid =>
1731
+ | Duplicate(_) | Invalid =>
1745
1732
  verdict->warnRejectedRegistration(
1746
1733
  ~chainId=fetchState.chainId,
1747
1734
  ~contractAddress=registration.address,
@@ -2627,7 +2614,7 @@ let make = (
2627
2614
  ~endBlock,
2628
2615
  ~onEventRegistrations: array<Internal.onEventRegistration>,
2629
2616
  ~addressStore: AddressStore.t,
2630
- ~addresses: array<Internal.indexingAddress>,
2617
+ ~addressRows: AddressRows.seedRows,
2631
2618
  ~maxAddrInPartition,
2632
2619
  ~chainId: ChainId.t,
2633
2620
  ~maxOnBlockBufferSize,
@@ -2681,27 +2668,24 @@ let make = (
2681
2668
  )
2682
2669
 
2683
2670
  // Every address the chain indexes goes into the store — including ones whose
2684
- // contract has no address-dependent events, so a later registration of the
2685
- // same address still conflicts and the address is still persisted.
2671
+ // contract has no address-dependent events, so the address is still persisted
2672
+ // and a config that later adds events picks it up.
2673
+ //
2674
+ // These rows come from the config or from a resume, so they're already stored
2675
+ // and must never be drained back into a write. Only the rows the store
2676
+ // refuses come back: a resume seeds millions of them.
2686
2677
  addressStore
2687
- // These come from the config or from a resume, so they're already stored and
2688
- // must never be drained back into a write.
2689
- ->AddressStore.seedBatch(
2690
- addresses->Array.map((contract): AddressStore.registration => {
2691
- address: contract.address,
2692
- contractName: contract.contractName,
2693
- registrationBlock: contract.registrationBlock,
2694
- }),
2695
- )
2696
- // Verdicts are in the batch's order, so they line up with `addresses`. A
2697
- // config address the store rejects is dropped exactly like a dynamic one, and
2698
- // needs the same warning — restored dynamic addresses come through here too.
2699
- ->Array.forEachWithIndex((verdict, idx) => {
2700
- let contract = addresses->Array.getUnsafe(idx)
2701
- verdict->warnRejectedRegistration(
2678
+ ->AddressStore.seedRows(addressRows)
2679
+ ->Array.forEach(rejected => {
2680
+ warnAddressRegistration(
2702
2681
  ~chainId,
2703
- ~contractAddress=contract.address,
2704
- ~contractName=contract.contractName,
2682
+ ~contractAddress=rejected.address,
2683
+ ~params={
2684
+ "contractName": rejected.contractName,
2685
+ "existingBlockNumber": rejected.existingEffectiveStartBlock,
2686
+ "newBlockNumber": rejected.effectiveStartBlock,
2687
+ },
2688
+ `Skipping a stored address: it is already registered for this contract.`,
2705
2689
  )
2706
2690
  })
2707
2691
 
@@ -2709,23 +2693,24 @@ let make = (
2709
2693
  let clientFilteredContracts = Utils.Set.make()
2710
2694
  let registeringSetsByContract = Dict.make()
2711
2695
 
2712
- addresses->Array.forEach(contract => {
2713
- let contractName = contract.contractName
2714
-
2715
- // Only addresses whose contract has events that depend on addresses get
2716
- // registered for active fetching via partitions.
2696
+ // What each contract needs a partition for is read back off the store rather
2697
+ // than re-derived from the seeded columns: the store is what resolved the
2698
+ // rows, including the ones it refused.
2699
+ contractNamesWithNormalEvents
2700
+ ->Utils.Set.toArray
2701
+ ->Array.forEach(contractName => {
2702
+ if addressStore->AddressStore.contractCount(contractName) > 0 {
2703
+ registeringSetsByContract->Dict.set(
2704
+ contractName,
2705
+ addressStore->AddressStore.makeSet(~contractName),
2706
+ )
2707
+ }
2708
+ })
2709
+ addressStore
2710
+ ->AddressStore.dynamicContractNames
2711
+ ->Array.forEach(contractName => {
2717
2712
  if contractNamesWithNormalEvents->Utils.Set.has(contractName) {
2718
- if !(registeringSetsByContract->Dict.has(contractName)) {
2719
- registeringSetsByContract->Dict.set(
2720
- contractName,
2721
- addressStore->AddressStore.makeSet(~contractName),
2722
- )
2723
- }
2724
-
2725
- // Detect dynamic contracts by registrationBlock
2726
- if contract.registrationBlock !== -1 {
2727
- dynamicContracts->Utils.Set.add(contractName)->ignore
2728
- }
2713
+ dynamicContracts->Utils.Set.add(contractName)->ignore
2729
2714
  }
2730
2715
  })
2731
2716
 
@@ -2768,7 +2753,7 @@ let make = (
2768
2753
  ) {
2769
2754
  JsError.throwWithMessage(
2770
2755
  `Invalid configuration: Nothing to fetch on chain ${chainId->ChainId.toString}. ` ++
2771
- `addresses=${addresses->Array.length->Int.toString}, ` ++
2756
+ `addresses=${addressRows.addresses->Array.length->Int.toString}, ` ++
2772
2757
  `onEventRegistrations=${onEventRegistrations->Array.length->Int.toString}, ` ++
2773
2758
  `normalRegistrations=${normalRegistrations
2774
2759
  ->Array.length
@@ -2845,20 +2830,28 @@ let rollbackPendingQueries = (mutPendingQueries: array<pendingQuery>, ~targetBlo
2845
2830
  adjusted
2846
2831
  }
2847
2832
 
2833
+ type rollbackResult = {
2834
+ fetchState: t,
2835
+ // The registrations the prune dropped, for the storage that deletes their rows.
2836
+ rolledBackAddresses: array<AddressStore.rolledBackAddress>,
2837
+ }
2838
+
2848
2839
  /**
2849
2840
  Rolls back fetch state to the given valid block.
2841
+ Prunes the store first, then rebuilds partitions from it: an address survives iff
2842
+ `filterByRegistrationBlock` keeps it, so the partitions and the rows the caller
2843
+ goes on to delete can't disagree about which registrations died.
2850
2844
  Always recreates optimized partitions to avoid duplicate addresses:
2851
2845
  - Wildcard: only rollback latestFetchedBlock
2852
2846
  - Non-wildcard with lfb <= target: keep, adjust pending queries and mergeBlock
2853
2847
  - Non-wildcard with lfb > target: delete, track addresses for recreation
2854
2848
  */
2855
- let rollback = (fetchState: t, ~addressStore: AddressStore.t, ~targetBlockNumber) => {
2856
- // Step 1: Prune addresses registered after the target block. The pruned store
2857
- // is then the source of truth for partition cleanup below — an address
2858
- // survives iff `filterByRegistrationBlock` keeps it.
2859
- addressStore->AddressStore.rollback(targetBlockNumber)->ignore
2860
-
2861
- // Step 2: Categorize partitions
2849
+ let rollback = (
2850
+ fetchState: t,
2851
+ ~rolledBackAddressStore: AddressStore.t,
2852
+ ~targetBlockNumber,
2853
+ ): rollbackResult => {
2854
+ let rolledBackAddresses = rolledBackAddressStore->AddressStore.rollback(targetBlockNumber)
2862
2855
  let keptPartitions = []
2863
2856
  let nextKeptIdRef = ref(0)
2864
2857
  let registeringSetsByContract: dict<AddressSet.t> = Dict.make()
@@ -2935,10 +2928,9 @@ let rollback = (fetchState: t, ~addressStore: AddressStore.t, ~targetBlockNumber
2935
2928
  }
2936
2929
  }
2937
2930
 
2938
- // Step 3: Recreate partitions from deleted partition addresses
2939
2931
  let optimizedPartitions = createPartitions(
2940
2932
  ~registeringSetsByContract,
2941
- ~addressStore,
2933
+ ~addressStore=rolledBackAddressStore,
2942
2934
  ~dynamicContracts=fetchState.optimizedPartitions.dynamicContracts,
2943
2935
  ~clientFilteredContracts=fetchState.optimizedPartitions.clientFilteredContracts,
2944
2936
  ~normalSelection=fetchState.normalSelection,
@@ -2949,8 +2941,7 @@ let rollback = (fetchState: t, ~addressStore: AddressStore.t, ~targetBlockNumber
2949
2941
  ~knownHeight=fetchState.knownHeight,
2950
2942
  )
2951
2943
 
2952
- // Step 4: Update state
2953
- {
2944
+ let rolledBack = {
2954
2945
  ...fetchState,
2955
2946
  // TODO: Test this. Currently it's not tested.
2956
2947
  latestOnBlockBlockNumber: Pervasives.min(
@@ -2969,6 +2960,7 @@ let rollback = (fetchState: t, ~addressStore: AddressStore.t, ~targetBlockNumber
2969
2960
  targetBlockNumber
2970
2961
  ),
2971
2962
  )
2963
+ {fetchState: rolledBack, rolledBackAddresses}
2972
2964
  }
2973
2965
 
2974
2966
  // Reset pending queries by removing in-flight queries (ones without fetchedBlock).
@@ -1123,25 +1123,16 @@ function warnRejectedRegistration(verdict, chainId, contractAddress, contractNam
1123
1123
  contractName: contractName
1124
1124
  }, `Skipping contract registration: Not a valid address for this chain's ecosystem.`);
1125
1125
  }
1126
- switch (verdict.TAG) {
1127
- case "Added" :
1128
- return;
1129
- case "Duplicate" :
1130
- let existingEffectiveStartBlock = verdict.existingEffectiveStartBlock;
1131
- let effectiveStartBlock = verdict.effectiveStartBlock;
1132
- if (existingEffectiveStartBlock > effectiveStartBlock) {
1133
- return warnAddressRegistration(chainId, contractAddress, {
1134
- existingBlockNumber: existingEffectiveStartBlock,
1135
- newBlockNumber: effectiveStartBlock
1136
- }, `Skipping contract registration: Contract address is already registered at a later block number. Currently registration of the same contract address is not supported by Envio. Reach out to us if it's a problem for you.`);
1137
- } else {
1138
- return;
1139
- }
1140
- case "Conflict" :
1141
- return warnAddressRegistration(chainId, contractAddress, {
1142
- existingContractType: verdict.existingContractName,
1143
- newContractType: contractName
1144
- }, `Skipping contract registration: Contract address is already registered for one contract and cannot be registered for another contract.`);
1126
+ if (verdict.TAG === "Added") {
1127
+ return;
1128
+ }
1129
+ let existingEffectiveStartBlock = verdict.existingEffectiveStartBlock;
1130
+ let effectiveStartBlock = verdict.effectiveStartBlock;
1131
+ if (existingEffectiveStartBlock > effectiveStartBlock) {
1132
+ return warnAddressRegistration(chainId, contractAddress, {
1133
+ existingBlockNumber: existingEffectiveStartBlock,
1134
+ newBlockNumber: effectiveStartBlock
1135
+ }, `Skipping same-contract re-registration: the address is already registered for this contract. The start block does not move earlier.`);
1145
1136
  }
1146
1137
  }
1147
1138
 
@@ -1727,7 +1718,7 @@ function getReadyItemsCount(fetchState, targetSize, fromItem) {
1727
1718
  return acc;
1728
1719
  }
1729
1720
 
1730
- function make$1(startBlock, endBlock, onEventRegistrations, addressStore, addresses, maxAddrInPartition, chainId, maxOnBlockBufferSize, knownHeight, progressBlockNumberOpt, onBlockRegistrationsOpt, blockLagOpt, firstEventBlockOpt, clientFilterAddressThresholdOpt, isResumedOpt) {
1721
+ function make$1(startBlock, endBlock, onEventRegistrations, addressStore, addressRows, maxAddrInPartition, chainId, maxOnBlockBufferSize, knownHeight, progressBlockNumberOpt, onBlockRegistrationsOpt, blockLagOpt, firstEventBlockOpt, clientFilterAddressThresholdOpt, isResumedOpt) {
1731
1722
  let progressBlockNumber = progressBlockNumberOpt !== undefined ? progressBlockNumberOpt : startBlock - 1 | 0;
1732
1723
  let onBlockRegistrations = onBlockRegistrationsOpt !== undefined ? onBlockRegistrationsOpt : [];
1733
1724
  let blockLag = blockLagOpt !== undefined ? blockLagOpt : 0;
@@ -1762,29 +1753,24 @@ function make$1(startBlock, endBlock, onEventRegistrations, addressStore, addres
1762
1753
  });
1763
1754
  }
1764
1755
  let normalSelection = makeSelection(normalRegistrations, true, undefined);
1765
- AddressStore.seedBatch(addressStore, addresses.map(contract => ({
1766
- address: contract.address,
1767
- contractName: contract.contractName,
1768
- registrationBlock: contract.registrationBlock
1769
- }))).forEach((verdict, idx) => {
1770
- let contract = addresses[idx];
1771
- warnRejectedRegistration(verdict, chainId, contract.address, contract.contractName);
1772
- });
1756
+ AddressStore.seedRows(addressStore, addressRows).forEach(rejected => warnAddressRegistration(chainId, rejected.address, {
1757
+ contractName: rejected.contractName,
1758
+ existingBlockNumber: rejected.existingEffectiveStartBlock,
1759
+ newBlockNumber: rejected.effectiveStartBlock
1760
+ }, `Skipping a stored address: it is already registered for this contract.`));
1773
1761
  let dynamicContracts = new Set();
1774
1762
  let clientFilteredContracts = new Set();
1775
1763
  let registeringSetsByContract = {};
1776
- addresses.forEach(contract => {
1777
- let contractName = contract.contractName;
1764
+ Array.from(contractNamesWithNormalEvents).forEach(contractName => {
1765
+ if (addressStore.contractCount(contractName) > 0) {
1766
+ registeringSetsByContract[contractName] = AddressStore.makeSet(addressStore, contractName, undefined);
1767
+ return;
1768
+ }
1769
+ });
1770
+ addressStore.dynamicContractNames().forEach(contractName => {
1778
1771
  if (contractNamesWithNormalEvents.has(contractName)) {
1779
- if (!(contractName in registeringSetsByContract)) {
1780
- registeringSetsByContract[contractName] = AddressStore.makeSet(addressStore, contractName, undefined);
1781
- }
1782
- if (contract.registrationBlock !== -1) {
1783
- dynamicContracts.add(contractName);
1784
- return;
1785
- } else {
1786
- return;
1787
- }
1772
+ dynamicContracts.add(contractName);
1773
+ return;
1788
1774
  }
1789
1775
  });
1790
1776
  if (clientFilterAddressThreshold !== undefined) {
@@ -1797,7 +1783,7 @@ function make$1(startBlock, endBlock, onEventRegistrations, addressStore, addres
1797
1783
  }
1798
1784
  let optimizedPartitions = createPartitions(registeringSetsByContract, addressStore, dynamicContracts, clientFilteredContracts, normalSelection, maxAddrInPartition, partitions.length, partitions, progressBlockNumber, knownHeight);
1799
1785
  if (optimizedPartitions.idsInAscOrder.length === 0 && Utils.$$Array.isEmpty(onBlockRegistrations)) {
1800
- Stdlib_JsError.throwWithMessage(`Invalid configuration: Nothing to fetch on chain ` + ChainId.toString(chainId) + `. ` + (`addresses=` + addresses.length.toString() + `, `) + (`onEventRegistrations=` + onEventRegistrations.length.toString() + `, `) + (`normalRegistrations=` + normalRegistrations.length.toString() + `. `) + `Make sure that you provided at least one contract address to index, or have events with Wildcard mode enabled, or have onBlock handlers.`);
1786
+ Stdlib_JsError.throwWithMessage(`Invalid configuration: Nothing to fetch on chain ` + ChainId.toString(chainId) + `. ` + (`addresses=` + addressRows.addresses.length.toString() + `, `) + (`onEventRegistrations=` + onEventRegistrations.length.toString() + `, `) + (`normalRegistrations=` + normalRegistrations.length.toString() + `. `) + `Make sure that you provided at least one contract address to index, or have events with Wildcard mode enabled, or have onBlock handlers.`);
1801
1787
  }
1802
1788
  let buffer = [];
1803
1789
  let latestOnBlockBlockNumber;
@@ -1861,8 +1847,8 @@ function rollbackPendingQueries(mutPendingQueries, targetBlockNumber) {
1861
1847
  return adjusted;
1862
1848
  }
1863
1849
 
1864
- function rollback(fetchState, addressStore, targetBlockNumber) {
1865
- addressStore.rollback(targetBlockNumber);
1850
+ function rollback(fetchState, rolledBackAddressStore, targetBlockNumber) {
1851
+ let rolledBackAddresses = rolledBackAddressStore.rollback(targetBlockNumber);
1866
1852
  let keptPartitions = [];
1867
1853
  let nextKeptIdRef = 0;
1868
1854
  let registeringSetsByContract = {};
@@ -1921,8 +1907,8 @@ function rollback(fetchState, addressStore, targetBlockNumber) {
1921
1907
  });
1922
1908
  }
1923
1909
  }
1924
- let optimizedPartitions = createPartitions(registeringSetsByContract, addressStore, fetchState.optimizedPartitions.dynamicContracts, fetchState.optimizedPartitions.clientFilteredContracts, fetchState.normalSelection, fetchState.optimizedPartitions.maxAddrInPartition, nextKeptIdRef, keptPartitions, targetBlockNumber, fetchState.knownHeight);
1925
- return updateInternal({
1910
+ let optimizedPartitions = createPartitions(registeringSetsByContract, rolledBackAddressStore, fetchState.optimizedPartitions.dynamicContracts, fetchState.optimizedPartitions.clientFilteredContracts, fetchState.normalSelection, fetchState.optimizedPartitions.maxAddrInPartition, nextKeptIdRef, keptPartitions, targetBlockNumber, fetchState.knownHeight);
1911
+ let rolledBack = updateInternal({
1926
1912
  optimizedPartitions: fetchState.optimizedPartitions,
1927
1913
  startBlock: fetchState.startBlock,
1928
1914
  endBlock: fetchState.endBlock,
@@ -1941,6 +1927,10 @@ function rollback(fetchState, addressStore, targetBlockNumber) {
1941
1927
  tmp = item.kind === 0 ? item.blockNumber : item.blockNumber;
1942
1928
  return tmp <= targetBlockNumber;
1943
1929
  }), true, undefined, undefined);
1930
+ return {
1931
+ fetchState: rolledBack,
1932
+ rolledBackAddresses: rolledBackAddresses
1933
+ };
1944
1934
  }
1945
1935
 
1946
1936
  function resetPendingQueries(fetchState) {
@@ -154,6 +154,7 @@ let buildOnEventRegistrationWith = (
154
154
  ~isWildcard,
155
155
  ~handler,
156
156
  ~contractRegister,
157
+ ~fieldSelection?,
157
158
  ~startBlock?,
158
159
  ) :> Internal.onEventRegistration)
159
160
  | Evm =>
@@ -299,21 +300,29 @@ let addOnEventRegistration = (
299
300
  let fieldSelection = switch eventOptions->Option.flatMap(v => v.fields) {
300
301
  | None => None
301
302
  | Some(fields) =>
302
- // Only EVM resolves a registration selection of its own; Fuel and SVM take
303
- // theirs from the event config, so an inline one would be silently dropped.
304
- if registration.config.ecosystem.name !== Evm {
303
+ switch registration.config.ecosystem.name {
304
+ | Evm =>
305
+ Some(
306
+ EventConfigBuilder.resolveInlineFieldSelection(
307
+ fields,
308
+ ~contractName,
309
+ ~eventName,
310
+ ~enableRawEvents=registration.config.enableRawEvents,
311
+ ),
312
+ )
313
+ | Svm =>
314
+ Some(
315
+ EventConfigBuilder.resolveSvmInlineFieldSelection(
316
+ fields,
317
+ ~contractName,
318
+ ~eventName,
319
+ ),
320
+ )
321
+ | Fuel =>
305
322
  JsError.throwWithMessage(
306
323
  `The fields option of the "${eventName}" event registration on contract "${contractName}" is only supported on EVM. Select the fields in your config instead.`,
307
324
  )
308
325
  }
309
- Some(
310
- EventConfigBuilder.resolveInlineFieldSelection(
311
- fields,
312
- ~contractName,
313
- ~eventName,
314
- ~enableRawEvents=registration.config.enableRawEvents,
315
- ),
316
- )
317
326
  }
318
327
  let matched = ref(false)
319
328
  registration.config.chainMap
@@ -102,7 +102,7 @@ function buildOnEventRegistrationWith(config, chainId, eventConfig, isWildcard,
102
102
  case "fuel" :
103
103
  return EventConfigBuilder.buildFuelOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, startBlock);
104
104
  case "svm" :
105
- return EventConfigBuilder.buildSvmOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, startBlock);
105
+ return EventConfigBuilder.buildSvmOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, fieldSelection, startBlock);
106
106
  }
107
107
  }
108
108
 
@@ -200,7 +200,24 @@ function addOnEventRegistration(registration, contractName, eventName, handler,
200
200
  let isWildcard = Stdlib_Option.getOr(Stdlib_Option.flatMap(eventOptions, v => v.wildcard), false);
201
201
  let where = Stdlib_Option.flatMap(eventOptions, v => v.where);
202
202
  let fields = Stdlib_Option.flatMap(eventOptions, v => v.fields);
203
- let fieldSelection = fields !== undefined ? (registration.config.ecosystem.name !== "evm" ? Stdlib_JsError.throwWithMessage(`The fields option of the "` + eventName + `" event registration on contract "` + contractName + `" is only supported on EVM. Select the fields in your config instead.`) : undefined, EventConfigBuilder.resolveInlineFieldSelection(fields, contractName, eventName, registration.config.enableRawEvents)) : undefined;
203
+ let fieldSelection;
204
+ if (fields !== undefined) {
205
+ let fields$1 = Primitive_option.valFromOption(fields);
206
+ let match = registration.config.ecosystem.name;
207
+ switch (match) {
208
+ case "evm" :
209
+ fieldSelection = EventConfigBuilder.resolveInlineFieldSelection(fields$1, contractName, eventName, registration.config.enableRawEvents);
210
+ break;
211
+ case "fuel" :
212
+ fieldSelection = Stdlib_JsError.throwWithMessage(`The fields option of the "` + eventName + `" event registration on contract "` + contractName + `" is only supported on EVM. Select the fields in your config instead.`);
213
+ break;
214
+ case "svm" :
215
+ fieldSelection = EventConfigBuilder.resolveSvmInlineFieldSelection(fields$1, contractName, eventName);
216
+ break;
217
+ }
218
+ } else {
219
+ fieldSelection = undefined;
220
+ }
204
221
  let matched = {
205
222
  contents: false
206
223
  };
@@ -220,8 +237,8 @@ function addOnEventRegistration(registration, contractName, eventName, handler,
220
237
  if (matched.contents) {
221
238
  return;
222
239
  }
223
- let match = describeConfigured(registration, contractName);
224
- let eventNames = match[1];
240
+ let match$1 = describeConfigured(registration, contractName);
241
+ let eventNames = match$1[1];
225
242
  let listOr = (names, empty) => {
226
243
  if (Utils.$$Array.isEmpty(names)) {
227
244
  return empty;
@@ -230,7 +247,7 @@ function addOnEventRegistration(registration, contractName, eventName, handler,
230
247
  }
231
248
  };
232
249
  if (Utils.$$Array.isEmpty(eventNames)) {
233
- return Stdlib_JsError.throwWithMessage(`Contract "` + contractName + `" is not configured on any chain, so its handler for "` + eventName + `" would never run. Add it to your config, or remove the registration. Configured contracts: ` + listOr(match[0], "none") + `.`);
250
+ return Stdlib_JsError.throwWithMessage(`Contract "` + contractName + `" is not configured on any chain, so its handler for "` + eventName + `" would never run. Add it to your config, or remove the registration. Configured contracts: ` + listOr(match$1[0], "none") + `.`);
234
251
  } else {
235
252
  return Stdlib_JsError.throwWithMessage(`Event "` + eventName + `" is not configured on contract "` + contractName + `", so its handler would never run. Add it to your config, or remove the registration. Configured events on "` + contractName + `": ` + listOr(eventNames, "none") + `.`);
236
253
  }
package/src/Hasura.res CHANGED
@@ -286,9 +286,7 @@ let createSelectPermission = async (
286
286
  // column joins alongside the id — without it the relationship would resolve to
287
287
  // another chain's row with the same id.
288
288
  let makeColumnMapping = (~relationalKey, ~isDerivedFrom, ~chainIdColumn) => {
289
- let pairs = [
290
- isDerivedFrom ? `"id": "${relationalKey}"` : `"${relationalKey}": "id"`,
291
- ]
289
+ let pairs = [isDerivedFrom ? `"id": "${relationalKey}"` : `"${relationalKey}": "id"`]
292
290
  switch chainIdColumn {
293
291
  | Some(column) => pairs->Array.push(`"${column}": "${column}"`)->ignore
294
292
  | None => ()
@@ -309,7 +307,6 @@ let createEntityRelationship = async (
309
307
  ~chainIdColumn: option<string>,
310
308
  ~comment: option<string>=?,
311
309
  ) => {
312
-
313
310
  let tableJson = {
314
311
  "schema": pgSchema,
315
312
  "name": tableName,
@@ -347,15 +344,7 @@ let createEntityRelationship = async (
347
344
  )
348
345
  }
349
346
 
350
- let trackDatabase = async (
351
- ~endpoint,
352
- ~auth,
353
- ~pgSchema,
354
- ~userEntities: array<Internal.entityConfig>,
355
- ~aggregateEntities,
356
- ~responseLimit,
357
- ~schema,
358
- ) => {
347
+ let makeTableConfigs = (~userEntities: array<Internal.entityConfig>) => {
359
348
  let exposedInternalTableConfigs = [
360
349
  {
361
350
  tableName: InternalTable.RawEvents.table.tableName,
@@ -373,12 +362,27 @@ let trackDatabase = async (
373
362
  columnConfigs: dict{},
374
363
  },
375
364
  ]
376
- let userTableConfigs = userEntities->Array.map(entity => {
377
- tableName: entity.table.tableName,
378
- description: entity.table.description,
379
- columnConfigs: entity.table->makeColumnConfigs,
380
- })
381
- let tableConfigs = [exposedInternalTableConfigs, userTableConfigs]->Array.flat
365
+ let userTableConfigs =
366
+ userEntities
367
+ ->Array.filter(entity => !entity.internal)
368
+ ->Array.map(entity => {
369
+ tableName: entity.table.tableName,
370
+ description: entity.table.description,
371
+ columnConfigs: entity.table->makeColumnConfigs,
372
+ })
373
+ [exposedInternalTableConfigs, userTableConfigs]->Array.flat
374
+ }
375
+
376
+ let trackDatabase = async (
377
+ ~endpoint,
378
+ ~auth,
379
+ ~pgSchema,
380
+ ~userEntities: array<Internal.entityConfig>,
381
+ ~aggregateEntities,
382
+ ~responseLimit,
383
+ ~schema,
384
+ ) => {
385
+ let tableConfigs = makeTableConfigs(~userEntities)
382
386
  let tableNames = tableConfigs->Array.map(c => c.tableName)
383
387
 
384
388
  Logging.info("Tracking tables in Hasura")
@@ -414,10 +418,14 @@ let trackDatabase = async (
414
418
  ->Option.flatMap(e => e.table->Table.getChainIdField)
415
419
  ->Option.map(Table.getPgDbFieldName)
416
420
 
417
- for i in 0 to userEntities->Array.length - 1 {
418
- let entityConfig = userEntities->Array.getUnsafe(i)
421
+ // Relationships to an @internal entity can't exist here: codegen rejects a
422
+ // reference from an exposed entity to an @internal one.
423
+ let exposedEntities = userEntities->Array.filter(e => !e.internal)
424
+ for i in 0 to exposedEntities->Array.length - 1 {
425
+ let entityConfig = exposedEntities->Array.getUnsafe(i)
419
426
  let {tableName} = entityConfig.table
420
- let ownChainIdColumn = entityConfig.table->Table.getChainIdField->Option.map(Table.getPgDbFieldName)
427
+ let ownChainIdColumn =
428
+ entityConfig.table->Table.getChainIdField->Option.map(Table.getPgDbFieldName)
421
429
  let sharedChainIdColumn = mappedEntity =>
422
430
  switch (ownChainIdColumn, chainIdColumnOf(mappedEntity)) {
423
431
  | (Some(column), Some(_)) => Some(column)
@@ -285,7 +285,7 @@ async function createEntityRelationship(endpoint, auth, pgSchema, tableName, rel
285
285
  });
286
286
  }
287
287
 
288
- async function trackDatabase(endpoint, auth, pgSchema, userEntities, aggregateEntities, responseLimit, schema) {
288
+ function makeTableConfigs(userEntities) {
289
289
  let exposedInternalTableConfigs = [
290
290
  {
291
291
  tableName: InternalTable.RawEvents.table.tableName,
@@ -303,15 +303,19 @@ async function trackDatabase(endpoint, auth, pgSchema, userEntities, aggregateEn
303
303
  columnConfigs: {}
304
304
  }
305
305
  ];
306
- let userTableConfigs = userEntities.map(entity => ({
306
+ let userTableConfigs = userEntities.filter(entity => !entity.internal).map(entity => ({
307
307
  tableName: entity.table.tableName,
308
308
  description: entity.table.description,
309
309
  columnConfigs: makeColumnConfigs(entity.table)
310
310
  }));
311
- let tableConfigs = [
311
+ return [
312
312
  exposedInternalTableConfigs,
313
313
  userTableConfigs
314
314
  ].flat();
315
+ }
316
+
317
+ async function trackDatabase(endpoint, auth, pgSchema, userEntities, aggregateEntities, responseLimit, schema) {
318
+ let tableConfigs = makeTableConfigs(userEntities);
315
319
  let tableNames = tableConfigs.map(c => c.tableName);
316
320
  Logging.info("Tracking tables in Hasura");
317
321
  await clearHasuraMetadata(endpoint, auth);
@@ -322,8 +326,9 @@ async function trackDatabase(endpoint, auth, pgSchema, userEntities, aggregateEn
322
326
  await createSelectPermission(endpoint, auth, tableName, pgSchema, responseLimit, aggregateEntities);
323
327
  }
324
328
  let chainIdColumnOf = entityName => Stdlib_Option.map(Stdlib_Option.flatMap(userEntities.find(e => e.name === entityName), e => Table.getChainIdField(e.table)), Table.getPgDbFieldName);
325
- for (let i$1 = 0, i_finish$1 = userEntities.length; i$1 < i_finish$1; ++i$1) {
326
- let entityConfig = userEntities[i$1];
329
+ let exposedEntities = userEntities.filter(e => !e.internal);
330
+ for (let i$1 = 0, i_finish$1 = exposedEntities.length; i$1 < i_finish$1; ++i$1) {
331
+ let entityConfig = exposedEntities[i$1];
327
332
  let match = entityConfig.table;
328
333
  let tableName$1 = match.tableName;
329
334
  let ownChainIdColumn = Stdlib_Option.map(Table.getChainIdField(entityConfig.table), Table.getPgDbFieldName);
@@ -365,6 +370,7 @@ export {
365
370
  createSelectPermission,
366
371
  makeColumnMapping,
367
372
  createEntityRelationship,
373
+ makeTableConfigs,
368
374
  trackDatabase,
369
375
  }
370
376
  /* Rest Not a pure module */