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.
Files changed (84) hide show
  1. package/evm.schema.json +7 -0
  2. package/fuel.schema.json +7 -0
  3. package/index.d.ts +33 -10
  4. package/package.json +6 -6
  5. package/src/ChainState.res +22 -4
  6. package/src/ChainState.res.mjs +20 -3
  7. package/src/ChainState.resi +3 -0
  8. package/src/Config.res +72 -2
  9. package/src/Config.res.mjs +57 -15
  10. package/src/Core.res +15 -0
  11. package/src/Core.res.mjs +11 -0
  12. package/src/EffectState.res +2 -2
  13. package/src/EffectState.res.mjs +2 -2
  14. package/src/EntityTables.res +32 -0
  15. package/src/EntityTables.res.mjs +44 -0
  16. package/src/Envio.res +7 -7
  17. package/src/Envio.res.mjs +5 -6
  18. package/src/EventProcessing.res +6 -6
  19. package/src/EventProcessing.res.mjs +8 -6
  20. package/src/HandlerLoader.res +20 -8
  21. package/src/HandlerLoader.res.mjs +11 -2
  22. package/src/Hasura.res +37 -3
  23. package/src/Hasura.res.mjs +23 -5
  24. package/src/InMemoryStore.res +48 -8
  25. package/src/InMemoryStore.res.mjs +37 -7
  26. package/src/IndexerState.res +30 -24
  27. package/src/IndexerState.res.mjs +20 -35
  28. package/src/IndexerState.resi +8 -6
  29. package/src/Internal.res +29 -11
  30. package/src/Internal.res.mjs +27 -10
  31. package/src/LoadLayer.res +39 -8
  32. package/src/LoadLayer.res.mjs +36 -9
  33. package/src/LoadLayer.resi +2 -0
  34. package/src/Metrics.res +5 -5
  35. package/src/Metrics.res.mjs +5 -1
  36. package/src/Persistence.res +12 -1
  37. package/src/PgStorage.res +183 -37
  38. package/src/PgStorage.res.mjs +149 -34
  39. package/src/PruneStaleHistory.res +1 -0
  40. package/src/PruneStaleHistory.res.mjs +2 -1
  41. package/src/Sink.res +3 -3
  42. package/src/Sink.res.mjs +2 -2
  43. package/src/TestIndexer.res +128 -17
  44. package/src/TestIndexer.res.mjs +75 -9
  45. package/src/UserContext.res +375 -53
  46. package/src/UserContext.res.mjs +281 -39
  47. package/src/Writing.res +8 -16
  48. package/src/Writing.res.mjs +10 -10
  49. package/src/bindings/ClickHouse.res +40 -4
  50. package/src/bindings/ClickHouse.res.mjs +37 -5
  51. package/src/db/EntityHistory.res +64 -21
  52. package/src/db/EntityHistory.res.mjs +43 -22
  53. package/src/db/InternalTable.res.mjs +31 -31
  54. package/src/db/Table.res +24 -1
  55. package/src/db/Table.res.mjs +26 -4
  56. package/src/sources/SourceManager.res +6 -12
  57. package/src/sources/SourceManager.res.mjs +8 -5
  58. package/src/subgraph/blocks.ts +176 -0
  59. package/src/subgraph/calls.ts +213 -0
  60. package/src/subgraph/conformance.ts +99 -0
  61. package/src/subgraph/division.ts +100 -0
  62. package/src/subgraph/errors.ts +90 -0
  63. package/src/subgraph/graph-ts-types/VERSION +2 -0
  64. package/src/subgraph/graph-ts-types/chain/arweave.d.ts +70 -0
  65. package/src/subgraph/graph-ts-types/chain/cosmos.d.ts +327 -0
  66. package/src/subgraph/graph-ts-types/chain/ethereum.d.ts +233 -0
  67. package/src/subgraph/graph-ts-types/chain/near.d.ts +253 -0
  68. package/src/subgraph/graph-ts-types/chain/starknet.d.ts +32 -0
  69. package/src/subgraph/graph-ts-types/common/collections.d.ts +136 -0
  70. package/src/subgraph/graph-ts-types/common/conversion.d.ts +11 -0
  71. package/src/subgraph/graph-ts-types/common/datasource.d.ts +30 -0
  72. package/src/subgraph/graph-ts-types/common/eager-offset.d.ts +0 -0
  73. package/src/subgraph/graph-ts-types/common/json.d.ts +17 -0
  74. package/src/subgraph/graph-ts-types/common/numbers.d.ts +120 -0
  75. package/src/subgraph/graph-ts-types/common/value.d.ts +120 -0
  76. package/src/subgraph/graph-ts-types/common/yaml.d.ts +90 -0
  77. package/src/subgraph/graph-ts-types/global/global.d.ts +194 -0
  78. package/src/subgraph/graph-ts-types/helper-functions.d.ts +22 -0
  79. package/src/subgraph/graph-ts-types/index.d.ts +102 -0
  80. package/src/subgraph/graph-ts.ts +1695 -0
  81. package/src/subgraph/hosts.ts +148 -0
  82. package/src/subgraph/runtime.ts +827 -0
  83. package/src/subgraph/scope.ts +63 -0
  84. package/svm.schema.json +7 -0
