envio 3.6.0 → 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 (43) hide show
  1. package/package.json +6 -6
  2. package/src/ChainState.res +10 -4
  3. package/src/ChainState.res.mjs +5 -1
  4. package/src/Config.res +12 -0
  5. package/src/Config.res.mjs +8 -2
  6. package/src/Core.res +15 -0
  7. package/src/Core.res.mjs +11 -0
  8. package/src/EventProcessing.res +6 -6
  9. package/src/EventProcessing.res.mjs +8 -6
  10. package/src/HandlerLoader.res +20 -8
  11. package/src/HandlerLoader.res.mjs +11 -2
  12. package/src/Metrics.res +5 -5
  13. package/src/Metrics.res.mjs +5 -1
  14. package/src/UserContext.res +358 -51
  15. package/src/UserContext.res.mjs +271 -35
  16. package/src/sources/SourceManager.res +6 -12
  17. package/src/sources/SourceManager.res.mjs +8 -5
  18. package/src/subgraph/blocks.ts +176 -0
  19. package/src/subgraph/calls.ts +213 -0
  20. package/src/subgraph/conformance.ts +99 -0
  21. package/src/subgraph/division.ts +100 -0
  22. package/src/subgraph/errors.ts +90 -0
  23. package/src/subgraph/graph-ts-types/VERSION +2 -0
  24. package/src/subgraph/graph-ts-types/chain/arweave.d.ts +70 -0
  25. package/src/subgraph/graph-ts-types/chain/cosmos.d.ts +327 -0
  26. package/src/subgraph/graph-ts-types/chain/ethereum.d.ts +233 -0
  27. package/src/subgraph/graph-ts-types/chain/near.d.ts +253 -0
  28. package/src/subgraph/graph-ts-types/chain/starknet.d.ts +32 -0
  29. package/src/subgraph/graph-ts-types/common/collections.d.ts +136 -0
  30. package/src/subgraph/graph-ts-types/common/conversion.d.ts +11 -0
  31. package/src/subgraph/graph-ts-types/common/datasource.d.ts +30 -0
  32. package/src/subgraph/graph-ts-types/common/eager-offset.d.ts +0 -0
  33. package/src/subgraph/graph-ts-types/common/json.d.ts +17 -0
  34. package/src/subgraph/graph-ts-types/common/numbers.d.ts +120 -0
  35. package/src/subgraph/graph-ts-types/common/value.d.ts +120 -0
  36. package/src/subgraph/graph-ts-types/common/yaml.d.ts +90 -0
  37. package/src/subgraph/graph-ts-types/global/global.d.ts +194 -0
  38. package/src/subgraph/graph-ts-types/helper-functions.d.ts +22 -0
  39. package/src/subgraph/graph-ts-types/index.d.ts +102 -0
  40. package/src/subgraph/graph-ts.ts +1695 -0
  41. package/src/subgraph/hosts.ts +148 -0
  42. package/src/subgraph/runtime.ts +827 -0
  43. package/src/subgraph/scope.ts +63 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envio",
3
- "version": "3.6.0",
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.6.0",
73
- "envio-linux-x64-musl": "3.6.0",
74
- "envio-linux-arm64": "3.6.0",
75
- "envio-darwin-x64": "3.6.0",
76
- "envio-darwin-arm64": "3.6.0"
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
  }
@@ -549,10 +549,16 @@ let targetBlock = (cs: t, ~chainTargetItems: float) => {
549
549
  let bufferBlockNumber = fetchState->FetchState.bufferBlockNumber
550
550
  switch cs->effectiveDensity {
551
551
  | Some(density) if density > 0. =>
552
- Pervasives.min(
553
- fetchCeiling,
554
- bufferBlockNumber + Math.ceil(chainTargetItems /. density)->Float.toInt,
555
- )
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
+ }
556
562
  | _ => Pervasives.min(bufferBlockNumber + coldTargetRange, fetchCeiling)
557
563
  }
558
564
  }
