envio 3.5.1 → 3.6.1-subgraph
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/evm.schema.json +7 -0
- package/fuel.schema.json +7 -0
- package/index.d.ts +33 -10
- package/package.json +6 -6
- package/src/ChainState.res +22 -4
- package/src/ChainState.res.mjs +20 -3
- package/src/ChainState.resi +3 -0
- package/src/Config.res +72 -2
- package/src/Config.res.mjs +57 -15
- package/src/Core.res +15 -0
- package/src/Core.res.mjs +11 -0
- package/src/EffectState.res +2 -2
- package/src/EffectState.res.mjs +2 -2
- package/src/EntityTables.res +32 -0
- package/src/EntityTables.res.mjs +44 -0
- package/src/Envio.res +7 -7
- package/src/Envio.res.mjs +5 -6
- package/src/EventProcessing.res +6 -6
- package/src/EventProcessing.res.mjs +8 -6
- package/src/HandlerLoader.res +20 -8
- package/src/HandlerLoader.res.mjs +11 -2
- package/src/Hasura.res +37 -3
- package/src/Hasura.res.mjs +23 -5
- package/src/InMemoryStore.res +48 -8
- package/src/InMemoryStore.res.mjs +37 -7
- package/src/IndexerState.res +30 -24
- package/src/IndexerState.res.mjs +20 -35
- package/src/IndexerState.resi +8 -6
- package/src/Internal.res +29 -11
- package/src/Internal.res.mjs +27 -10
- package/src/LoadLayer.res +39 -8
- package/src/LoadLayer.res.mjs +36 -9
- package/src/LoadLayer.resi +2 -0
- package/src/Metrics.res +5 -5
- package/src/Metrics.res.mjs +5 -1
- package/src/Persistence.res +12 -1
- package/src/PgStorage.res +183 -37
- package/src/PgStorage.res.mjs +149 -34
- package/src/PruneStaleHistory.res +1 -0
- package/src/PruneStaleHistory.res.mjs +2 -1
- package/src/Sink.res +3 -3
- package/src/Sink.res.mjs +2 -2
- package/src/TestIndexer.res +128 -17
- package/src/TestIndexer.res.mjs +75 -9
- package/src/UserContext.res +375 -53
- package/src/UserContext.res.mjs +281 -39
- package/src/Writing.res +8 -16
- package/src/Writing.res.mjs +10 -10
- package/src/bindings/ClickHouse.res +40 -4
- package/src/bindings/ClickHouse.res.mjs +37 -5
- package/src/db/EntityHistory.res +64 -21
- package/src/db/EntityHistory.res.mjs +43 -22
- package/src/db/InternalTable.res.mjs +31 -31
- package/src/db/Table.res +24 -1
- package/src/db/Table.res.mjs +26 -4
- package/src/sources/SourceManager.res +6 -12
- package/src/sources/SourceManager.res.mjs +8 -5
- package/src/subgraph/blocks.ts +176 -0
- package/src/subgraph/calls.ts +213 -0
- package/src/subgraph/conformance.ts +99 -0
- package/src/subgraph/division.ts +100 -0
- package/src/subgraph/errors.ts +90 -0
- package/src/subgraph/graph-ts-types/VERSION +2 -0
- package/src/subgraph/graph-ts-types/chain/arweave.d.ts +70 -0
- package/src/subgraph/graph-ts-types/chain/cosmos.d.ts +327 -0
- package/src/subgraph/graph-ts-types/chain/ethereum.d.ts +233 -0
- package/src/subgraph/graph-ts-types/chain/near.d.ts +253 -0
- package/src/subgraph/graph-ts-types/chain/starknet.d.ts +32 -0
- package/src/subgraph/graph-ts-types/common/collections.d.ts +136 -0
- package/src/subgraph/graph-ts-types/common/conversion.d.ts +11 -0
- package/src/subgraph/graph-ts-types/common/datasource.d.ts +30 -0
- package/src/subgraph/graph-ts-types/common/eager-offset.d.ts +0 -0
- package/src/subgraph/graph-ts-types/common/json.d.ts +17 -0
- package/src/subgraph/graph-ts-types/common/numbers.d.ts +120 -0
- package/src/subgraph/graph-ts-types/common/value.d.ts +120 -0
- package/src/subgraph/graph-ts-types/common/yaml.d.ts +90 -0
- package/src/subgraph/graph-ts-types/global/global.d.ts +194 -0
- package/src/subgraph/graph-ts-types/helper-functions.d.ts +22 -0
- package/src/subgraph/graph-ts-types/index.d.ts +102 -0
- package/src/subgraph/graph-ts.ts +1695 -0
- package/src/subgraph/hosts.ts +148 -0
- package/src/subgraph/runtime.ts +827 -0
- package/src/subgraph/scope.ts +63 -0
- package/svm.schema.json +7 -0
package/evm.schema.json
CHANGED
|
@@ -49,6 +49,13 @@
|
|
|
49
49
|
}
|
|
50
50
|
]
|
|
51
51
|
},
|
|
52
|
+
"disable_default_cross_chain": {
|
|
53
|
+
"description": "Make entities and effect caches per-chain instead of shared across every chain (recommended). Sharing then becomes explicit — add `@crossChain` to an entity in schema.graphql or `crossChain: true` to an effect. (default: false)",
|
|
54
|
+
"type": [
|
|
55
|
+
"boolean",
|
|
56
|
+
"null"
|
|
57
|
+
]
|
|
58
|
+
},
|
|
52
59
|
"ecosystem": {
|
|
53
60
|
"description": "Ecosystem of the project.",
|
|
54
61
|
"anyOf": [
|
package/fuel.schema.json
CHANGED
|
@@ -49,6 +49,13 @@
|
|
|
49
49
|
}
|
|
50
50
|
]
|
|
51
51
|
},
|
|
52
|
+
"disable_default_cross_chain": {
|
|
53
|
+
"description": "Make entities and effect caches per-chain instead of shared across every chain (recommended). Sharing then becomes explicit — add `@crossChain` to an entity in schema.graphql or `crossChain: true` to an effect. (default: false)",
|
|
54
|
+
"type": [
|
|
55
|
+
"boolean",
|
|
56
|
+
"null"
|
|
57
|
+
]
|
|
58
|
+
},
|
|
52
59
|
"ecosystem": {
|
|
53
60
|
"description": "Ecosystem of the project.",
|
|
54
61
|
"$ref": "#/$defs/EcosystemTag"
|
package/index.d.ts
CHANGED
|
@@ -48,8 +48,7 @@ export type EffectCaller = <I, O>(
|
|
|
48
48
|
input: I extends undefined ? undefined : I
|
|
49
49
|
) => Promise<O>;
|
|
50
50
|
|
|
51
|
-
/** The chain an Effect was called on. Available only on chain-scoped effects
|
|
52
|
-
* (`crossChain: false`). */
|
|
51
|
+
/** The chain an Effect was called on. Available only on chain-scoped effects. */
|
|
53
52
|
export type EffectChain = {
|
|
54
53
|
/** The chain id the effect handler was called on. */
|
|
55
54
|
readonly id: number;
|
|
@@ -64,8 +63,8 @@ export type EffectContext = {
|
|
|
64
63
|
/** Whether to cache this call's result. Defaults to the effect's `cache`
|
|
65
64
|
* option; set to `false` to skip caching for this specific invocation. */
|
|
66
65
|
cache: boolean;
|
|
67
|
-
/** The chain the effect was called on. Only available on
|
|
68
|
-
*
|
|
66
|
+
/** The chain the effect was called on. Only available on chain-scoped
|
|
67
|
+
* effects; accessing it on a cross-chain effect throws. */
|
|
69
68
|
readonly chain: EffectChain;
|
|
70
69
|
};
|
|
71
70
|
|
|
@@ -93,9 +92,10 @@ export type EffectOptions<Input, Output> = {
|
|
|
93
92
|
/** Whether the effect should be cached. */
|
|
94
93
|
readonly cache?: boolean;
|
|
95
94
|
/** Whether the effect's cache is shared across all chains. Defaults to
|
|
96
|
-
* `true
|
|
97
|
-
*
|
|
98
|
-
*
|
|
95
|
+
* `true`, or to `false` when config.yaml sets
|
|
96
|
+
* `disable_default_cross_chain: true`. Set to `false` to isolate the cache
|
|
97
|
+
* and rate limiting per chain and enable `context.chain.id` inside the
|
|
98
|
+
* handler. Changing this changes the effect's cache identity. */
|
|
99
99
|
readonly crossChain?: boolean;
|
|
100
100
|
};
|
|
101
101
|
|
|
@@ -225,8 +225,10 @@ export function createEffect<
|
|
|
225
225
|
/** Whether the effect should be cached. */
|
|
226
226
|
readonly cache?: boolean;
|
|
227
227
|
/** Whether the effect's cache is shared across all chains. Defaults to
|
|
228
|
-
* `true
|
|
229
|
-
*
|
|
228
|
+
* `true`, or to `false` when config.yaml sets
|
|
229
|
+
* `disable_default_cross_chain: true`. Set to `false` to isolate the cache
|
|
230
|
+
* and rate limiting per chain and enable `context.chain.id` inside the
|
|
231
|
+
* handler. */
|
|
230
232
|
readonly crossChain?: boolean;
|
|
231
233
|
},
|
|
232
234
|
handler: (args: EffectArgs<I>) => Promise<R>
|
|
@@ -502,6 +504,8 @@ type IndexerConfigTypes = {
|
|
|
502
504
|
};
|
|
503
505
|
svm?: { chains: Record<string, { id: number }> };
|
|
504
506
|
entities?: Record<string, object>;
|
|
507
|
+
// Union of the entity names whose rows belong to a single chain, or `never`.
|
|
508
|
+
perChainEntities?: string;
|
|
505
509
|
enums?: Record<string, string>;
|
|
506
510
|
};
|
|
507
511
|
|
|
@@ -1529,6 +1533,21 @@ type AddressRegistration = {
|
|
|
1529
1533
|
type ConfigEntities<Config extends IndexerConfigTypes = GlobalConfig> =
|
|
1530
1534
|
Config["entities"] extends Record<string, object> ? Config["entities"] : {};
|
|
1531
1535
|
|
|
1536
|
+
/** Entity names whose rows belong to a single chain. */
|
|
1537
|
+
type PerChainEntityNames<Config extends IndexerConfigTypes = GlobalConfig> =
|
|
1538
|
+
Config extends { perChainEntities: infer Names extends string } ? Names : never;
|
|
1539
|
+
|
|
1540
|
+
/** The row shape the chain-agnostic test-indexer operations exchange. A
|
|
1541
|
+
* per-chain entity's row is only identified together with its chain, so the
|
|
1542
|
+
* chain id travels alongside the entity fields. */
|
|
1543
|
+
type TestIndexerEntityRow<
|
|
1544
|
+
Config extends IndexerConfigTypes,
|
|
1545
|
+
Name,
|
|
1546
|
+
Entity
|
|
1547
|
+
> = Name extends PerChainEntityNames<Config>
|
|
1548
|
+
? Entity & { readonly chainId: number }
|
|
1549
|
+
: Entity;
|
|
1550
|
+
|
|
1532
1551
|
/** Entity operations available on test indexer for direct entity manipulation. */
|
|
1533
1552
|
type TestIndexerEntityOperations<Entity> = {
|
|
1534
1553
|
/** Get an entity by ID. Returns undefined if not found. */
|
|
@@ -1537,6 +1556,10 @@ type TestIndexerEntityOperations<Entity> = {
|
|
|
1537
1556
|
readonly getOrThrow: (id: EntityId<Entity>, message?: string) => Promise<Entity>;
|
|
1538
1557
|
/** Get all entities. */
|
|
1539
1558
|
readonly getAll: () => Promise<Entity[]>;
|
|
1559
|
+
/** Get the entities matching a filter. For a per-chain entity the filter
|
|
1560
|
+
* accepts `chainId`, which is how an id present on several chains is
|
|
1561
|
+
* narrowed to one. */
|
|
1562
|
+
readonly getWhere: (filter: GetWhereFilter<Entity>) => Promise<Entity[]>;
|
|
1540
1563
|
/** Set (create or update) an entity. */
|
|
1541
1564
|
readonly set: (entity: Entity) => void;
|
|
1542
1565
|
};
|
|
@@ -1711,7 +1734,7 @@ export type TestIndexerFromConfig<Config extends IndexerConfigTypes = GlobalConf
|
|
|
1711
1734
|
} & SingleEcosystemTestChains<Config> & {
|
|
1712
1735
|
/** Entity operations for direct manipulation outside of handlers. */
|
|
1713
1736
|
readonly [K in keyof ConfigEntities<Config>]: TestIndexerEntityOperations<
|
|
1714
|
-
ConfigEntities<Config>[K]
|
|
1737
|
+
TestIndexerEntityRow<Config, K, ConfigEntities<Config>[K]>
|
|
1715
1738
|
>;
|
|
1716
1739
|
};
|
|
1717
1740
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.6.1-subgraph",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
|
|
6
6
|
"bin": "./bin.mjs",
|
|
@@ -69,10 +69,10 @@
|
|
|
69
69
|
"tsx": "4.21.0"
|
|
70
70
|
},
|
|
71
71
|
"optionalDependencies": {
|
|
72
|
-
"envio-linux-x64": "3.
|
|
73
|
-
"envio-linux-x64-musl": "3.
|
|
74
|
-
"envio-linux-arm64": "3.
|
|
75
|
-
"envio-darwin-x64": "3.
|
|
76
|
-
"envio-darwin-arm64": "3.
|
|
72
|
+
"envio-linux-x64": "3.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"
|
|
77
77
|
}
|
|
78
78
|
}
|
package/src/ChainState.res
CHANGED
|
@@ -31,6 +31,10 @@ type t = {
|
|
|
31
31
|
// then smoothed with an EMA on every batch (see applyBatchProgress). None
|
|
32
32
|
// until the chain has processed at least one event.
|
|
33
33
|
mutable chainDensity: option<float>,
|
|
34
|
+
// In-memory tables for the entities whose rows belong to a single chain.
|
|
35
|
+
// Empty in the default cross-chain mode, where every entity's table lives on
|
|
36
|
+
// the indexer instead.
|
|
37
|
+
mutable entities: EntityTables.t,
|
|
34
38
|
mutable reorgDetection: ReorgDetection.t,
|
|
35
39
|
mutable safeCheckpointTracking: option<SafeCheckpointTracking.t>,
|
|
36
40
|
// Holds this chain's transactions (kept in Rust) keyed by (blockNumber,
|
|
@@ -96,11 +100,13 @@ let make = (
|
|
|
96
100
|
~chainDensity=None,
|
|
97
101
|
~blockStore=BlockStore.make(~ecosystem=Ecosystem.Evm, ~shouldChecksum=false),
|
|
98
102
|
~reorgThresholdReadyTolerance=100,
|
|
103
|
+
~perChainEntities: array<Internal.entityConfig>=[],
|
|
99
104
|
~logger: Pino.t,
|
|
100
105
|
): t => {
|
|
101
106
|
validateOnEventRegistrations(~chainId=chainConfig.id, onEventRegistrations)
|
|
102
107
|
{
|
|
103
108
|
logger,
|
|
109
|
+
entities: EntityTables.make(perChainEntities),
|
|
104
110
|
onEventRegistrations,
|
|
105
111
|
fetchState,
|
|
106
112
|
addressStore,
|
|
@@ -318,6 +324,7 @@ let makeInternal = (
|
|
|
318
324
|
~chainReorgCheckpoints,
|
|
319
325
|
),
|
|
320
326
|
~committedProgressBlockNumber=progressBlockNumber,
|
|
327
|
+
~perChainEntities=config.allEntities->EntityTables.perChain,
|
|
321
328
|
~timestampCaughtUpToHeadOrEndblock,
|
|
322
329
|
~numEventsProcessed,
|
|
323
330
|
~transactionStore=TransactionStore.make(
|
|
@@ -408,6 +415,11 @@ let makeFromDbState = (
|
|
|
408
415
|
// --- Read accessors. ---
|
|
409
416
|
|
|
410
417
|
let logger = (cs: t) => cs.logger
|
|
418
|
+
let entities = (cs: t) => cs.entities
|
|
419
|
+
|
|
420
|
+
// Rollback discards every uncommitted change, so this chain's partition is
|
|
421
|
+
// rebuilt from scratch alongside the indexer's cross-chain one.
|
|
422
|
+
let resetEntities = (cs: t, ~perChainEntities) => cs.entities = EntityTables.make(perChainEntities)
|
|
411
423
|
let sourceManager = (cs: t) => cs.sourceManager
|
|
412
424
|
let chainConfig = (cs: t) => cs.chainConfig
|
|
413
425
|
let reorgDetection = (cs: t) => cs.reorgDetection
|
|
@@ -537,10 +549,16 @@ let targetBlock = (cs: t, ~chainTargetItems: float) => {
|
|
|
537
549
|
let bufferBlockNumber = fetchState->FetchState.bufferBlockNumber
|
|
538
550
|
switch cs->effectiveDensity {
|
|
539
551
|
| Some(density) if density > 0. =>
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
552
|
+
// Decided by comparison so no unbounded value is ever converted to int:
|
|
553
|
+
// at low densities chainTargetItems /. density exceeds the int range, and
|
|
554
|
+
// truncating it wraps negative — the target collapses below the frontier
|
|
555
|
+
// and the chain stops querying. The division only runs when its result is
|
|
556
|
+
// provably below the ceiling-bounded range.
|
|
557
|
+
if density *. (fetchCeiling - bufferBlockNumber)->Int.toFloat <= chainTargetItems {
|
|
558
|
+
fetchCeiling
|
|
559
|
+
} else {
|
|
560
|
+
bufferBlockNumber + Math.ceil(chainTargetItems /. density)->Float.toInt
|
|
561
|
+
}
|
|
544
562
|
| _ => Pervasives.min(bufferBlockNumber + coldTargetRange, fetchCeiling)
|
|
545
563
|
}
|
|
546
564
|
}
|
package/src/ChainState.res.mjs
CHANGED
|
@@ -13,6 +13,7 @@ import * as BlockStore from "./sources/BlockStore.res.mjs";
|
|
|
13
13
|
import * as FetchState from "./FetchState.res.mjs";
|
|
14
14
|
import * as Stdlib_Null from "@rescript/runtime/lib/es6/Stdlib_Null.js";
|
|
15
15
|
import * as AddressStore from "./sources/AddressStore.res.mjs";
|
|
16
|
+
import * as EntityTables from "./EntityTables.res.mjs";
|
|
16
17
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
17
18
|
import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
|
|
18
19
|
import * as SourceManager from "./sources/SourceManager.res.mjs";
|
|
@@ -49,7 +50,7 @@ function validateOnEventRegistrations(chainId, registrations) {
|
|
|
49
50
|
});
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, sourceManager, reorgDetection, committedProgressBlockNumber, safeCheckpointTrackingOpt, numEventsProcessedOpt, timestampCaughtUpToHeadOrEndblockOpt, isProgressAtHeadOpt, transactionStoreOpt, chainDensityOpt, blockStoreOpt, reorgThresholdReadyToleranceOpt, logger) {
|
|
53
|
+
function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, sourceManager, reorgDetection, committedProgressBlockNumber, safeCheckpointTrackingOpt, numEventsProcessedOpt, timestampCaughtUpToHeadOrEndblockOpt, isProgressAtHeadOpt, transactionStoreOpt, chainDensityOpt, blockStoreOpt, reorgThresholdReadyToleranceOpt, perChainEntitiesOpt, logger) {
|
|
53
54
|
let onEventRegistrations = onEventRegistrationsOpt !== undefined ? onEventRegistrationsOpt : [];
|
|
54
55
|
let safeCheckpointTracking = safeCheckpointTrackingOpt !== undefined ? Primitive_option.valFromOption(safeCheckpointTrackingOpt) : undefined;
|
|
55
56
|
let numEventsProcessed = numEventsProcessedOpt !== undefined ? numEventsProcessedOpt : 0;
|
|
@@ -59,6 +60,7 @@ function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, so
|
|
|
59
60
|
let chainDensity = chainDensityOpt !== undefined ? Primitive_option.valFromOption(chainDensityOpt) : undefined;
|
|
60
61
|
let blockStore = blockStoreOpt !== undefined ? Primitive_option.valFromOption(blockStoreOpt) : BlockStore.make("evm", false);
|
|
61
62
|
let reorgThresholdReadyTolerance = reorgThresholdReadyToleranceOpt !== undefined ? reorgThresholdReadyToleranceOpt : 100;
|
|
63
|
+
let perChainEntities = perChainEntitiesOpt !== undefined ? perChainEntitiesOpt : [];
|
|
62
64
|
validateOnEventRegistrations(chainConfig.id, onEventRegistrations);
|
|
63
65
|
return {
|
|
64
66
|
logger: logger,
|
|
@@ -74,6 +76,7 @@ function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, so
|
|
|
74
76
|
numEventsProcessed: numEventsProcessed,
|
|
75
77
|
pendingBudget: 0,
|
|
76
78
|
chainDensity: chainDensity,
|
|
79
|
+
entities: EntityTables.make(perChainEntities),
|
|
77
80
|
reorgDetection: reorgDetection,
|
|
78
81
|
safeCheckpointTracking: safeCheckpointTracking,
|
|
79
82
|
transactionStore: transactionStore,
|
|
@@ -186,7 +189,7 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
|
|
|
186
189
|
}
|
|
187
190
|
let firstEventBlock$1 = fetchState.firstEventBlock;
|
|
188
191
|
let chainDensity = firstEventBlock$1 !== undefined && progressBlockNumber > firstEventBlock$1 && numEventsProcessed > 0 ? numEventsProcessed / (progressBlockNumber - firstEventBlock$1 | 0) : undefined;
|
|
189
|
-
return make(chainConfig, fetchState, onEventRegistrations, addressStore, SourceManager.make(sources$1, isRealtime, undefined, undefined, undefined, reducedPollingInterval, undefined, undefined), ReorgDetection.make(chainReorgCheckpoints, maxReorgDepth, config.shouldRollbackOnReorg), progressBlockNumber, Primitive_option.some(SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints)), numEventsProcessed, Primitive_option.some(timestampCaughtUpToHeadOrEndblock), undefined, Primitive_option.some(TransactionStore.make(config.ecosystem.name, !lowercaseAddresses)), Primitive_option.some(chainDensity), Primitive_option.some(BlockStore.make(config.ecosystem.name, !lowercaseAddresses)), config.reorgThresholdReadyTolerance, logger);
|
|
192
|
+
return make(chainConfig, fetchState, onEventRegistrations, addressStore, SourceManager.make(sources$1, isRealtime, undefined, undefined, undefined, reducedPollingInterval, undefined, undefined), ReorgDetection.make(chainReorgCheckpoints, maxReorgDepth, config.shouldRollbackOnReorg), progressBlockNumber, Primitive_option.some(SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints)), numEventsProcessed, Primitive_option.some(timestampCaughtUpToHeadOrEndblock), undefined, Primitive_option.some(TransactionStore.make(config.ecosystem.name, !lowercaseAddresses)), Primitive_option.some(chainDensity), Primitive_option.some(BlockStore.make(config.ecosystem.name, !lowercaseAddresses)), config.reorgThresholdReadyTolerance, EntityTables.perChain(config.allEntities), logger);
|
|
190
193
|
}
|
|
191
194
|
|
|
192
195
|
function makeFromConfig(chainConfig, config, registrationsByChainId, knownHeight) {
|
|
@@ -209,6 +212,14 @@ function logger(cs) {
|
|
|
209
212
|
return cs.logger;
|
|
210
213
|
}
|
|
211
214
|
|
|
215
|
+
function entities(cs) {
|
|
216
|
+
return cs.entities;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function resetEntities(cs, perChainEntities) {
|
|
220
|
+
cs.entities = EntityTables.make(perChainEntities);
|
|
221
|
+
}
|
|
222
|
+
|
|
212
223
|
function sourceManager(cs) {
|
|
213
224
|
return cs.sourceManager;
|
|
214
225
|
}
|
|
@@ -357,7 +368,11 @@ function targetBlock(cs, chainTargetItems) {
|
|
|
357
368
|
let bufferBlockNumber = FetchState.bufferBlockNumber(fetchState);
|
|
358
369
|
let density = effectiveDensity(cs);
|
|
359
370
|
if (density !== undefined && density > 0) {
|
|
360
|
-
|
|
371
|
+
if (density * (fetchCeiling$1 - bufferBlockNumber | 0) <= chainTargetItems) {
|
|
372
|
+
return fetchCeiling$1;
|
|
373
|
+
} else {
|
|
374
|
+
return bufferBlockNumber + (Math.ceil(chainTargetItems / density) | 0) | 0;
|
|
375
|
+
}
|
|
361
376
|
} else {
|
|
362
377
|
return Primitive_int.min(bufferBlockNumber + 20000 | 0, fetchCeiling$1);
|
|
363
378
|
}
|
|
@@ -841,6 +856,8 @@ export {
|
|
|
841
856
|
makeFromConfig,
|
|
842
857
|
makeFromDbState,
|
|
843
858
|
logger,
|
|
859
|
+
entities,
|
|
860
|
+
resetEntities,
|
|
844
861
|
sourceManager,
|
|
845
862
|
chainConfig,
|
|
846
863
|
reorgDetection,
|
package/src/ChainState.resi
CHANGED
|
@@ -21,6 +21,7 @@ let make: (
|
|
|
21
21
|
~chainDensity: option<float>=?,
|
|
22
22
|
~blockStore: BlockStore.t=?,
|
|
23
23
|
~reorgThresholdReadyTolerance: int=?,
|
|
24
|
+
~perChainEntities: array<Internal.entityConfig>=?,
|
|
24
25
|
~logger: Pino.t,
|
|
25
26
|
) => t
|
|
26
27
|
|
|
@@ -44,6 +45,8 @@ let makeFromDbState: (
|
|
|
44
45
|
|
|
45
46
|
// Accessors.
|
|
46
47
|
let logger: t => Pino.t
|
|
48
|
+
let entities: t => EntityTables.t
|
|
49
|
+
let resetEntities: (t, ~perChainEntities: array<Internal.entityConfig>) => unit
|
|
47
50
|
let sourceManager: t => SourceManager.t
|
|
48
51
|
let chainConfig: t => Config.chain
|
|
49
52
|
let reorgDetection: t => ReorgDetection.t
|
package/src/Config.res
CHANGED
|
@@ -59,11 +59,26 @@ type sourceSync = {
|
|
|
59
59
|
pollingInterval: int,
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
// How a backend spells column names, mirroring `column_name_format` in
|
|
63
|
+
// config.yaml. Only the internal columns the runtime appends need it — user
|
|
64
|
+
// field names arrive pre-resolved from the CLI.
|
|
65
|
+
type columnNameFormat = | @as("original") Original | @as("snake_case") SnakeCase
|
|
66
|
+
|
|
62
67
|
type storage = {
|
|
63
68
|
postgres: bool,
|
|
64
69
|
clickhouse: bool,
|
|
70
|
+
postgresColumnNameFormat: columnNameFormat,
|
|
71
|
+
clickhouseColumnNameFormat: columnNameFormat,
|
|
65
72
|
}
|
|
66
73
|
|
|
74
|
+
let chainIdFieldName = "chainId"
|
|
75
|
+
|
|
76
|
+
let chainIdColumnName = format =>
|
|
77
|
+
switch format {
|
|
78
|
+
| Original => chainIdFieldName
|
|
79
|
+
| SnakeCase => "chain_id"
|
|
80
|
+
}
|
|
81
|
+
|
|
67
82
|
type contractHandler = {
|
|
68
83
|
name: string,
|
|
69
84
|
handler: option<string>,
|
|
@@ -76,6 +91,10 @@ type t = {
|
|
|
76
91
|
contractHandlers: array<contractHandler>,
|
|
77
92
|
shouldRollbackOnReorg: bool,
|
|
78
93
|
shouldSaveFullHistory: bool,
|
|
94
|
+
// False when `disable_default_cross_chain: true` in config.yaml. Entities
|
|
95
|
+
// carry their own resolved `crossChain`; this only decides the default for
|
|
96
|
+
// effects that don't state one.
|
|
97
|
+
defaultCrossChain: bool,
|
|
79
98
|
storage: storage,
|
|
80
99
|
// Widest scalar the internal chain-id columns need, resolved by the CLI from
|
|
81
100
|
// the maximum active chain id. Older configs predate the field, and every id
|
|
@@ -100,6 +119,9 @@ type t = {
|
|
|
100
119
|
userEntities: array<Internal.entityConfig>,
|
|
101
120
|
allEntities: array<Internal.entityConfig>,
|
|
102
121
|
allEnums: array<Table.enumConfig<Table.enum>>,
|
|
122
|
+
// Set only in subgraph mode: the translated manifest, which is what makes
|
|
123
|
+
// the subgraph runtime take over handler registration.
|
|
124
|
+
subgraph: option<JSON.t>,
|
|
103
125
|
}
|
|
104
126
|
|
|
105
127
|
module EnvioAddresses = {
|
|
@@ -161,6 +183,9 @@ module EnvioAddresses = {
|
|
|
161
183
|
// always required to have Postgres enabled (Storage::resolve forbids
|
|
162
184
|
// a Postgres-disabled global), so this is safe regardless of mode.
|
|
163
185
|
storage: {postgres: true, clickhouse: false},
|
|
186
|
+
// The table already keys rows by chain through the composite `id`, so the
|
|
187
|
+
// per-chain mode must not append a second chain-id column to it.
|
|
188
|
+
crossChain: true,
|
|
164
189
|
}->Internal.fromGenericEntityConfig
|
|
165
190
|
}
|
|
166
191
|
|
|
@@ -353,6 +378,7 @@ let entityStorageSchema = S.schema(s =>
|
|
|
353
378
|
let entityJsonSchema = S.schema(s =>
|
|
354
379
|
{
|
|
355
380
|
"name": s.matches(S.string),
|
|
381
|
+
"crossChain": s.matches(S.option(S.bool)),
|
|
356
382
|
"storage": s.matches(S.option(entityStorageSchema)),
|
|
357
383
|
"properties": s.matches(S.array(propertySchema)),
|
|
358
384
|
"derivedFields": s.matches(S.option(S.array(derivedFieldSchema))),
|
|
@@ -424,13 +450,30 @@ let parseEnumsFromJson = (enumsJson: dict<array<string>>): array<Table.enumConfi
|
|
|
424
450
|
)
|
|
425
451
|
}
|
|
426
452
|
|
|
453
|
+
// The chain-id column appended to a per-chain entity's table. It completes the
|
|
454
|
+
// primary key so the same id can exist independently on every chain, and it's
|
|
455
|
+
// spelled per backend because the two can be configured with different
|
|
456
|
+
// `column_name_format`s.
|
|
457
|
+
let makeChainIdField = (~globalStorage: storage) =>
|
|
458
|
+
Table.mkField(
|
|
459
|
+
chainIdFieldName,
|
|
460
|
+
ChainId,
|
|
461
|
+
~fieldSchema=ChainId.schema,
|
|
462
|
+
~isPrimaryKey=true,
|
|
463
|
+
~isChainId=true,
|
|
464
|
+
~postgresDbName=globalStorage.postgresColumnNameFormat->chainIdColumnName,
|
|
465
|
+
~clickhouseDbName=globalStorage.clickhouseColumnNameFormat->chainIdColumnName,
|
|
466
|
+
)
|
|
467
|
+
|
|
427
468
|
let parseEntitiesFromJson = (
|
|
428
469
|
entitiesJson: array<'entityJson>,
|
|
429
470
|
~enumConfigsByName: dict<Table.enumConfig<Table.enum>>,
|
|
430
471
|
~globalStorage: storage,
|
|
472
|
+
~defaultCrossChain: bool,
|
|
431
473
|
): array<Internal.entityConfig> => {
|
|
432
474
|
entitiesJson->Array.mapWithIndex((entityJson, index) => {
|
|
433
475
|
let entityName = entityJson["name"]
|
|
476
|
+
let crossChain = entityJson["crossChain"]->Option.getOr(defaultCrossChain)
|
|
434
477
|
|
|
435
478
|
let fields: array<Table.fieldOrDerived> = entityJson["properties"]->Array.map(prop => {
|
|
436
479
|
let (fieldType, fieldSchema, isNullable, isArray, isIndex) = getFieldTypeAndSchema(
|
|
@@ -478,7 +521,10 @@ let parseEntitiesFromJson = (
|
|
|
478
521
|
|
|
479
522
|
let table = Table.mkTable(
|
|
480
523
|
entityName,
|
|
481
|
-
~fields=Array.
|
|
524
|
+
~fields=Array.concatMany(
|
|
525
|
+
fields,
|
|
526
|
+
[crossChain ? [] : [makeChainIdField(~globalStorage)], derivedFields],
|
|
527
|
+
),
|
|
482
528
|
~compositeIndexes,
|
|
483
529
|
~description=?entityJson["description"],
|
|
484
530
|
)
|
|
@@ -536,14 +582,19 @@ let parseEntitiesFromJson = (
|
|
|
536
582
|
schema: schema->(Utils.magic: S.t<dict<unknown>> => S.t<Internal.entity>),
|
|
537
583
|
table,
|
|
538
584
|
storage,
|
|
585
|
+
crossChain,
|
|
539
586
|
}->Internal.fromGenericEntityConfig
|
|
540
587
|
})
|
|
541
588
|
}
|
|
542
589
|
|
|
590
|
+
let columnNameFormatSchema = S.enum([Original, SnakeCase])
|
|
591
|
+
|
|
543
592
|
let publicConfigStorageSchema = S.schema(s =>
|
|
544
593
|
{
|
|
545
594
|
"postgres": s.matches(S.bool),
|
|
546
595
|
"clickhouse": s.matches(S.option(S.bool)),
|
|
596
|
+
"postgresColumnNameFormat": s.matches(S.option(columnNameFormatSchema)),
|
|
597
|
+
"clickhouseColumnNameFormat": s.matches(S.option(columnNameFormatSchema)),
|
|
547
598
|
}
|
|
548
599
|
)
|
|
549
600
|
|
|
@@ -558,12 +609,14 @@ let publicConfigSchema = S.schema(s =>
|
|
|
558
609
|
"saveFullHistory": s.matches(S.option(S.bool)),
|
|
559
610
|
"rawEvents": s.matches(S.option(S.bool)),
|
|
560
611
|
"chainIdMode": s.matches(S.option(ChainId.modeSchema)),
|
|
612
|
+
"defaultCrossChain": s.matches(S.option(S.bool)),
|
|
561
613
|
"storage": s.matches(publicConfigStorageSchema),
|
|
562
614
|
"evm": s.matches(S.option(publicConfigEvmSchema)),
|
|
563
615
|
"fuel": s.matches(S.option(publicConfigEcosystemSchema)),
|
|
564
616
|
"svm": s.matches(S.option(publicConfigEcosystemSchema)),
|
|
565
617
|
"enums": s.matches(S.option(S.dict(S.array(S.string)))),
|
|
566
618
|
"entities": s.matches(S.option(S.array(entityJsonSchema))),
|
|
619
|
+
"subgraph": s.matches(S.option(S.json(~validate=false))),
|
|
567
620
|
}
|
|
568
621
|
)
|
|
569
622
|
|
|
@@ -995,12 +1048,20 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
995
1048
|
let globalStorage: storage = {
|
|
996
1049
|
postgres: publicConfig["storage"]["postgres"],
|
|
997
1050
|
clickhouse: publicConfig["storage"]["clickhouse"]->Option.getOr(false),
|
|
1051
|
+
postgresColumnNameFormat: publicConfig["storage"]["postgresColumnNameFormat"]->Option.getOr(
|
|
1052
|
+
Original,
|
|
1053
|
+
),
|
|
1054
|
+
clickhouseColumnNameFormat: publicConfig["storage"]["clickhouseColumnNameFormat"]->Option.getOr(
|
|
1055
|
+
Original,
|
|
1056
|
+
),
|
|
998
1057
|
}
|
|
999
1058
|
|
|
1059
|
+
let defaultCrossChain = publicConfig["defaultCrossChain"]->Option.getOr(true)
|
|
1060
|
+
|
|
1000
1061
|
let userEntities =
|
|
1001
1062
|
publicConfig["entities"]
|
|
1002
1063
|
->Option.getOr([])
|
|
1003
|
-
->parseEntitiesFromJson(~enumConfigsByName, ~globalStorage)
|
|
1064
|
+
->parseEntitiesFromJson(~enumConfigsByName, ~globalStorage, ~defaultCrossChain)
|
|
1004
1065
|
|
|
1005
1066
|
let allEntities = userEntities->Array.concat([EnvioAddresses.entityConfig])
|
|
1006
1067
|
|
|
@@ -1036,6 +1097,7 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
1036
1097
|
contractHandlers,
|
|
1037
1098
|
shouldRollbackOnReorg: publicConfig["rollbackOnReorg"]->Option.getOr(true),
|
|
1038
1099
|
shouldSaveFullHistory: publicConfig["saveFullHistory"]->Option.getOr(false),
|
|
1100
|
+
defaultCrossChain,
|
|
1039
1101
|
storage: globalStorage,
|
|
1040
1102
|
chainIdMode: publicConfig["chainIdMode"]->Option.getOr(Int32),
|
|
1041
1103
|
chainMap,
|
|
@@ -1052,6 +1114,7 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
1052
1114
|
userEntities,
|
|
1053
1115
|
allEntities,
|
|
1054
1116
|
allEnums,
|
|
1117
|
+
subgraph: publicConfig["subgraph"],
|
|
1055
1118
|
}
|
|
1056
1119
|
}
|
|
1057
1120
|
|
|
@@ -1188,6 +1251,13 @@ let stripSensitiveData = (json: JSON.t): JSON.t => {
|
|
|
1188
1251
|
stripChains(obj->Dict.get("evm"))
|
|
1189
1252
|
stripChains(obj->Dict.get("fuel"))
|
|
1190
1253
|
stripChains(obj->Dict.get("svm"))
|
|
1254
|
+
// A subgraph names its endpoint through ENVIO_SUBGRAPH_RPC, and those
|
|
1255
|
+
// URLs usually carry a provider key. Rotating one must not read as an
|
|
1256
|
+
// incompatible config either.
|
|
1257
|
+
switch obj->Dict.get("subgraph") {
|
|
1258
|
+
| Some(Object(subgraph)) => subgraph->Utils.Dict.deleteInPlace("rpcUrls")
|
|
1259
|
+
| _ => ()
|
|
1260
|
+
}
|
|
1191
1261
|
}
|
|
1192
1262
|
| _ => ()
|
|
1193
1263
|
}
|