@@ -166,21 +166,36 @@ let fuelTransferParamsSchema = S$RescriptSchema.schema(s => ({
166
166
  amount: s.m(Utils.$$BigInt.schema)
167
167
  }));
168
168
 
169
- let cacheTablePrefix = "envio_effect_";
169
+ function chainScopeToString(scope) {
170
+ if (scope === "crossChain") {
171
+ return "crossChain";
172
+ } else {
173
+ return ChainId.toString(scope);
174
+ }
175
+ }
170
176
 
171
- function toTableName(effectName, scope) {
177
+ function chainScopeChainId(scope) {
172
178
  if (scope === "crossChain") {
173
- return cacheTablePrefix + effectName;
179
+ return;
174
180
  } else {
175
- return `envio_` + ChainId.toString(scope) + `_effect_` + effectName;
181
+ return Primitive_option.some(scope);
176
182
  }
177
183
  }
178
184
 
179
- function scopeToString(scope) {
185
+ function stampChainId(entity, fieldName, chainId) {
186
+ return Utils.Dict.merge(entity, Object.fromEntries([[
187
+ fieldName,
188
+ chainId
189
+ ]]));
190
+ }
191
+
192
+ let cacheTablePrefix = "envio_effect_";
193
+
194
+ function toTableName(effectName, scope) {
180
195
  if (scope === "crossChain") {
181
- return "crossChain";
196
+ return cacheTablePrefix + effectName;
182
197
  } else {
183
- return ChainId.toString(scope);
198
+ return `envio_` + ChainId.toString(scope) + `_effect_` + effectName;
184
199
  }
185
200
  }
186
201
 
@@ -260,7 +275,6 @@ function toCachePath(effectName, scope) {
260
275
 
261
276
  let EffectCache = {
262
277
  toTableName: toTableName,
263
- scopeToString: scopeToString,
264
278
  parseChainId: parseChainId,
265
279
  chainScopedRe: chainScopedRe,
266
280
  crossChainRe: crossChainRe,
@@ -272,8 +286,8 @@ let cacheOutputSchema = S$RescriptSchema.json(false);
272
286
 
273
287
  function makeCacheTable(effectName, scope) {
274
288
  return Table.mkTable(toTableName(effectName, scope), undefined, [
275
- Table.mkField("id", "String", S$RescriptSchema.string, undefined, undefined, undefined, true, undefined, undefined, undefined, undefined, undefined),
276
- Table.mkField("output", "Json", cacheOutputSchema, undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined)
289
+ Table.mkField("id", "String", S$RescriptSchema.string, undefined, undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined),
290
+ Table.mkField("output", "Json", cacheOutputSchema, undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined, undefined)
277
291
  ], undefined);
278
292
  }
279
293
 
@@ -296,6 +310,9 @@ export {
296
310
  getItemChainId,
297
311
  fuelSupplyParamsSchema,
298
312
  fuelTransferParamsSchema,
313
+ chainScopeToString,
314
+ chainScopeChainId,
315
+ stampChainId,
299
316
  cacheTablePrefix,
300
317
  EffectCache,
301
318
  cacheOutputSchema,
package/src/LoadLayer.res CHANGED
@@ -1,15 +1,41 @@
1
+ // A per-chain entity's rows must never dedup or merge across chains, so both
2
+ // the load-group key and the storage query carry the scope.
3
+ let scopeKeySuffix = (scope: Internal.chainScope) =>
4
+ switch scope {
5
+ | CrossChain => ""
6
+ | Chain(chainId) => `.${chainId->ChainId.toString}`
7
+ }
8
+
9
+ // Narrows a query to the scope's chain. Cross-chain entities have no chain-id
10
+ // column, so their filter is left untouched.
11
+ let scopeFilter = (filter: EntityFilter.t, ~table: Table.table, ~scope: Internal.chainScope) =>
12
+ switch (scope, table->Table.getChainIdField) {
13
+ | (Chain(chainId), Some(field)) =>
14
+ EntityFilter.And({
15
+ filters: [
16
+ filter,
17
+ Eq({
18
+ fieldName: field.fieldName,
19
+ fieldValue: chainId->(Utils.magic: ChainId.t => unknown),
20
+ }),
21
+ ],
22
+ })
23
+ | _ => filter
24
+ }
25
+
1
26
  let loadById = (
2
27
  ~loadManager,
3
28
  ~persistence: Persistence.t,
4
29
  ~entityConfig: Internal.entityConfig,
30
+ ~scope: Internal.chainScope,
5
31
  ~indexerState,
6
32
  ~shouldGroup,
7
33
  ~item,
8
34
  ~ecosystem,
9
35
  ~entityId,
10
36
  ) => {
11
- let key = `${entityConfig.name}.get`
12
- let inMemTable = indexerState->InMemoryStore.getInMemTable(~entityConfig)
37
+ let key = `${entityConfig.name}.get${scope->scopeKeySuffix}`
38
+ let inMemTable = indexerState->InMemoryStore.getInMemTable(~entityConfig, ~scope)
13
39
 
14
40
  let load = async (idsToLoad, ~onError as _) => {
15
41
  let storage = persistence->Persistence.getInitializedStorageOrThrow
@@ -25,7 +51,7 @@ let loadById = (
25
51
  ~filter=EntityFilter.In({
26
52
  fieldName: Table.idFieldName,
27
53
  fieldValue: idsToLoad->(Utils.magic: array<string> => array<unknown>),
28
- }),
54
+ })->scopeFilter(~table=entityConfig.table, ~scope),
29
55
  )
30
56
  )->(Utils.magic: array<unknown> => array<Internal.entity>)
31
57
  } catch {
@@ -327,14 +353,16 @@ let loadByFilter = (
327
353
  ~loadManager,
328
354
  ~persistence: Persistence.t,
329
355
  ~entityConfig: Internal.entityConfig,
356
+ ~scope: Internal.chainScope,
330
357
  ~indexerState,
331
358
  ~shouldGroup,
332
359
  ~item,
333
360
  ~ecosystem,
334
361
  ~filter: EntityFilter.t,
335
362
  ) => {
336
- let key = filter->EntityFilter.toOperationKey(~entityName=entityConfig.name)
337
- let inMemTable = indexerState->InMemoryStore.getInMemTable(~entityConfig)
363
+ let key =
364
+ filter->EntityFilter.toOperationKey(~entityName=entityConfig.name) ++ scope->scopeKeySuffix
365
+ let inMemTable = indexerState->InMemoryStore.getInMemTable(~entityConfig, ~scope)
338
366
 
339
367
  let load = async (filters: array<EntityFilter.t>, ~onError as _) => {
340
368
  let storage = persistence->Persistence.getInitializedStorageOrThrow
@@ -362,9 +390,12 @@ let loadByFilter = (
362
390
  ->Array.map(async filter => {
363
391
  try {
364
392
  let entities =
365
- (await storage.loadOrThrow(~table=entityConfig.table, ~filter))->(
366
- Utils.magic: array<unknown> => array<Internal.entity>
367
- )
393
+ (
394
+ await storage.loadOrThrow(
395
+ ~table=entityConfig.table,
396
+ ~filter=filter->scopeFilter(~table=entityConfig.table, ~scope),
397
+ )
398
+ )->(Utils.magic: array<unknown> => array<Internal.entity>)
368
399
 
369
400
  entities->Array.forEach(entity => {
370
401
  inMemTable->InMemoryTable.Entity.initValue(
@@ -18,19 +18,46 @@ import * as InMemoryTable from "./InMemoryTable.res.mjs";
18
18
  import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
19
19
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
20
20
 
21
- function loadById(loadManager, persistence, entityConfig, indexerState, shouldGroup, item, ecosystem, entityId) {
22
- let key = entityConfig.name + `.get`;
23
- let inMemTable = InMemoryStore.getInMemTable(indexerState, entityConfig);
21
+ function scopeKeySuffix(scope) {
22
+ if (scope === "crossChain") {
23
+ return "";
24
+ } else {
25
+ return `.` + ChainId.toString(scope);
26
+ }
27
+ }
28
+
29
+ function scopeFilter(filter, table, scope) {
30
+ let match = Table.getChainIdField(table);
31
+ if (scope === "crossChain" || match === undefined) {
32
+ return filter;
33
+ } else {
34
+ return {
35
+ operator: "and",
36
+ filters: [
37
+ filter,
38
+ {
39
+ operator: "=",
40
+ fieldName: match.fieldName,
41
+ fieldValue: scope
42
+ }
43
+ ]
44
+ };
45
+ }
46
+ }
47
+
48
+ function loadById(loadManager, persistence, entityConfig, scope, indexerState, shouldGroup, item, ecosystem, entityId) {
49
+ let key = entityConfig.name + `.get` + scopeKeySuffix(scope);
50
+ let inMemTable = InMemoryStore.getInMemTable(indexerState, entityConfig, scope);
24
51
  let load = async (idsToLoad, param) => {
25
52
  let storage = Persistence.getInitializedStorageOrThrow(persistence);
26
53
  let timerRef = IndexerState.startStorageLoad(indexerState, storage.name, key);
27
54
  let dbEntities;
28
55
  try {
29
- dbEntities = await storage.loadOrThrow({
56
+ dbEntities = await storage.loadOrThrow(scopeFilter({
30
57
  operator: "in",
31
58
  fieldName: Table.idFieldName,
32
59
  fieldValue: idsToLoad
33
- }, entityConfig.table);
60
+ }, entityConfig.table, scope), entityConfig.table);
34
61
  } catch (raw_exn) {
35
62
  let exn = Primitive_exceptions.internalToException(raw_exn);
36
63
  if (exn.RE_EXN_ID === Persistence.StorageError) {
@@ -181,9 +208,9 @@ function loadEffect(loadManager, persistence, effect, effectArgs, scope, indexer
181
208
  return LoadManager.call(loadManager, effectArgs, key, load, args => args.cacheKey, shouldGroup, hash => InMemoryStore.hasEffectOutput(inMemTable, hash), hash => InMemoryStore.getEffectOutputUnsafe(inMemTable, hash));
182
209
  }
183
210
 
184
- function loadByFilter(loadManager, persistence, entityConfig, indexerState, shouldGroup, item, ecosystem, filter) {
185
- let key = EntityFilter.toOperationKey(filter, entityConfig.name);
186
- let inMemTable = InMemoryStore.getInMemTable(indexerState, entityConfig);
211
+ function loadByFilter(loadManager, persistence, entityConfig, scope, indexerState, shouldGroup, item, ecosystem, filter) {
212
+ let key = EntityFilter.toOperationKey(filter, entityConfig.name) + scopeKeySuffix(scope);
213
+ let inMemTable = InMemoryStore.getInMemTable(indexerState, entityConfig, scope);
187
214
  let load = async (filters, param) => {
188
215
  let storage = Persistence.getInitializedStorageOrThrow(persistence);
189
216
  let timerRef = IndexerState.startStorageLoad(indexerState, storage.name, key);
@@ -195,7 +222,7 @@ function loadByFilter(loadManager, persistence, entityConfig, indexerState, shou
195
222
  let queries = EntityFilter.merge(filters);
196
223
  await Promise.all(queries.map(async filter => {
197
224
  try {
198
- let entities = await storage.loadOrThrow(filter, entityConfig.table);
225
+ let entities = await storage.loadOrThrow(scopeFilter(filter, entityConfig.table, scope), entityConfig.table);
199
226
  entities.forEach(entity => InMemoryTable.Entity.initValue(inMemTable, IndexerState.committedCheckpointId(indexerState), entity.id, entity));
200
227
  size.contents = size.contents + entities.length | 0;
201
228
  return;
@@ -2,6 +2,7 @@ let loadById: (
2
2
  ~loadManager: LoadManager.t,
3
3
  ~persistence: Persistence.t,
4
4
  ~entityConfig: Internal.entityConfig,
5
+ ~scope: Internal.chainScope,
5
6
  ~indexerState: IndexerState.t,
6
7
  ~shouldGroup: bool,
7
8
  ~item: Internal.item,
@@ -13,6 +14,7 @@ let loadByFilter: (
13
14
  ~loadManager: LoadManager.t,
14
15
  ~persistence: Persistence.t,
15
16
  ~entityConfig: Internal.entityConfig,
17
+ ~scope: Internal.chainScope,
16
18
  ~indexerState: IndexerState.t,
17
19
  ~shouldGroup: bool,
18
20
  ~item: Internal.item,
package/src/Metrics.res CHANGED
@@ -445,15 +445,15 @@ let renderMetrics = (b: builder, metrics: t) => {
445
445
  ~entries=chains,
446
446
  ~value=m => m.endBlock->Option.map(Int.toFloat),
447
447
  )
448
- b->series(
448
+ b->seriesOpt(
449
449
  ~name="envio_source_request_total",
450
- ~help="The number of requests made to data sources.",
450
+ ~help="The number of requests made to data sources. Heights pushed by a subscription stream are counted here too, under the heightPush and heightPushIgnored methods.",
451
451
  ~kind="counter",
452
452
  ~entries=sourceRequests,
453
- ~value=s => s.count->Int.toFloat,
453
+ ~value=s => s.count > 0 ? Some(s.count->Int.toFloat) : None,
454
454
  )
455
- // Skips a method's seconds line when it has no timing (e.g. heightSubscription,
456
- // which only ever records a count).
455
+ // Skips a method's seconds line when it has no timing (e.g. heightPush, which
456
+ // only ever records a count).
457
457
  b->seriesOpt(
458
458
  ~name="envio_source_request_seconds_total",
459
459
  ~help="Cumulative time spent on data source requests.",
@@ -144,7 +144,11 @@ function renderMetrics(b, metrics) {
144
144
  single(b, "envio_indexing_target_buffer_size", "The indexer-wide target buffer size shared across all chains. The actual number of items in the queue may exceed this value, but the indexer always tries to keep the buffer filled up to this target.", "gauge", metrics.targetBufferSize);
145
145
  series(b, "envio_indexing_buffer_block", "The highest block number that has been fully fetched by the indexer.", "gauge", chains, m => m.bufferBlockNumber);
146
146
  seriesOpt(b, "envio_indexing_end_block", "The block number to stop indexing at. (inclusive)", "gauge", chains, m => Stdlib_Option.map(m.endBlock, prim => prim));
147
- series(b, "envio_source_request_total", "The number of requests made to data sources.", "counter", sourceRequests, s => s.count);
147
+ seriesOpt(b, "envio_source_request_total", "The number of requests made to data sources. Heights pushed by a subscription stream are counted here too, under the heightPush and heightPushIgnored methods.", "counter", sourceRequests, s => {
148
+ if (s.count > 0) {
149
+ return s.count;
150
+ }
151
+ });
148
152
  seriesOpt(b, "envio_source_request_seconds_total", "Cumulative time spent on data source requests.", "counter", sourceRequests, s => {
149
153
  if (s.seconds !== 0) {
150
154
  return s.seconds;
@@ -63,11 +63,21 @@ type rollback = {
63
63
  progressBlockNumberByChainId: dict<int>,
64
64
  }
65
65
 
66
+ // One flush group: the changes an entity accumulated within a single chain
67
+ // scope. A per-chain entity contributes one group per chain, and the scope is
68
+ // what stamps the chain id onto the rows — it's never re-derived downstream.
66
69
  type updatedEntity = {
67
70
  entityConfig: Internal.entityConfig,
71
+ scope: Internal.chainScope,
68
72
  changes: array<Change.t<Internal.entity>>,
69
73
  }
70
74
 
75
+ // An id the rollback must delete, together with the scope its row lives in.
76
+ type rollbackRemoval = {
77
+ entityId: EntityId.t,
78
+ scope: Internal.chainScope,
79
+ }
80
+
71
81
  type storage = {
72
82
  // Identifier used as the `storage` label on Prometheus metrics.
73
83
  name: string,
@@ -119,6 +129,7 @@ type storage = {
119
129
  pruneStaleEntityHistory: (
120
130
  ~entityName: string,
121
131
  ~entityIndex: int,
132
+ ~chainIdColumn: option<string>,
122
133
  ~safeCheckpointId: Internal.checkpointId,
123
134
  ) => promise<unit>,
124
135
  // Get rollback target checkpoint
@@ -140,7 +151,7 @@ type storage = {
140
151
  getRollbackData: (
141
152
  ~entityConfig: Internal.entityConfig,
142
153
  ~rollbackTargetCheckpointId: Internal.checkpointId,
143
- ) => promise<(array<EntityId.t>, array<unknown>)>,
154
+ ) => promise<(array<rollbackRemoval>, array<unknown>)>,
144
155
  // Write batch to storage
145
156
  writeBatch: (
146
157
  ~batch: Batch.t,