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
@@ -5,6 +5,7 @@ import * as Table from "../db/Table.res.mjs";
5
5
  import * as Utils from "../Utils.res.mjs";
6
6
  import * as ChainId from "../ChainId.res.mjs";
7
7
  import * as Logging from "../Logging.res.mjs";
8
+ import * as Internal from "../Internal.res.mjs";
8
9
  import * as Persistence from "../Persistence.res.mjs";
9
10
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
10
11
  import * as EntityHistory from "../db/EntityHistory.res.mjs";
@@ -197,11 +198,14 @@ async function setCheckpointsOrThrow(client, batch, database) {
197
198
  }
198
199
  }
199
200
 
200
- async function setUpdatesOrThrow(client, cache, changes, entityConfig, database) {
201
+ async function setUpdatesOrThrow(client, cache, changes, entityConfig, scope, database) {
201
202
  if (changes.length === 0) {
202
203
  return;
203
204
  }
204
- let cached = cache.get(entityConfig);
205
+ let chainIdField = Table.getChainIdField(entityConfig.table);
206
+ let scopeChainId = Internal.chainScopeChainId(scope);
207
+ let cacheKey = entityConfig.name + `|` + Internal.chainScopeToString(scope);
208
+ let cached = cache[cacheKey];
205
209
  let match;
206
210
  if (cached !== undefined) {
207
211
  match = cached;
@@ -211,6 +215,9 @@ async function setUpdatesOrThrow(client, cache, changes, entityConfig, database)
211
215
  EntityHistory.makeSetUpdateSchema(Table.getIdSchema(entityConfig.table), makeClickHouseEntitySchema(entityConfig.table)),
212
216
  S$RescriptSchema.object(s => {
213
217
  s.tag(EntityHistory.changeFieldName, "DELETE");
218
+ if (chainIdField !== undefined && scopeChainId !== undefined) {
219
+ s.tag(Table.getClickHouseDbFieldName(chainIdField), Primitive_option.valFromOption(scopeChainId));
220
+ }
214
221
  return {
215
222
  type: "DELETE",
216
223
  entityId: s.f(Table.idFieldName, Table.getIdSchema(entityConfig.table)),
@@ -222,12 +229,30 @@ async function setUpdatesOrThrow(client, cache, changes, entityConfig, database)
222
229
  tableName: cached_tableName,
223
230
  convertOrThrow: cached_convertOrThrow
224
231
  };
225
- cache.set(entityConfig, cached$1);
232
+ cache[cacheKey] = cached$1;
226
233
  match = cached$1;
227
234
  }
228
235
  let tableName = match.tableName;
236
+ let changes$1;
237
+ if (chainIdField !== undefined && scopeChainId !== undefined) {
238
+ let chainId = Primitive_option.valFromOption(scopeChainId);
239
+ changes$1 = changes.map(change => {
240
+ if (change.type === "SET") {
241
+ return {
242
+ type: "SET",
243
+ entityId: change.entityId,
244
+ entity: Internal.stampChainId(change.entity, chainIdField.fieldName, chainId),
245
+ checkpointId: change.checkpointId
246
+ };
247
+ } else {
248
+ return change;
249
+ }
250
+ });
251
+ } else {
252
+ changes$1 = changes;
253
+ }
229
254
  try {
230
- let values = match.convertOrThrow(changes);
255
+ let values = match.convertOrThrow(changes$1);
231
256
  return await insertWithRetry(client, tableName, values, "JSONEachRow", undefined);
232
257
  } catch (raw_exn) {
233
258
  let exn = Primitive_exceptions.internalToException(raw_exn);
@@ -346,6 +371,13 @@ ORDER BY (` + "id" + `)`;
346
371
  function makeCreateViewQuery(entityConfig, database, onClusterOpt) {
347
372
  let onCluster = onClusterOpt !== undefined ? onClusterOpt : false;
348
373
  let historyTableName = EntityHistory.historyTableName(entityConfig.name, entityConfig.index);
374
+ let field = Table.getChainIdField(entityConfig.table);
375
+ let dedupKey = (
376
+ field !== undefined ? [
377
+ Table.idFieldName,
378
+ Table.getClickHouseDbFieldName(field)
379
+ ] : [Table.idFieldName]
380
+ ).map(name => `\`` + name + `\``).join(", ");
349
381
  let checkpointsTableName = InternalTable.Checkpoints.table.tableName;
350
382
  let entityFields = Stdlib_Array.filterMap(entityConfig.table.fields, field => {
351
383
  if (field.TAG !== "Field") {
@@ -363,7 +395,7 @@ FROM (
363
395
  FROM ` + database + `.\`` + historyTableName + `\`
364
396
  WHERE \`` + EntityHistory.checkpointIdFieldName + `\` <= (SELECT max(` + "id" + `) FROM ` + database + `.\`` + checkpointsTableName + `\`)
365
397
  ORDER BY \`` + EntityHistory.checkpointIdFieldName + `\` DESC
366
- LIMIT 1 BY \`` + Table.idFieldName + `\`
398
+ LIMIT 1 BY ` + dedupKey + `
367
399
  )
368
400
  WHERE \`` + EntityHistory.changeFieldName + `\` = '` + "SET" + `'`;
369
401
  }
@@ -82,27 +82,41 @@ type safeReorgBlocks = {
82
82
  // - Rollbacks will not cross the safe checkpoint id, so rows older than the anchor can never be referenced again.
83
83
  // - If nothing changed in reorg threshold (after the safe checkpoint), the current state for that id can be reconstructed from the
84
84
  // origin table; we do not need a pre-safe anchor for it.
85
- let makePruneStaleEntityHistoryQuery = (~entityName, ~entityIndex, ~pgSchema) => {
85
+ // A per-chain entity's rows are only comparable within a chain, so every id
86
+ // correlation in the history SQL widens to (id, chain id).
87
+ let makeKeyColumns = (~chainIdColumn: option<string>) =>
88
+ switch chainIdColumn {
89
+ | Some(column) => ["id", `"${column}"`]
90
+ | None => ["id"]
91
+ }
92
+
93
+ let makeKeyMatch = (~chainIdColumn, ~left, ~right) =>
94
+ makeKeyColumns(~chainIdColumn)
95
+ ->Array.map(column => `${left}.${column} = ${right}.${column}`)
96
+ ->Array.joinUnsafe(" AND ")
97
+
98
+ let makePruneStaleEntityHistoryQuery = (~entityName, ~entityIndex, ~pgSchema, ~chainIdColumn) => {
86
99
  let historyTableRef = `"${pgSchema}"."${historyTableName(~entityName, ~entityIndex)}"`
100
+ let keyColumns = makeKeyColumns(~chainIdColumn)
101
+ let anchorKeys = keyColumns->Array.map(column => `t.${column}`)->Array.joinUnsafe(", ")
87
102
 
103
+ // Whether a key still has a row above the safe checkpoint is an aggregate
104
+ // over the same groups as the anchor, so it's computed in the one pass
105
+ // rather than as a per-row correlated lookup.
106
+ // `d.envio_checkpoint_id <= $1` is implied by the rest of the predicate — it
107
+ // is there to keep the rows above the safe checkpoint out of the join.
88
108
  `WITH anchors AS (
89
- SELECT t.id, MAX(t.${checkpointIdFieldName}) AS keep_checkpoint_id
90
- FROM ${historyTableRef} t WHERE t.${checkpointIdFieldName} <= $1
91
- GROUP BY t.id
109
+ SELECT ${anchorKeys},
110
+ MAX(t.${checkpointIdFieldName}) FILTER (WHERE t.${checkpointIdFieldName} <= $1) AS keep_checkpoint_id,
111
+ bool_or(t.${checkpointIdFieldName} > $1) AS has_above
112
+ FROM ${historyTableRef} t
113
+ GROUP BY ${anchorKeys}
92
114
  )
93
115
  DELETE FROM ${historyTableRef} d
94
116
  USING anchors a
95
- WHERE d.id = a.id
96
- AND (
97
- d.${checkpointIdFieldName} < a.keep_checkpoint_id
98
- OR (
99
- d.${checkpointIdFieldName} = a.keep_checkpoint_id AND
100
- NOT EXISTS (
101
- SELECT 1 FROM ${historyTableRef} ps
102
- WHERE ps.id = d.id AND ps.${checkpointIdFieldName} > $1
103
- )
104
- )
105
- );`
117
+ WHERE ${makeKeyMatch(~chainIdColumn, ~left="d", ~right="a")}
118
+ AND d.${checkpointIdFieldName} <= $1
119
+ AND (d.${checkpointIdFieldName} < a.keep_checkpoint_id OR NOT a.has_above);`
106
120
  }
107
121
 
108
122
  let pruneStaleEntityHistory = (
@@ -110,26 +124,40 @@ let pruneStaleEntityHistory = (
110
124
  ~entityName,
111
125
  ~entityIndex,
112
126
  ~pgSchema,
127
+ ~chainIdColumn,
113
128
  ~safeCheckpointId,
114
129
  ): promise<unit> => {
115
130
  sql->Postgres.preparedUnsafe(
116
- makePruneStaleEntityHistoryQuery(~entityName, ~entityIndex, ~pgSchema),
131
+ makePruneStaleEntityHistoryQuery(~entityName, ~entityIndex, ~pgSchema, ~chainIdColumn),
117
132
  [safeCheckpointId->BigInt.toString]->(Utils.magic: array<string> => unknown),
118
133
  )
119
134
  }
120
135
 
121
136
  // If an entity doesn't have a history before the update
122
137
  // we create it automatically with envio_checkpoint_id 0
123
- let makeBackfillHistoryQuery = (~pgSchema, ~entityName, ~entityIndex, ~idPgType) => {
138
+ // The ids belong to a single chain (the flush group's scope), so the chain is
139
+ // bound once as $2 rather than unnested alongside them.
140
+ let makeBackfillHistoryQuery = (
141
+ ~pgSchema,
142
+ ~entityName,
143
+ ~entityIndex,
144
+ ~idPgType,
145
+ ~chainIdColumn,
146
+ ~chainId: option<ChainId.t>,
147
+ ) => {
124
148
  let historyTableRef = `"${pgSchema}"."${historyTableName(~entityName, ~entityIndex)}"`
149
+ let chainFilter = switch (chainIdColumn, chainId) {
150
+ | (Some(column), Some(_)) => ` AND e."${column}" = $2`
151
+ | _ => ""
152
+ }
125
153
  `WITH target_ids AS (
126
154
  SELECT UNNEST($1::${idPgType}[]) AS id
127
155
  ),
128
156
  missing_history AS (
129
157
  SELECT e.*
130
158
  FROM "${pgSchema}"."${entityName}" e
131
- JOIN target_ids t ON e.id = t.id
132
- LEFT JOIN ${historyTableRef} h ON h.id = e.id
159
+ JOIN target_ids t ON e.id = t.id${chainFilter}
160
+ LEFT JOIN ${historyTableRef} h ON ${makeKeyMatch(~chainIdColumn, ~left="h", ~right="e")}
133
161
  WHERE h.id IS NULL
134
162
  )
135
163
  INSERT INTO ${historyTableRef}
@@ -142,13 +170,28 @@ let backfillHistory = (
142
170
  ~pgSchema,
143
171
  ~table: Table.table,
144
172
  ~entityIndex,
173
+ ~chainId: option<ChainId.t>,
145
174
  ~ids: array<EntityId.t>,
146
175
  ) => {
147
176
  let idPgType = table->Table.getIdPgFieldType(~pgSchema)
177
+ let chainIdColumn = table->Table.getChainIdField->Option.map(Table.getPgDbFieldName)
178
+ let params = [table->Table.encodeIdsToJson(ids)->(Utils.magic: JSON.t => unknown)]
179
+ switch (chainIdColumn, chainId) {
180
+ | (Some(_), Some(chainId)) =>
181
+ params->Array.push(chainId->(Utils.magic: ChainId.t => unknown))->ignore
182
+ | _ => ()
183
+ }
148
184
  sql
149
185
  ->Postgres.preparedUnsafe(
150
- makeBackfillHistoryQuery(~entityName=table.tableName, ~entityIndex, ~pgSchema, ~idPgType),
151
- [table->Table.encodeIdsToJson(ids)]->(Utils.magic: array<JSON.t> => unknown),
186
+ makeBackfillHistoryQuery(
187
+ ~entityName=table.tableName,
188
+ ~entityIndex,
189
+ ~pgSchema,
190
+ ~idPgType,
191
+ ~chainIdColumn,
192
+ ~chainId,
193
+ ),
194
+ params->Obj.magic,
152
195
  )
153
196
  ->Utils.Promise.ignoreValue
154
197
  }
@@ -3,6 +3,8 @@
3
3
  import * as Table from "./Table.res.mjs";
4
4
  import * as Js_string from "@rescript/runtime/lib/es6/Js_string.js";
5
5
  import * as Stdlib_BigInt from "@rescript/runtime/lib/es6/Stdlib_BigInt.js";
6
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
7
+ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
6
8
  import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
7
9
 
8
10
  let variants = [
@@ -71,42 +73,54 @@ function historyTableName(entityName, entityIndex) {
71
73
  return Js_string.slice(0, 63 - entityIndexStr.length | 0, fullName) + entityIndexStr;
72
74
  }
73
75
 
74
- function makePruneStaleEntityHistoryQuery(entityName, entityIndex, pgSchema) {
76
+ function makeKeyColumns(chainIdColumn) {
77
+ if (chainIdColumn !== undefined) {
78
+ return [
79
+ "id",
80
+ `"` + chainIdColumn + `"`
81
+ ];
82
+ } else {
83
+ return ["id"];
84
+ }
85
+ }
86
+
87
+ function makeKeyMatch(chainIdColumn, left, right) {
88
+ return makeKeyColumns(chainIdColumn).map(column => left + `.` + column + ` = ` + right + `.` + column).join(" AND ");
89
+ }
90
+
91
+ function makePruneStaleEntityHistoryQuery(entityName, entityIndex, pgSchema, chainIdColumn) {
75
92
  let historyTableRef = `"` + pgSchema + `"."` + historyTableName(entityName, entityIndex) + `"`;
93
+ let keyColumns = makeKeyColumns(chainIdColumn);
94
+ let anchorKeys = keyColumns.map(column => `t.` + column).join(", ");
76
95
  return `WITH anchors AS (
77
- SELECT t.id, MAX(t.` + checkpointIdFieldName + `) AS keep_checkpoint_id
78
- FROM ` + historyTableRef + ` t WHERE t.` + checkpointIdFieldName + ` <= $1
79
- GROUP BY t.id
96
+ SELECT ` + anchorKeys + `,
97
+ MAX(t.` + checkpointIdFieldName + `) FILTER (WHERE t.` + checkpointIdFieldName + ` <= $1) AS keep_checkpoint_id,
98
+ bool_or(t.` + checkpointIdFieldName + ` > $1) AS has_above
99
+ FROM ` + historyTableRef + ` t
100
+ GROUP BY ` + anchorKeys + `
80
101
  )
81
102
  DELETE FROM ` + historyTableRef + ` d
82
103
  USING anchors a
83
- WHERE d.id = a.id
84
- AND (
85
- d.` + checkpointIdFieldName + ` < a.keep_checkpoint_id
86
- OR (
87
- d.` + checkpointIdFieldName + ` = a.keep_checkpoint_id AND
88
- NOT EXISTS (
89
- SELECT 1 FROM ` + historyTableRef + ` ps
90
- WHERE ps.id = d.id AND ps.` + checkpointIdFieldName + ` > $1
91
- )
92
- )
93
- );`;
104
+ WHERE ` + makeKeyMatch(chainIdColumn, "d", "a") + `
105
+ AND d.` + checkpointIdFieldName + ` <= $1
106
+ AND (d.` + checkpointIdFieldName + ` < a.keep_checkpoint_id OR NOT a.has_above);`;
94
107
  }
95
108
 
96
- function pruneStaleEntityHistory(sql, entityName, entityIndex, pgSchema, safeCheckpointId) {
97
- return sql.unsafe(makePruneStaleEntityHistoryQuery(entityName, entityIndex, pgSchema), [safeCheckpointId.toString()], {prepare: true});
109
+ function pruneStaleEntityHistory(sql, entityName, entityIndex, pgSchema, chainIdColumn, safeCheckpointId) {
110
+ return sql.unsafe(makePruneStaleEntityHistoryQuery(entityName, entityIndex, pgSchema, chainIdColumn), [safeCheckpointId.toString()], {prepare: true});
98
111
  }
99
112
 
100
- function makeBackfillHistoryQuery(pgSchema, entityName, entityIndex, idPgType) {
113
+ function makeBackfillHistoryQuery(pgSchema, entityName, entityIndex, idPgType, chainIdColumn, chainId) {
101
114
  let historyTableRef = `"` + pgSchema + `"."` + historyTableName(entityName, entityIndex) + `"`;
115
+ let chainFilter = chainIdColumn !== undefined && chainId !== undefined ? ` AND e."` + chainIdColumn + `" = $2` : "";
102
116
  return `WITH target_ids AS (
103
117
  SELECT UNNEST($1::` + idPgType + `[]) AS id
104
118
  ),
105
119
  missing_history AS (
106
120
  SELECT e.*
107
121
  FROM "` + pgSchema + `"."` + entityName + `" e
108
- JOIN target_ids t ON e.id = t.id
109
- LEFT JOIN ` + historyTableRef + ` h ON h.id = e.id
122
+ JOIN target_ids t ON e.id = t.id` + chainFilter + `
123
+ LEFT JOIN ` + historyTableRef + ` h ON ` + makeKeyMatch(chainIdColumn, "h", "e") + `
110
124
  WHERE h.id IS NULL
111
125
  )
112
126
  INSERT INTO ` + historyTableRef + `
@@ -114,9 +128,14 @@ SELECT *, 0 AS ` + checkpointIdFieldName + `, '` + "SET" + `' as ` + changeField
114
128
  FROM missing_history;`;
115
129
  }
116
130
 
117
- function backfillHistory(sql, pgSchema, table, entityIndex, ids) {
131
+ function backfillHistory(sql, pgSchema, table, entityIndex, chainId, ids) {
118
132
  let idPgType = Table.getIdPgFieldType(table, pgSchema);
119
- return sql.unsafe(makeBackfillHistoryQuery(pgSchema, table.tableName, entityIndex, idPgType), [Table.encodeIdsToJson(table, ids)], {prepare: true});
133
+ let chainIdColumn = Stdlib_Option.map(Table.getChainIdField(table), Table.getPgDbFieldName);
134
+ let params = [Table.encodeIdsToJson(table, ids)];
135
+ if (chainIdColumn !== undefined && chainId !== undefined) {
136
+ params.push(Primitive_option.valFromOption(chainId));
137
+ }
138
+ return sql.unsafe(makeBackfillHistoryQuery(pgSchema, table.tableName, entityIndex, idPgType, chainIdColumn, chainId), params, {prepare: true});
120
139
  }
121
140
 
122
141
  function rollback(sql, pgSchema, entityName, entityIndex, rollbackTargetCheckpointId) {
@@ -138,6 +157,8 @@ export {
138
157
  maxPgTableNameLength,
139
158
  historyTablePrefix,
140
159
  historyTableName,
160
+ makeKeyColumns,
161
+ makeKeyMatch,
141
162
  makePruneStaleEntityHistoryQuery,
142
163
  pruneStaleEntityHistory,
143
164
  makeBackfillHistoryQuery,
@@ -26,17 +26,17 @@ let fields = [
26
26
  ];
27
27
 
28
28
  let table = Table.mkTable("envio_chains", undefined, [
29
- Table.mkField("id", "ChainId", ChainId.schema, undefined, undefined, undefined, true, undefined, undefined, undefined, undefined, undefined),
30
- Table.mkField("start_block", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
31
- Table.mkField("end_block", "Int32", S$RescriptSchema.$$null(S$RescriptSchema.int), undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined),
32
- Table.mkField("max_reorg_depth", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
33
- Table.mkField("buffer_block", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
34
- Table.mkField("source_block", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
35
- Table.mkField("first_event_block", "Int32", S$RescriptSchema.$$null(S$RescriptSchema.int), undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined),
36
- Table.mkField("ready_at", "Date", S$RescriptSchema.$$null(Utils.Schema.dbDate), undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined),
37
- Table.mkField("events_processed", "UInt52", S$RescriptSchema.float, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
38
- Table.mkField("_is_hyper_sync", "Boolean", S$RescriptSchema.bool, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
39
- Table.mkField("progress_block", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined)
29
+ Table.mkField("id", "ChainId", ChainId.schema, undefined, undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined),
30
+ Table.mkField("start_block", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
31
+ Table.mkField("end_block", "Int32", S$RescriptSchema.$$null(S$RescriptSchema.int), undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
32
+ Table.mkField("max_reorg_depth", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
33
+ Table.mkField("buffer_block", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
34
+ Table.mkField("source_block", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
35
+ Table.mkField("first_event_block", "Int32", S$RescriptSchema.$$null(S$RescriptSchema.int), undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
36
+ Table.mkField("ready_at", "Date", S$RescriptSchema.$$null(Utils.Schema.dbDate), undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
37
+ Table.mkField("events_processed", "UInt52", S$RescriptSchema.float, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
38
+ Table.mkField("_is_hyper_sync", "Boolean", S$RescriptSchema.bool, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
39
+ Table.mkField("progress_block", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined)
40
40
  ], undefined);
41
41
 
42
42
  function initialFromConfig(chainConfig) {
@@ -235,8 +235,8 @@ let Chains = {
235
235
  };
236
236
 
237
237
  let table$1 = Table.mkTable("envio_info", undefined, [
238
- Table.mkField("id", "Int32", S$RescriptSchema.int, "1", undefined, undefined, true, undefined, undefined, undefined, undefined, undefined),
239
- Table.mkField("config", "String", S$RescriptSchema.string, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined)
238
+ Table.mkField("id", "Int32", S$RescriptSchema.int, "1", undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined),
239
+ Table.mkField("config", "String", S$RescriptSchema.string, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined)
240
240
  ], undefined);
241
241
 
242
242
  let undefinedTableSqlState = "42P01";
@@ -284,11 +284,11 @@ let dbSchema = S$RescriptSchema.object(s => ({
284
284
  }));
285
285
 
286
286
  let table$2 = Table.mkTable("envio_checkpoints", undefined, [
287
- Table.mkField("id", "UInt64", S$RescriptSchema.bigint, undefined, undefined, undefined, true, undefined, undefined, undefined, undefined, undefined),
288
- Table.mkField("chain_id", "ChainId", ChainId.schema, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
289
- Table.mkField("block_number", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
290
- Table.mkField("block_hash", "String", S$RescriptSchema.$$null(S$RescriptSchema.string), undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined),
291
- Table.mkField("events_processed", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined)
287
+ Table.mkField("id", "UInt64", S$RescriptSchema.bigint, undefined, undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined),
288
+ Table.mkField("chain_id", "ChainId", ChainId.schema, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
289
+ Table.mkField("block_number", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
290
+ Table.mkField("block_hash", "String", S$RescriptSchema.$$null(S$RescriptSchema.string), undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
291
+ Table.mkField("events_processed", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined)
292
292
  ], undefined);
293
293
 
294
294
  function makeGetReorgCheckpointsQuery(pgSchema) {
@@ -412,19 +412,19 @@ let schema = S$RescriptSchema.schema(s => ({
412
412
  }));
413
413
 
414
414
  let table$3 = Table.mkTable("raw_events", undefined, [
415
- Table.mkField("chain_id", "ChainId", ChainId.schema, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
416
- Table.mkField("event_id", "UInt64", S$RescriptSchema.bigint, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
417
- Table.mkField("event_name", "String", S$RescriptSchema.string, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
418
- Table.mkField("contract_name", "String", S$RescriptSchema.string, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
419
- Table.mkField("block_number", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
420
- Table.mkField("log_index", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
421
- Table.mkField("src_address", "String", Address.schema, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
422
- Table.mkField("block_hash", "String", S$RescriptSchema.string, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
423
- Table.mkField("block_timestamp", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
424
- Table.mkField("block_fields", "Json", S$RescriptSchema.json(false), undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
425
- Table.mkField("transaction_fields", "Json", S$RescriptSchema.json(false), undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
426
- Table.mkField("params", "Json", S$RescriptSchema.json(false), undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
427
- Table.mkField("serial", "BigSerial", S$RescriptSchema.$$null(S$RescriptSchema.bigint), undefined, undefined, true, true, undefined, undefined, undefined, undefined, undefined)
415
+ Table.mkField("chain_id", "ChainId", ChainId.schema, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
416
+ Table.mkField("event_id", "UInt64", S$RescriptSchema.bigint, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
417
+ Table.mkField("event_name", "String", S$RescriptSchema.string, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
418
+ Table.mkField("contract_name", "String", S$RescriptSchema.string, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
419
+ Table.mkField("block_number", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
420
+ Table.mkField("log_index", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
421
+ Table.mkField("src_address", "String", Address.schema, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
422
+ Table.mkField("block_hash", "String", S$RescriptSchema.string, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
423
+ Table.mkField("block_timestamp", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
424
+ Table.mkField("block_fields", "Json", S$RescriptSchema.json(false), undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
425
+ Table.mkField("transaction_fields", "Json", S$RescriptSchema.json(false), undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
426
+ Table.mkField("params", "Json", S$RescriptSchema.json(false), undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
427
+ Table.mkField("serial", "BigSerial", S$RescriptSchema.$$null(S$RescriptSchema.bigint), undefined, undefined, true, true, undefined, undefined, undefined, undefined, undefined, undefined)
428
428
  ], undefined);
429
429
 
430
430
  let RawEvents = {
package/src/db/Table.res CHANGED
@@ -43,6 +43,10 @@ type field = {
43
43
  isNullable: bool,
44
44
  isPrimaryKey: bool,
45
45
  isIndex: bool,
46
+ // The chain-id column appended to a per-chain entity's table. It's part of
47
+ // the primary key but not of the entity schema the handlers see, so the
48
+ // read paths skip it and the write path stamps it from the flush scope.
49
+ isChainId: bool,
46
50
  linkedEntity: option<string>,
47
51
  defaultValue: option<string>,
48
52
  description: option<string>,
@@ -71,6 +75,7 @@ let mkField = (
71
75
  ~isNullable=false,
72
76
  ~isPrimaryKey=false,
73
77
  ~isIndex=false,
78
+ ~isChainId=false,
74
79
  ~linkedEntity=?,
75
80
  ~description=?,
76
81
  ~postgresDbName=?,
@@ -84,6 +89,7 @@ let mkField = (
84
89
  isNullable,
85
90
  isPrimaryKey,
86
91
  isIndex,
92
+ isChainId,
87
93
  linkedEntity,
88
94
  defaultValue: default,
89
95
  description,
@@ -252,6 +258,16 @@ let getDerivedFromFields = table =>
252
258
  }
253
259
  )
254
260
 
261
+ // The chain-id column of a per-chain entity table. None for cross-chain
262
+ // entities and every internal table.
263
+ let getChainIdField = (table): option<field> =>
264
+ table.fields->Array.findMap(field =>
265
+ switch field {
266
+ | Field({isChainId: true} as field) => Some(field)
267
+ | _ => None
268
+ }
269
+ )
270
+
255
271
  let getFieldByName = (table, fieldName) =>
256
272
  table.fields->Array.find(field => field->getUserDefinedFieldName === fieldName)
257
273
 
@@ -334,12 +350,13 @@ let queryFields: table => dict<queryField> = Utils.WeakMap.memoize(table => {
334
350
  // Parses rows into entity objects keyed by API field names (the camelCase
335
351
  // record field names are type-level only), reading each value from the
336
352
  // row key produced by ~rowFieldName.
337
- let makeRowsSchema = (table, ~rowFieldName) =>
353
+ let makeRowsSchema = (table, ~rowFieldName, ~skipChainId=false) =>
338
354
  S.array(
339
355
  S.object(s => {
340
356
  let dict = Dict.make()
341
357
  table.fields->Array.forEach(field =>
342
358
  switch field {
359
+ | Field({isChainId: true}) if skipChainId => ()
343
360
  | Field(field) =>
344
361
  dict->Dict.set(field->getApiFieldName, s.field(field->rowFieldName, field.fieldSchema))
345
362
  | DerivedFrom(_) => ()
@@ -361,6 +378,12 @@ let pgRowsSchema: table => S.t<array<unknown>> = Utils.WeakMap.memoize(table =>
361
378
  table->makeRowsSchema(~rowFieldName=getPgDbFieldName)
362
379
  )
363
380
 
381
+ // Rows shaped like the entity the handlers see: the chain-id column is dropped,
382
+ // since the chain is already known from the scope the rows were loaded for.
383
+ let pgEntityRowsSchema: table => S.t<array<unknown>> = Utils.WeakMap.memoize(table =>
384
+ table->makeRowsSchema(~rowFieldName=getPgDbFieldName, ~skipChainId=true)
385
+ )
386
+
364
387
  exception NonExistingTableField(string)
365
388
 
366
389
  /*
@@ -16,11 +16,12 @@ function makeEnumConfig(name, variants) {
16
16
  };
17
17
  }
18
18
 
19
- function mkField(fieldName, fieldType, fieldSchema, $$default, isArrayOpt, isNullableOpt, isPrimaryKeyOpt, isIndexOpt, linkedEntity, description, postgresDbName, clickhouseDbName) {
19
+ function mkField(fieldName, fieldType, fieldSchema, $$default, isArrayOpt, isNullableOpt, isPrimaryKeyOpt, isIndexOpt, isChainIdOpt, linkedEntity, description, postgresDbName, clickhouseDbName) {
20
20
  let isArray = isArrayOpt !== undefined ? isArrayOpt : false;
21
21
  let isNullable = isNullableOpt !== undefined ? isNullableOpt : false;
22
22
  let isPrimaryKey = isPrimaryKeyOpt !== undefined ? isPrimaryKeyOpt : false;
23
23
  let isIndex = isIndexOpt !== undefined ? isIndexOpt : false;
24
+ let isChainId = isChainIdOpt !== undefined ? isChainIdOpt : false;
24
25
  return {
25
26
  TAG: "Field",
26
27
  _0: {
@@ -31,6 +32,7 @@ function mkField(fieldName, fieldType, fieldSchema, $$default, isArrayOpt, isNul
31
32
  isNullable: isNullable,
32
33
  isPrimaryKey: isPrimaryKey,
33
34
  isIndex: isIndex,
35
+ isChainId: isChainId,
34
36
  linkedEntity: linkedEntity,
35
37
  defaultValue: $$default,
36
38
  description: description,
@@ -225,6 +227,18 @@ function getDerivedFromFields(table) {
225
227
  });
226
228
  }
227
229
 
230
+ function getChainIdField(table) {
231
+ return Stdlib_Array.findMap(table.fields, field => {
232
+ if (field.TAG !== "Field") {
233
+ return;
234
+ }
235
+ let field$1 = field._0;
236
+ if (field$1.isChainId) {
237
+ return field$1;
238
+ }
239
+ });
240
+ }
241
+
228
242
  function getFieldByName(table, fieldName) {
229
243
  return table.fields.find(field => getUserDefinedFieldName(field) === fieldName);
230
244
  }
@@ -288,7 +302,8 @@ let queryFields = Utils.$$WeakMap.memoize(table => {
288
302
  return dict;
289
303
  });
290
304
 
291
- function makeRowsSchema(table, rowFieldName) {
305
+ function makeRowsSchema(table, rowFieldName, skipChainIdOpt) {
306
+ let skipChainId = skipChainIdOpt !== undefined ? skipChainIdOpt : false;
292
307
  return S$RescriptSchema.array(S$RescriptSchema.object(s => {
293
308
  let dict = {};
294
309
  table.fields.forEach(field => {
@@ -296,15 +311,20 @@ function makeRowsSchema(table, rowFieldName) {
296
311
  return;
297
312
  }
298
313
  let field$1 = field._0;
314
+ if (field$1.isChainId && skipChainId) {
315
+ return;
316
+ }
299
317
  dict[getApiFieldName(field$1)] = s.f(rowFieldName(field$1), field$1.fieldSchema);
300
318
  });
301
319
  return dict;
302
320
  }));
303
321
  }
304
322
 
305
- let rowsSchema = Utils.$$WeakMap.memoize(table => makeRowsSchema(table, getApiFieldName));
323
+ let rowsSchema = Utils.$$WeakMap.memoize(table => makeRowsSchema(table, getApiFieldName, undefined));
324
+
325
+ let pgRowsSchema = Utils.$$WeakMap.memoize(table => makeRowsSchema(table, getPgDbFieldName, undefined));
306
326
 
307
- let pgRowsSchema = Utils.$$WeakMap.memoize(table => makeRowsSchema(table, getPgDbFieldName));
327
+ let pgEntityRowsSchema = Utils.$$WeakMap.memoize(table => makeRowsSchema(table, getPgDbFieldName, true));
308
328
 
309
329
  let NonExistingTableField = /* @__PURE__ */Primitive_exceptions.create("Table.NonExistingTableField");
310
330
 
@@ -465,6 +485,7 @@ export {
465
485
  getNonDefaultFields,
466
486
  getLinkedEntityFields,
467
487
  getDerivedFromFields,
488
+ getChainIdField,
468
489
  getFieldByName,
469
490
  NoIdField,
470
491
  getIdFieldOrThrow,
@@ -477,6 +498,7 @@ export {
477
498
  makeRowsSchema,
478
499
  rowsSchema,
479
500
  pgRowsSchema,
501
+ pgEntityRowsSchema,
480
502
  NonExistingTableField,
481
503
  getUnfilteredCompositeIndexesUnsafe,
482
504
  toSqlParams,
@@ -468,16 +468,16 @@ let getSourceNewHeight = async (
468
468
  // head on every (re)connect; waking the wait loop on a height we already
469
469
  // know spins it and leaks fallback pollers (#1270).
470
470
  if newHeight > sourceState.knownHeight {
471
+ sourceState->recordRequestStats([{Source.method: "heightPush", seconds: 0.}])
471
472
  sourceState.knownHeight = newHeight
472
473
  let resolvers = sourceState.pendingHeightResolvers
473
474
  sourceState.pendingHeightResolvers = []
474
475
  resolvers->Array.forEach(resolve => resolve(newHeight))
476
+ } else {
477
+ sourceState->recordRequestStats([{Source.method: "heightPushIgnored", seconds: 0.}])
475
478
  }
476
479
  })
477
480
  sourceState.unsubscribe = Some(unsubscribe)
478
- // Count a subscription (re)start rather than every pushed height —
479
- // there's no request/response to time here.
480
- sourceState->recordRequestStats([{Source.method: "heightSubscription", seconds: 0.}])
481
481
  | _ =>
482
482
  // Slowdown polling when the chain isn't progressing
483
483
  let pollingInterval = if reducedPolling {
@@ -724,9 +724,7 @@ let executeQuery = async (
724
724
  ) {
725
725
  | Some(s) =>
726
726
  if s.source !== sourceManager.activeSource {
727
- let logger = Logging.createChild(
728
- ~params={"chainId": sourceManager.activeSource.chainId},
729
- )
727
+ let logger = Logging.createChild(~params={"chainId": sourceManager.activeSource.chainId})
730
728
  logger->Logging.childInfo({
731
729
  "msg": "Switching data-source",
732
730
  "source": s.source.name,
@@ -736,9 +734,7 @@ let executeQuery = async (
736
734
  }
737
735
  s
738
736
  | None =>
739
- let logger = Logging.createChild(
740
- ~params={"chainId": sourceManager.activeSource.chainId},
741
- )
737
+ let logger = Logging.createChild(~params={"chainId": sourceManager.activeSource.chainId})
742
738
  %raw(`null`)->ErrorHandling.mkLogAndRaise(~logger, ~msg=noSourcesError)
743
739
  }
744
740
  sourceManager.activeSource = sourceState.source
@@ -895,9 +891,7 @@ let getBlockHashes = async (sourceManager: t, ~blockNumbers: array<int>, ~isReal
895
891
  let sourceState = switch sourceManager->getNextSource(~isRealtime) {
896
892
  | Some(s) => s
897
893
  | None =>
898
- let logger = Logging.createChild(
899
- ~params={"chainId": sourceManager.activeSource.chainId},
900
- )
894
+ let logger = Logging.createChild(~params={"chainId": sourceManager.activeSource.chainId})
901
895
  %raw(`null`)->ErrorHandling.mkLogAndRaise(
902
896
  ~logger,
903
897
  ~msg="No data-sources available for fetching block hashes.",