@@ -368,7 +368,11 @@ function targetBlock(cs, chainTargetItems) {
368
368
  let bufferBlockNumber = FetchState.bufferBlockNumber(fetchState);
369
369
  let density = effectiveDensity(cs);
370
370
  if (density !== undefined && density > 0) {
371
- return Primitive_int.min(fetchCeiling$1, bufferBlockNumber + (Math.ceil(chainTargetItems / density) | 0) | 0);
371
+ if (density * (fetchCeiling$1 - bufferBlockNumber | 0) <= chainTargetItems) {
372
+ return fetchCeiling$1;
373
+ } else {
374
+ return bufferBlockNumber + (Math.ceil(chainTargetItems / density) | 0) | 0;
375
+ }
372
376
  } else {
373
377
  return Primitive_int.min(bufferBlockNumber + 20000 | 0, fetchCeiling$1);
374
378
  }
package/src/Config.res CHANGED
@@ -119,6 +119,9 @@ type t = {
119
119
  userEntities: array<Internal.entityConfig>,
120
120
  allEntities: array<Internal.entityConfig>,
121
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>,
122
125
  }
123
126
 
124
127
  module EnvioAddresses = {
@@ -613,6 +616,7 @@ let publicConfigSchema = S.schema(s =>
613
616
  "svm": s.matches(S.option(publicConfigEcosystemSchema)),
614
617
  "enums": s.matches(S.option(S.dict(S.array(S.string)))),
615
618
  "entities": s.matches(S.option(S.array(entityJsonSchema))),
619
+ "subgraph": s.matches(S.option(S.json(~validate=false))),
616
620
  }
617
621
  )
618
622
 
@@ -1110,6 +1114,7 @@ let fromPublic = (publicConfigJson: JSON.t) => {
1110
1114
  userEntities,
1111
1115
  allEntities,
1112
1116
  allEnums,
1117
+ subgraph: publicConfig["subgraph"],
1113
1118
  }
1114
1119
  }
1115
1120
 
@@ -1246,6 +1251,13 @@ let stripSensitiveData = (json: JSON.t): JSON.t => {
1246
1251
  stripChains(obj->Dict.get("evm"))
1247
1252
  stripChains(obj->Dict.get("fuel"))
1248
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
+ }
1249
1261
  }
1250
1262
  | _ => ()
1251
1263
  }
@@ -433,7 +433,8 @@ let publicConfigSchema = S$RescriptSchema.schema(s => ({
433
433
  fuel: s.m(S$RescriptSchema.option(publicConfigEcosystemSchema)),
434
434
  svm: s.m(S$RescriptSchema.option(publicConfigEcosystemSchema)),
435
435
  enums: s.m(S$RescriptSchema.option(S$RescriptSchema.dict(S$RescriptSchema.array(S$RescriptSchema.string)))),
436
- entities: s.m(S$RescriptSchema.option(S$RescriptSchema.array(entityJsonSchema)))
436
+ entities: s.m(S$RescriptSchema.option(S$RescriptSchema.array(entityJsonSchema))),
437
+ subgraph: s.m(S$RescriptSchema.option(S$RescriptSchema.json(false)))
437
438
  }));
438
439
 
439
440
  function fromPublic(publicConfigJson) {
@@ -757,7 +758,8 @@ function fromPublic(publicConfigJson) {
757
758
  userEntitiesByName: userEntitiesByName,
758
759
  userEntities: userEntities,
759
760
  allEntities: allEntities,
760
- allEnums: allEnums
761
+ allEnums: allEnums,
762
+ subgraph: publicConfig.subgraph
761
763
  };
762
764
  }
763
765
 
@@ -902,6 +904,10 @@ function stripSensitiveData(json) {
902
904
  stripChains(cloned["evm"]);
903
905
  stripChains(cloned["fuel"]);
904
906
  stripChains(cloned["svm"]);
907
+ let match = cloned["subgraph"];
908
+ if (typeof match === "object" && match !== null && !Array.isArray(match)) {
909
+ Utils.Dict.deleteInPlace(match, "rpcUrls");
910
+ }
905
911
  }
906
912
  return cloned;
907
913
  }
package/src/Core.res CHANGED
@@ -24,8 +24,16 @@ type fromUserApiResult = {
24
24
  indexerCode: Null.t<string>,
25
25
  }
26
26
 
27
+ type fromSubgraphOptions = {
28
+ name?: string,
29
+ env?: dict<string>,
30
+ files?: dict<string>,
31
+ root?: string,
32
+ }
33
+
27
34
  type addon = {
28
35
  getConfigJson: (~configPath: Null.t<string>, ~directory: Null.t<string>) => string,
36
+ fromSubgraph: (string, string, fromSubgraphOptions) => fromUserApiResult,
29
37
  encodeIndexedTopic: (~abiType: string, ~value: unknown) => EvmTypes.Hex.t,
30
38
  fromUserApi: (string, fromUserApiOptions) => fromUserApiResult,
31
39
  runCli: (~args: array<string>, ~envioPackageDir: Null.t<string>) => promise<Null.t<string>>,
@@ -232,6 +240,13 @@ let fromUserApi = (~schema=?, ~env=?, ~files=?, ~withIndexerTypes=false, yaml) =
232
240
  )
233
241
  }
234
242
 
243
+ /// Parses a subgraph project without touching the filesystem: subgraph.yaml and
244
+ /// its schema inline, ABI bodies through `files`.
245
+ let fromSubgraph = (~name=?, ~env=?, ~files=?, ~root=?, ~manifest, ~schema) => {
246
+ let addon = getAddon()
247
+ addon.fromSubgraph(manifest, schema, {?name, ?env, ?files, ?root})
248
+ }
249
+
235
250
  let runCli = args => {
236
251
  let addon = getAddon()
237
252
  addon.runCli(~args, ~envioPackageDir=Null.make(envioPackageDir))
package/src/Core.res.mjs CHANGED
@@ -191,6 +191,16 @@ function fromUserApi(schema, env, files, withIndexerTypesOpt, yaml) {
191
191
  });
