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.
- package/README.md +2 -2
- package/index.d.ts +399 -156
- package/package.json +6 -6
- package/src/AddressRows.res +121 -0
- package/src/AddressRows.res.mjs +143 -0
- package/src/Batch.res +2 -0
- package/src/Batch.res.mjs +2 -1
- package/src/ChainState.res +82 -68
- package/src/ChainState.res.mjs +81 -65
- package/src/ChainState.resi +14 -9
- package/src/Config.res +41 -91
- package/src/Config.res.mjs +34 -68
- package/src/ContractMapping.res +66 -0
- package/src/ContractMapping.res.mjs +73 -0
- package/src/Core.res +20 -0
- package/src/Core.res.mjs +4 -0
- package/src/Envio.res +54 -76
- package/src/EventConfigBuilder.res +157 -30
- package/src/EventConfigBuilder.res.mjs +118 -24
- package/src/FetchState.res +59 -67
- package/src/FetchState.res.mjs +34 -44
- package/src/HandlerRegister.res +20 -11
- package/src/HandlerRegister.res.mjs +22 -5
- package/src/Hasura.res +30 -22
- package/src/Hasura.res.mjs +11 -5
- package/src/InMemoryStore.res +18 -36
- package/src/InMemoryStore.res.mjs +12 -26
- package/src/IndexerState.res +21 -1
- package/src/IndexerState.res.mjs +9 -3
- package/src/IndexerState.resi +1 -0
- package/src/Internal.res +33 -13
- package/src/Internal.res.mjs +12 -16
- package/src/Main.res +27 -20
- package/src/Main.res.mjs +19 -14
- package/src/MemoryStorage.res +78 -73
- package/src/MemoryStorage.res.mjs +66 -81
- package/src/Metrics.res +15 -1
- package/src/Metrics.res.mjs +5 -1
- package/src/Persistence.res +34 -18
- package/src/Persistence.res.mjs +8 -5
- package/src/PgStorage.res +189 -66
- package/src/PgStorage.res.mjs +93 -59
- package/src/Rollback.res +14 -10
- package/src/Rollback.res.mjs +4 -2
- package/src/SimulateItems.res +254 -9
- package/src/SimulateItems.res.mjs +214 -47
- package/src/TestIndexer.res +119 -121
- package/src/TestIndexer.res.mjs +95 -100
- package/src/Utils.res +7 -0
- package/src/Utils.res.mjs +9 -2
- package/src/Writing.res +2 -0
- package/src/Writing.res.mjs +2 -1
- package/src/bindings/ClickHouse.res +39 -6
- package/src/bindings/ClickHouse.res.mjs +29 -4
- package/src/bindings/NodeJs.res +9 -0
- package/src/bindings/NodeJs.res.mjs +8 -1
- package/src/bindings/Postgres.res +9 -0
- package/src/bindings/Postgres.res.mjs +3 -0
- package/src/db/EntityHistory.res +12 -18
- package/src/db/EntityHistory.res.mjs +3 -14
- package/src/db/InternalTable.res +182 -65
- package/src/db/InternalTable.res.mjs +183 -73
- package/src/db/Table.res +24 -0
- package/src/db/Table.res.mjs +20 -1
- package/src/sources/AddressSet.res +0 -22
- package/src/sources/AddressStore.res +48 -88
- package/src/sources/AddressStore.res.mjs +11 -22
- package/src/sources/EvmHyperSyncSource.res +3 -2
- package/src/sources/SimulateSource.res +8 -4
- package/src/sources/SimulateSource.res.mjs +7 -3
- package/src/sources/Svm.res +34 -7
- package/src/sources/Svm.res.mjs +32 -4
- package/src/sources/SvmHyperSyncClient.res +24 -30
- package/src/sources/SvmHyperSyncClient.res.mjs +3 -2
- package/src/sources/SvmHyperSyncSource.res +88 -36
- package/src/sources/SvmHyperSyncSource.res.mjs +71 -39
- package/src/sources/TransactionStore.res +38 -0
- package/src/sources/TransactionStore.res.mjs +5 -0
- package/svm.schema.json +37 -82
package/src/FetchState.res
CHANGED
|
@@ -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:
|
|
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
|
|
1727
|
-
// single batch
|
|
1728
|
-
// additions this chain fetches for, since it's what
|
|
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
|
-
|
|
|
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
|
-
~
|
|
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
|
|
2685
|
-
//
|
|
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
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
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=
|
|
2704
|
-
~
|
|
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
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
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
|
-
|
|
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 = (
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
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
|
-
|
|
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).
|
package/src/FetchState.res.mjs
CHANGED
|
@@ -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
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
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,
|
|
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.
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
}
|
|
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
|
-
|
|
1777
|
-
|
|
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
|
-
|
|
1780
|
-
|
|
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,
|
|
1865
|
-
|
|
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,
|
|
1925
|
-
|
|
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) {
|
package/src/HandlerRegister.res
CHANGED
|
@@ -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
|
-
|
|
303
|
-
|
|
304
|
-
|
|
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
|
|
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
|
|
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 =
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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
|
-
|
|
418
|
-
|
|
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 =
|
|
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)
|
package/src/Hasura.res.mjs
CHANGED
|
@@ -285,7 +285,7 @@ async function createEntityRelationship(endpoint, auth, pgSchema, tableName, rel
|
|
|
285
285
|
});
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
-
|
|
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
|
-
|
|
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
|
-
|
|
326
|
-
|
|
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 */
|