192
192
  }
193
193
 
194
+ function fromSubgraph(name, env, files, root, manifest, schema) {
195
+ let addon = getAddon();
196
+ return addon.fromSubgraph(manifest, schema, {
197
+ name: name,
198
+ env: env,
199
+ files: files,
200
+ root: root
201
+ });
202
+ }
203
+
194
204
  function runCli(args) {
195
205
  let addon = getAddon();
196
206
  return addon.runCli(args, envioPackageDir);
@@ -208,6 +218,7 @@ export {
208
218
  getAddon,
209
219
  getConfigJson,
210
220
  fromUserApi,
221
+ fromSubgraph,
211
222
  runCli,
212
223
  }
213
224
  /* envioPackageDir Not a pure module */
@@ -51,7 +51,7 @@ let runEventHandlerOrThrow = async (
51
51
  isPreload: false,
52
52
  chains,
53
53
  config,
54
- isResolved: false,
54
+ sync: UserContext.makeSyncState(),
55
55
  }
56
56
  await handler(
57
57
  (
@@ -61,7 +61,7 @@ let runEventHandlerOrThrow = async (
61
61
  }: Internal.handlerArgs
62
62
  ),
63
63
  )
64
- contextParams.isResolved = true
64
+ contextParams.sync.status = Resolved
65
65
  } catch {
66
66
  | exn =>
67
67
  throw(
@@ -102,7 +102,7 @@ let runHandlerOrThrow = async (
102
102
  isPreload: false,
103
103
  chains,
104
104
  config,
105
- isResolved: false,
105
+ sync: UserContext.makeSyncState(),
106
106
  }
107
107
  await handler(
108
108
  Ecosystem.makeOnBlockArgs(
@@ -111,7 +111,7 @@ let runHandlerOrThrow = async (
111
111
  ~context=UserContext.getHandlerContext(contextParams),
112
112
  ),
113
113
  )
114
- contextParams.isResolved = true
114
+ contextParams.sync.status = Resolved
115
115
  } catch {
116
116
  | exn =>
117
117
  throw(
@@ -186,7 +186,7 @@ let preloadBatchOrThrow = async (
186
186
  checkpointId,
187
187
  isPreload: true,
188
188
  chains,
189
- isResolved: false,
189
+ sync: UserContext.makeSyncState(),
190
190
  config,
191
191
  }),
192
192
  })
@@ -221,7 +221,7 @@ let preloadBatchOrThrow = async (
221
221
  checkpointId,
222
222
  isPreload: true,
223
223
  chains,
224
- isResolved: false,
224
+ sync: UserContext.makeSyncState(),
225
225
  config,
226
226
  }),
227
227
  )
@@ -38,6 +38,7 @@ let ProcessingError = /* @__PURE__ */Primitive_exceptions.create("EventProcessin
38
38
  async function runEventHandlerOrThrow(item, checkpointId, handler, indexerState, loadManager, persistence, chains, config) {
39
39
  let timeBeforeHandler = Performance.now();
40
40
  try {
41
+ let contextParams_sync = UserContext.makeSyncState();
41
42
  let contextParams = {
42
43
  item: item,
43
44
  checkpointId: checkpointId,
@@ -47,13 +48,13 @@ async function runEventHandlerOrThrow(item, checkpointId, handler, indexerState,
47
48
  isPreload: false,
48
49
  chains: chains,
49
50
  config: config,
50
- isResolved: false
51
+ sync: contextParams_sync
51
52
  };
52
53
  await handler({
53
54
  event: Ecosystem.getItemEvent(item, config.ecosystem),
54
55
  context: UserContext.getHandlerContext(contextParams)
55
56
  });
56
- contextParams.isResolved = true;
57
+ contextParams_sync.status = "Resolved";
57
58
  } catch (raw_exn) {
58
59
  let exn = Primitive_exceptions.internalToException(raw_exn);
59
60
  throw {
@@ -79,6 +80,7 @@ async function runHandlerOrThrow(item, checkpointId, indexerState, loadManager,
79
80
  }
80
81
  }
81
82
  try {
83
+ let contextParams_sync = UserContext.makeSyncState();
82
84
  let contextParams = {
83
85
  item: item,
84
86
  checkpointId: checkpointId,
@@ -88,10 +90,10 @@ async function runHandlerOrThrow(item, checkpointId, indexerState, loadManager,
88
90
  isPreload: false,
89
91
  chains: chains,
90
92
  config: config,
91
- isResolved: false
93
+ sync: contextParams_sync
92
94
  };
93
95
  await item.onBlockRegistration.handler(Ecosystem.makeOnBlockArgs(item.blockNumber, config.ecosystem, UserContext.getHandlerContext(contextParams)));
94
- contextParams.isResolved = true;
96
+ contextParams_sync.status = "Resolved";
95
97
  return;
96
98
  } catch (raw_exn) {
97
99
  let exn = Primitive_exceptions.internalToException(raw_exn);
@@ -133,7 +135,7 @@ async function preloadBatchOrThrow(batch, loadManager, persistence, config, inde
133
135
  isPreload: true,
134
136
  chains: chains,
135
137
  config: config,
136
- isResolved: false
138
+ sync: UserContext.makeSyncState()
137
139
  })
138
140
  }).then(() => IndexerState.endPreloadHandler(indexerState, timerRef, contractName, eventName))));
139
141
  } catch (exn) {
@@ -151,7 +153,7 @@ async function preloadBatchOrThrow(batch, loadManager, persistence, config, inde
151
153
  isPreload: true,
152
154
  chains: chains,
153
155
  config: config,
154
- isResolved: false
156
+ sync: UserContext.makeSyncState()
155
157
  })))));
156
158
  } catch (exn$1) {
157
159
 
@@ -77,18 +77,30 @@ let autoLoadFromSrcHandlers = async (~handlers: string) => {
77
77
  // `HandlerRegister.finishRegistration`. This loads the user handler files
78
78
  // (populating the global `HandlerRegister` registry as a side effect) and
79
79
  // returns the resulting per-chain registrations.
80
+ // The subgraph runtime lives in this package but is never exported: it's
81
+ // reached only from here, when the resolved config carries a translated
82
+ // manifest.
83
+ let registerSubgraph: (JSON.t, ~isDev: bool) => promise<unit> = %raw(`(subgraph, isDev) =>
84
+ import("./subgraph/runtime.ts").then((m) => m.registerSubgraph({ ...subgraph, isDev }))`)
85
+
80
86
  let registerAllHandlers = async (~config: Config.t): HandlerRegister.registrationsByChainId => {
81
87
  HandlerRegister.startRegistration(~config)
82
88
 
83
- // Auto-load all .js files from src/handlers directory
84
- await autoLoadFromSrcHandlers(~handlers=config.handlers)
89
+ switch config.subgraph {
90
+ // A subgraph project's `src/` holds AssemblyScript mappings, not envio
91
+ // handlers, so the usual auto-load would import them without a scope.
92
+ | Some(subgraph) => await registerSubgraph(subgraph, ~isDev=config.isDev)
93
+ | None =>
94
+ // Auto-load all .js files from src/handlers directory
95
+ await autoLoadFromSrcHandlers(~handlers=config.handlers)
85
96
 
86
- // Load contract-specific handlers
87
- let _ = await config.contractHandlers
88
- ->Array.map(({name, handler}) => {
89
- registerContractHandlers(~contractName=name, ~handler)
90
- })
91
- ->Promise.all
97
+ // Load contract-specific handlers
98
+ let _ = await config.contractHandlers
99
+ ->Array.map(({name, handler}) => {
100
+ registerContractHandlers(~contractName=name, ~handler)
101
+ })
102
+ ->Promise.all
103
+ }
92
104
 
93
105
  HandlerRegister.finishRegistration(~config)
94
106
  }
@@ -56,10 +56,18 @@ async function autoLoadFromSrcHandlers(handlers) {
56
56
  })));
57
57
  }
58
58
 
59
+ let registerSubgraph = ((subgraph, isDev) =>
60
+ import("./subgraph/runtime.ts").then((m) => m.registerSubgraph({ ...subgraph, isDev })));
61
+
59
62
  async function registerAllHandlers(config) {
60
63
  HandlerRegister.startRegistration(config);
61
- await autoLoadFromSrcHandlers(config.handlers);
62
- await Promise.all(config.contractHandlers.map(param => registerContractHandlers(param.name, param.handler)));
64
+ let subgraph = config.subgraph;
65
+ if (subgraph !== undefined) {
66
+ await registerSubgraph(subgraph, config.isDev);
67
+ } else {
68
+ await autoLoadFromSrcHandlers(config.handlers);
69
+ await Promise.all(config.contractHandlers.map(param => registerContractHandlers(param.name, param.handler)));
70
+ }
63
71
  return HandlerRegister.finishRegistration(config);
64
72
  }
65
73
 
@@ -67,6 +75,7 @@ export {
67
75
  toImportUrl,
68
76
  registerContractHandlers,
69
77
  autoLoadFromSrcHandlers,
78
+ registerSubgraph,
70
79
  registerAllHandlers,
71
80
  }
72
81
  /* Not a pure module */
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;