envio 3.7.0 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/index.d.ts +331 -142
  2. package/package.json +6 -6
  3. package/src/ChainState.res +9 -1
  4. package/src/ChainState.res.mjs +6 -1
  5. package/src/Config.res +19 -10
  6. package/src/Config.res.mjs +17 -7
  7. package/src/Envio.res +54 -76
  8. package/src/EventConfigBuilder.res +159 -30
  9. package/src/EventConfigBuilder.res.mjs +120 -24
  10. package/src/HandlerRegister.res +20 -11
  11. package/src/HandlerRegister.res.mjs +22 -5
  12. package/src/Hasura.res +30 -22
  13. package/src/Hasura.res.mjs +11 -5
  14. package/src/InMemoryStore.res +1 -6
  15. package/src/InMemoryStore.res.mjs +1 -3
  16. package/src/Internal.res +33 -13
  17. package/src/Internal.res.mjs +12 -16
  18. package/src/Main.res +2 -5
  19. package/src/MemoryStorage.res +40 -8
  20. package/src/MemoryStorage.res.mjs +38 -16
  21. package/src/Persistence.res +4 -2
  22. package/src/PgStorage.res +6 -1
  23. package/src/PgStorage.res.mjs +1 -1
  24. package/src/SimulateItems.res +261 -9
  25. package/src/SimulateItems.res.mjs +218 -47
  26. package/src/TestIndexer.res +26 -10
  27. package/src/TestIndexer.res.mjs +34 -12
  28. package/src/bindings/ClickHouse.res +33 -6
  29. package/src/bindings/ClickHouse.res.mjs +25 -4
  30. package/src/db/InternalTable.res +11 -0
  31. package/src/db/InternalTable.res.mjs +7 -0
  32. package/src/sources/EvmHyperSyncSource.res +3 -2
  33. package/src/sources/SimulateSource.res +8 -4
  34. package/src/sources/SimulateSource.res.mjs +7 -3
  35. package/src/sources/Svm.res +34 -7
  36. package/src/sources/Svm.res.mjs +32 -4
  37. package/src/sources/SvmHyperSyncClient.res +10 -12
  38. package/src/sources/SvmHyperSyncClient.res.mjs +3 -2
  39. package/src/sources/SvmHyperSyncSource.res +91 -34
  40. package/src/sources/SvmHyperSyncSource.res.mjs +77 -37
  41. package/src/sources/TransactionStore.res +38 -0
  42. package/src/sources/TransactionStore.res.mjs +5 -0
  43. package/svm.schema.json +27 -78
@@ -238,7 +238,7 @@ let alwaysIncludedBlockFields = [
238
238
  function resolveFieldSelection(blockFields, transactionFields, globalBlockFieldsSet, globalTransactionFieldsSet) {
239
239
  let selectedBlockFields = blockFields !== undefined ? new Set(alwaysIncludedBlockFields.concat(blockFields)) : globalBlockFieldsSet;
240
240
  let selectedTransactionFields = transactionFields !== undefined ? new Set(transactionFields) : globalTransactionFieldsSet;
241
- return Internal.makeFieldSelection(selectedBlockFields, selectedTransactionFields, Evm.eventBlockFieldMask, Evm.eventTransactionFieldMask);
241
+ return Internal.makeFieldSelection(selectedBlockFields, selectedTransactionFields, undefined, undefined, undefined, Evm.eventBlockFieldMask, Evm.eventTransactionFieldMask);
242
242
  }
243
243
 
244
244
  let internalBlockFields = ["number"];
@@ -248,53 +248,76 @@ let rawEventBlockFields = [
248
248
  "timestamp"
249
249
  ];
250
250
 
251
- let selectionKinds = [
251
+ let evmSelectionKinds = [
252
252
  "block",
253
253
  "transaction"
254
254
  ];
255
255
 
256
- function validateFieldsShapeOrThrow(fields, registration) {
256
+ let svmSelectionKinds = [
257
+ "instruction",
258
+ "transaction",
259
+ "accountActivity",
260
+ "block",
261
+ "log"
262
+ ];
263
+
264
+ function quotedJoin(names) {
265
+ return names.map(name => `"` + name + `"`).join(", ");
266
+ }
267
+
268
+ function validateFieldsShapeOrThrow(fields, registration, validKeys, shapeNoun) {
257
269
  if (typeof fields !== "object" || fields === null || Array.isArray(fields)) {
258
- Stdlib_JsError.throwWithMessage(`The fields option of ` + registration + ` must be an object of block and transaction field names.`);
270
+ Stdlib_JsError.throwWithMessage(`The fields option of ` + registration + ` must be an object of ` + shapeNoun + ` field names.`);
259
271
  }
260
272
  Object.keys(fields).forEach(key => {
261
- if (!selectionKinds.includes(key)) {
262
- return Stdlib_JsError.throwWithMessage(`Invalid "` + key + `" key in the fields option of ` + registration + `. Valid keys: ` + selectionKinds.join(", ") + `.`);
273
+ if (!validKeys.includes(key)) {
274
+ return Stdlib_JsError.throwWithMessage(`Invalid "` + key + `" key in the fields option of ` + registration + `. Valid keys: ` + quotedJoin(validKeys) + `.`);
263
275
  }
264
276
  });
265
277
  }
266
278
 
279
+ function selectionList(fields, key) {
280
+ let value = fields[key];
281
+ if (value !== undefined) {
282
+ return Primitive_option.valFromOption(value);
283
+ }
284
+ }
285
+
267
286
  let validBlockFields = new Set(Evm.blockFields);
268
287
 
269
288
  let validTransactionFields = new Set(Evm.transactionFields);
270
289
 
271
- function parseFieldsOrThrow(fields, valid, kind, registration) {
290
+ function parseFieldsOrThrow(fields, valid, kind, registration, rejectEmptyOpt) {
291
+ let rejectEmpty = rejectEmptyOpt !== undefined ? rejectEmptyOpt : false;
272
292
  let seen = new Set();
273
293
  let fields$1 = fields !== undefined ? (
274
294
  Array.isArray(fields) ? fields : Stdlib_JsError.throwWithMessage(`The fields.` + kind + ` option of ` + registration + ` must be an array of field names.`)
275
295
  ) : [];
276
296
  fields$1.forEach(name => {
277
297
  if (!valid.has(name)) {
278
- Stdlib_JsError.throwWithMessage(`Invalid "` + name + `" field in the fields.` + kind + ` option of ` + registration + `. Valid ` + kind + ` fields: ` + Array.from(valid).join(", ") + `.`);
298
+ Stdlib_JsError.throwWithMessage(`Invalid "` + name + `" field in the fields.` + kind + ` option of ` + registration + `. Valid ` + kind + ` fields: ` + quotedJoin(Array.from(valid)) + `.`);
279
299
  }
280
300
  if (seen.has(name)) {
281
301
  Stdlib_JsError.throwWithMessage(`Duplicate "` + name + `" field in the fields.` + kind + ` option of ` + registration + `.`);
282
302
  }
283
303
  seen.add(name);
284
304
  });
305
+ if (rejectEmpty && fields$1.length === 0) {
306
+ Stdlib_JsError.throwWithMessage(`The fields.` + kind + ` option of ` + registration + ` must list at least one field.`);
307
+ }
285
308
  return seen;
286
309
  }
287
310
 
288
311
  function resolveInlineFieldSelection(fields, contractName, eventName, enableRawEvents) {
289
312
  let registration = `the "` + eventName + `" event registration on contract "` + contractName + `"`;
290
- validateFieldsShapeOrThrow(fields, registration);
291
- let blockFields = parseFieldsOrThrow(fields.block, validBlockFields, "block", registration);
292
- let transactionFields = parseFieldsOrThrow(fields.transaction, validTransactionFields, "transaction", registration);
313
+ validateFieldsShapeOrThrow(fields, registration, evmSelectionKinds, "block and transaction");
314
+ let blockFields = parseFieldsOrThrow(selectionList(fields, "block"), validBlockFields, "block", registration, undefined);
315
+ let transactionFields = parseFieldsOrThrow(selectionList(fields, "transaction"), validTransactionFields, "transaction", registration, undefined);
293
316
  Utils.$$Set.addMany(blockFields, internalBlockFields);
294
317
  if (enableRawEvents) {
295
318
  Utils.$$Set.addMany(blockFields, rawEventBlockFields);
296
319
  }
297
- return Internal.makeFieldSelection(blockFields, transactionFields, Evm.eventBlockFieldMask, Evm.eventTransactionFieldMask);
320
+ return Internal.makeFieldSelection(blockFields, transactionFields, undefined, undefined, undefined, Evm.eventBlockFieldMask, Evm.eventTransactionFieldMask);
298
321
  }
299
322
 
300
323
  function buildEvmEventConfig(contractName, eventName, sighash, params, blockFields, transactionFields, globalBlockFieldsSetOpt, globalTransactionFieldsSetOpt) {
@@ -342,20 +365,85 @@ function buildEvmOnEventRegistration(eventConfig, isWildcard, handler, contractR
342
365
  };
343
366
  }
344
367
 
345
- let alwaysIncludedSvmBlockFields = [
368
+ let alwaysIncludedSvmBlockFields = ["slot"];
369
+
370
+ let validSvmInstructionFields = new Set([
371
+ "args",
372
+ "accounts",
373
+ "accountArguments",
374
+ "programId",
375
+ "data",
376
+ "path",
377
+ "isInner"
378
+ ]);
379
+
380
+ let validSvmTransactionFields = new Set([
381
+ "transactionIndex",
382
+ "signature",
383
+ "feePayer",
384
+ "success",
385
+ "err",
386
+ "fee",
387
+ "computeUnitsConsumed",
388
+ "accountKeys",
389
+ "recentBlockhash",
390
+ "version",
391
+ "allSignatures"
392
+ ]);
393
+
394
+ let validSvmAccountActivityFields = new Set([
395
+ "address",
396
+ "transactionAccountIndex",
397
+ "isSigner",
398
+ "isWritable",
399
+ "lamports",
400
+ "lamports.pre",
401
+ "lamports.post",
402
+ "token",
403
+ "token.mint",
404
+ "token.owner",
405
+ "token.decimals",
406
+ "token.preAmount",
407
+ "token.postAmount"
408
+ ]);
409
+
410
+ let validSvmBlockFields = new Set([
346
411
  "slot",
347
412
  "time",
348
- "hash"
349
- ];
413
+ "hash",
414
+ "height",
415
+ "parentSlot",
416
+ "parentHash"
417
+ ]);
418
+
419
+ let validSvmLogFields = new Set([
420
+ "kind",
421
+ "message"
422
+ ]);
350
423
 
351
- function buildSvmInstructionEventConfig(contractName, instructionName, programId, discriminator, discriminatorByteLen, includeLogs, transactionFieldsOpt, blockFieldsOpt, accountFilters, isInner, accountsOpt, argsOpt, definedTypesOpt) {
352
- let transactionFields = transactionFieldsOpt !== undefined ? transactionFieldsOpt : [];
353
- let blockFields = blockFieldsOpt !== undefined ? blockFieldsOpt : [];
424
+ function resolveSvmInlineFieldSelection(fields, contractName, eventName) {
425
+ let registration = `the "` + eventName + `" event registration on contract "` + contractName + `"`;
426
+ validateFieldsShapeOrThrow(fields, registration, svmSelectionKinds, "instruction, transaction, accountActivity, block and log");
427
+ let accountActivity = selectionList(fields, "accountActivity");
428
+ let log = selectionList(fields, "log");
429
+ let instructionFields = parseFieldsOrThrow(selectionList(fields, "instruction"), validSvmInstructionFields, "instruction", registration, undefined);
430
+ let transactionFields = parseFieldsOrThrow(selectionList(fields, "transaction"), validSvmTransactionFields, "transaction", registration, undefined);
431
+ let accountActivityFields = parseFieldsOrThrow(accountActivity, validSvmAccountActivityFields, "accountActivity", registration, Stdlib_Option.isSome(accountActivity));
432
+ let blockFields = parseFieldsOrThrow(selectionList(fields, "block"), validSvmBlockFields, "block", registration, undefined);
433
+ let logFields = parseFieldsOrThrow(log, validSvmLogFields, "log", registration, Stdlib_Option.isSome(log));
434
+ Utils.$$Set.addMany(blockFields, alwaysIncludedSvmBlockFields);
435
+ if (accountActivityFields.size > 0) {
436
+ transactionFields.add("accountActivities");
437
+ }
438
+ return Internal.makeFieldSelection(blockFields, transactionFields, Primitive_option.some(instructionFields), Primitive_option.some(accountActivityFields), Primitive_option.some(logFields), Svm.eventBlockFieldMask, Svm.eventTransactionFieldMask);
439
+ }
440
+
441
+ function buildSvmInstructionEventConfig(contractName, instructionName, programId, discriminator, discriminatorByteLen, accountFilters, isInner, accountsOpt, argsOpt, definedTypesOpt) {
354
442
  let accounts = accountsOpt !== undefined ? accountsOpt : [];
355
443
  let args = argsOpt !== undefined ? argsOpt : null;
356
444
  let definedTypes = definedTypesOpt !== undefined ? definedTypesOpt : null;
357
445
  let paramsSchema = Utils.Schema.coerceToJsonPgType(S$RescriptSchema.json(false));
358
- let fieldSelection = Internal.makeFieldSelection(new Set(alwaysIncludedSvmBlockFields.concat(blockFields)), new Set(transactionFields), Svm.eventBlockFieldMask, Svm.eventTransactionFieldMask);
446
+ let fieldSelection = Internal.makeFieldSelection(new Set(alwaysIncludedSvmBlockFields), new Set(), undefined, undefined, undefined, Svm.eventBlockFieldMask, Svm.eventTransactionFieldMask);
359
447
  return {
360
448
  id: discriminator !== undefined ? discriminator : "none",
361
449
  name: instructionName,
@@ -366,7 +454,6 @@ function buildSvmInstructionEventConfig(contractName, instructionName, programId
366
454
  programId: programId,
367
455
  discriminator: discriminator,
368
456
  discriminatorByteLen: discriminatorByteLen,
369
- includeLogs: includeLogs,
370
457
  accountFilters: accountFilters,
371
458
  isInner: isInner,
372
459
  accounts: accounts,
@@ -375,7 +462,7 @@ function buildSvmInstructionEventConfig(contractName, instructionName, programId
375
462
  };
376
463
  }
377
464
 
378
- function buildSvmOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, startBlock) {
465
+ function buildSvmOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, fieldSelection, startBlock) {
379
466
  return {
380
467
  index: -1,
381
468
  eventConfig: eventConfig,
@@ -385,7 +472,7 @@ function buildSvmOnEventRegistration(eventConfig, isWildcard, handler, contractR
385
472
  filterByAddresses: false,
386
473
  dependsOnAddresses: Internal.dependsOnAddresses(isWildcard, false),
387
474
  startBlock: startBlock,
388
- fieldSelection: eventConfig.fieldSelection
475
+ fieldSelection: fieldSelection !== undefined ? fieldSelection : eventConfig.fieldSelection
389
476
  };
390
477
  }
391
478
 
@@ -438,7 +525,7 @@ function buildFuelEventConfig(contractName, eventName, kind, sighash, rawAbi) {
438
525
  contractName: contractName,
439
526
  paramsRawEventSchema: paramsSchema,
440
527
  simulateParamsSchema: paramsSchema,
441
- fieldSelection: Internal.makeFieldSelection(new Set(Fuel.blockFields), new Set(Fuel.transactionFields), Fuel.eventBlockFieldMask, Fuel.eventTransactionFieldMask),
528
+ fieldSelection: Internal.makeFieldSelection(new Set(Fuel.blockFields), new Set(Fuel.transactionFields), undefined, undefined, undefined, Fuel.eventBlockFieldMask, Fuel.eventTransactionFieldMask),
442
529
  kind: fuelKind
443
530
  };
444
531
  }
@@ -474,8 +561,11 @@ export {
474
561
  resolveFieldSelection,
475
562
  internalBlockFields,
476
563
  rawEventBlockFields,
477
- selectionKinds,
564
+ evmSelectionKinds,
565
+ svmSelectionKinds,
566
+ quotedJoin,
478
567
  validateFieldsShapeOrThrow,
568
+ selectionList,
479
569
  validBlockFields,
480
570
  validTransactionFields,
481
571
  parseFieldsOrThrow,
@@ -483,6 +573,12 @@ export {
483
573
  buildEvmEventConfig,
484
574
  buildEvmOnEventRegistration,
485
575
  alwaysIncludedSvmBlockFields,
576
+ validSvmInstructionFields,
577
+ validSvmTransactionFields,
578
+ validSvmAccountActivityFields,
579
+ validSvmBlockFields,
580
+ validSvmLogFields,
581
+ resolveSvmInlineFieldSelection,
486
582
  buildSvmInstructionEventConfig,
487
583
  buildSvmOnEventRegistration,
488
584
  buildFuelEventConfig,
@@ -154,6 +154,7 @@ let buildOnEventRegistrationWith = (
154
154
  ~isWildcard,
155
155
  ~handler,
156
156
  ~contractRegister,
157
+ ~fieldSelection?,
157
158
  ~startBlock?,
158
159
  ) :> Internal.onEventRegistration)
159
160
  | Evm =>
@@ -299,21 +300,29 @@ let addOnEventRegistration = (
299
300
  let fieldSelection = switch eventOptions->Option.flatMap(v => v.fields) {
300
301
  | None => None
301
302
  | Some(fields) =>
302
- // Only EVM resolves a registration selection of its own; Fuel and SVM take
303
- // theirs from the event config, so an inline one would be silently dropped.
304
- if registration.config.ecosystem.name !== Evm {
303
+ switch registration.config.ecosystem.name {
304
+ | Evm =>
305
+ Some(
306
+ EventConfigBuilder.resolveInlineFieldSelection(
307
+ fields,
308
+ ~contractName,
309
+ ~eventName,
310
+ ~enableRawEvents=registration.config.enableRawEvents,
311
+ ),
312
+ )
313
+ | Svm =>
314
+ Some(
315
+ EventConfigBuilder.resolveSvmInlineFieldSelection(
316
+ fields,
317
+ ~contractName,
318
+ ~eventName,
319
+ ),
320
+ )
321
+ | Fuel =>
305
322
  JsError.throwWithMessage(
306
323
  `The fields option of the "${eventName}" event registration on contract "${contractName}" is only supported on EVM. Select the fields in your config instead.`,
307
324
  )
308
325
  }
309
- Some(
310
- EventConfigBuilder.resolveInlineFieldSelection(
311
- fields,
312
- ~contractName,
313
- ~eventName,
314
- ~enableRawEvents=registration.config.enableRawEvents,
315
- ),
316
- )
317
326
  }
318
327
  let matched = ref(false)
319
328
  registration.config.chainMap
@@ -102,7 +102,7 @@ function buildOnEventRegistrationWith(config, chainId, eventConfig, isWildcard,
102
102
  case "fuel" :
103
103
  return EventConfigBuilder.buildFuelOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, startBlock);
104
104
  case "svm" :
105
- return EventConfigBuilder.buildSvmOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, startBlock);
105
+ return EventConfigBuilder.buildSvmOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, fieldSelection, startBlock);
106
106
  }
107
107
  }
108
108
 
@@ -200,7 +200,24 @@ function addOnEventRegistration(registration, contractName, eventName, handler,
200
200
  let isWildcard = Stdlib_Option.getOr(Stdlib_Option.flatMap(eventOptions, v => v.wildcard), false);
201
201
  let where = Stdlib_Option.flatMap(eventOptions, v => v.where);
202
202
  let fields = Stdlib_Option.flatMap(eventOptions, v => v.fields);
203
- let fieldSelection = fields !== undefined ? (registration.config.ecosystem.name !== "evm" ? Stdlib_JsError.throwWithMessage(`The fields option of the "` + eventName + `" event registration on contract "` + contractName + `" is only supported on EVM. Select the fields in your config instead.`) : undefined, EventConfigBuilder.resolveInlineFieldSelection(fields, contractName, eventName, registration.config.enableRawEvents)) : undefined;
203
+ let fieldSelection;
204
+ if (fields !== undefined) {
205
+ let fields$1 = Primitive_option.valFromOption(fields);
206
+ let match = registration.config.ecosystem.name;
207
+ switch (match) {
208
+ case "evm" :
209
+ fieldSelection = EventConfigBuilder.resolveInlineFieldSelection(fields$1, contractName, eventName, registration.config.enableRawEvents);
210
+ break;
211
+ case "fuel" :
212
+ fieldSelection = Stdlib_JsError.throwWithMessage(`The fields option of the "` + eventName + `" event registration on contract "` + contractName + `" is only supported on EVM. Select the fields in your config instead.`);
213
+ break;
214
+ case "svm" :
215
+ fieldSelection = EventConfigBuilder.resolveSvmInlineFieldSelection(fields$1, contractName, eventName);
216
+ break;
217
+ }
218
+ } else {
219
+ fieldSelection = undefined;
220
+ }
204
221
  let matched = {
205
222
  contents: false
206
223
  };
@@ -220,8 +237,8 @@ function addOnEventRegistration(registration, contractName, eventName, handler,
220
237
  if (matched.contents) {
221
238
  return;
222
239
  }
223
- let match = describeConfigured(registration, contractName);
224
- let eventNames = match[1];
240
+ let match$1 = describeConfigured(registration, contractName);
241
+ let eventNames = match$1[1];
225
242
  let listOr = (names, empty) => {
226
243
  if (Utils.$$Array.isEmpty(names)) {
227
244
  return empty;
@@ -230,7 +247,7 @@ function addOnEventRegistration(registration, contractName, eventName, handler,
230
247
  }
231
248
  };
232
249
  if (Utils.$$Array.isEmpty(eventNames)) {
233
- return Stdlib_JsError.throwWithMessage(`Contract "` + contractName + `" is not configured on any chain, so its handler for "` + eventName + `" would never run. Add it to your config, or remove the registration. Configured contracts: ` + listOr(match[0], "none") + `.`);
250
+ return Stdlib_JsError.throwWithMessage(`Contract "` + contractName + `" is not configured on any chain, so its handler for "` + eventName + `" would never run. Add it to your config, or remove the registration. Configured contracts: ` + listOr(match$1[0], "none") + `.`);
234
251
  } else {
235
252
  return Stdlib_JsError.throwWithMessage(`Event "` + eventName + `" is not configured on contract "` + contractName + `", so its handler would never run. Add it to your config, or remove the registration. Configured events on "` + contractName + `": ` + listOr(eventNames, "none") + `.`);
236
253
  }
package/src/Hasura.res CHANGED
@@ -286,9 +286,7 @@ let createSelectPermission = async (
286
286
  // column joins alongside the id — without it the relationship would resolve to
287
287
  // another chain's row with the same id.
288
288
  let makeColumnMapping = (~relationalKey, ~isDerivedFrom, ~chainIdColumn) => {
289
- let pairs = [
290
- isDerivedFrom ? `"id": "${relationalKey}"` : `"${relationalKey}": "id"`,
291
- ]
289
+ let pairs = [isDerivedFrom ? `"id": "${relationalKey}"` : `"${relationalKey}": "id"`]
292
290
  switch chainIdColumn {
293
291
  | Some(column) => pairs->Array.push(`"${column}": "${column}"`)->ignore
294
292
  | None => ()
@@ -309,7 +307,6 @@ let createEntityRelationship = async (
309
307
  ~chainIdColumn: option<string>,
310
308
  ~comment: option<string>=?,
311
309
  ) => {
312
-
313
310
  let tableJson = {
314
311
  "schema": pgSchema,
315
312
  "name": tableName,
@@ -347,15 +344,7 @@ let createEntityRelationship = async (
347
344
  )
348
345
  }
349
346
 
350
- let trackDatabase = async (
351
- ~endpoint,
352
- ~auth,
353
- ~pgSchema,
354
- ~userEntities: array<Internal.entityConfig>,
355
- ~aggregateEntities,
356
- ~responseLimit,
357
- ~schema,
358
- ) => {
347
+ let makeTableConfigs = (~userEntities: array<Internal.entityConfig>) => {
359
348
  let exposedInternalTableConfigs = [
360
349
  {
361
350
  tableName: InternalTable.RawEvents.table.tableName,
@@ -373,12 +362,27 @@ let trackDatabase = async (
373
362
  columnConfigs: dict{},
374
363
  },
375
364
  ]
376
- let userTableConfigs = userEntities->Array.map(entity => {
377
- tableName: entity.table.tableName,
378
- description: entity.table.description,
379
- columnConfigs: entity.table->makeColumnConfigs,
380
- })
381
- let tableConfigs = [exposedInternalTableConfigs, userTableConfigs]->Array.flat
365
+ let userTableConfigs =
366
+ userEntities
367
+ ->Array.filter(entity => !entity.internal)
368
+ ->Array.map(entity => {
369
+ tableName: entity.table.tableName,
370
+ description: entity.table.description,
371
+ columnConfigs: entity.table->makeColumnConfigs,
372
+ })
373
+ [exposedInternalTableConfigs, userTableConfigs]->Array.flat
374
+ }
375
+
376
+ let trackDatabase = async (
377
+ ~endpoint,
378
+ ~auth,
379
+ ~pgSchema,
380
+ ~userEntities: array<Internal.entityConfig>,
381
+ ~aggregateEntities,
382
+ ~responseLimit,
383
+ ~schema,
384
+ ) => {
385
+ let tableConfigs = makeTableConfigs(~userEntities)
382
386
  let tableNames = tableConfigs->Array.map(c => c.tableName)
383
387
 
384
388
  Logging.info("Tracking tables in Hasura")
@@ -414,10 +418,14 @@ let trackDatabase = async (
414
418
  ->Option.flatMap(e => e.table->Table.getChainIdField)
415
419
  ->Option.map(Table.getPgDbFieldName)
416
420
 
417
- for i in 0 to userEntities->Array.length - 1 {
418
- let entityConfig = userEntities->Array.getUnsafe(i)
421
+ // Relationships to an @internal entity can't exist here: codegen rejects a
422
+ // reference from an exposed entity to an @internal one.
423
+ let exposedEntities = userEntities->Array.filter(e => !e.internal)
424
+ for i in 0 to exposedEntities->Array.length - 1 {
425
+ let entityConfig = exposedEntities->Array.getUnsafe(i)
419
426
  let {tableName} = entityConfig.table
420
- let ownChainIdColumn = entityConfig.table->Table.getChainIdField->Option.map(Table.getPgDbFieldName)
427
+ let ownChainIdColumn =
428
+ entityConfig.table->Table.getChainIdField->Option.map(Table.getPgDbFieldName)
421
429
  let sharedChainIdColumn = mappedEntity =>
422
430
  switch (ownChainIdColumn, chainIdColumnOf(mappedEntity)) {
423
431
  | (Some(column), Some(_)) => Some(column)
@@ -285,7 +285,7 @@ async function createEntityRelationship(endpoint, auth, pgSchema, tableName, rel
285
285
  });
286
286
  }
287
287
 
288
- async function trackDatabase(endpoint, auth, pgSchema, userEntities, aggregateEntities, responseLimit, schema) {
288
+ function makeTableConfigs(userEntities) {
289
289
  let exposedInternalTableConfigs = [
290
290
  {
291
291
  tableName: InternalTable.RawEvents.table.tableName,
@@ -303,15 +303,19 @@ async function trackDatabase(endpoint, auth, pgSchema, userEntities, aggregateEn
303
303
  columnConfigs: {}
304
304
  }
305
305
  ];
306
- let userTableConfigs = userEntities.map(entity => ({
306
+ let userTableConfigs = userEntities.filter(entity => !entity.internal).map(entity => ({
307
307
  tableName: entity.table.tableName,
308
308
  description: entity.table.description,
309
309
  columnConfigs: makeColumnConfigs(entity.table)
310
310
  }));
311
- let tableConfigs = [
311
+ return [
312
312
  exposedInternalTableConfigs,
313
313
  userTableConfigs
314
314
  ].flat();
315
+ }
316
+
317
+ async function trackDatabase(endpoint, auth, pgSchema, userEntities, aggregateEntities, responseLimit, schema) {
318
+ let tableConfigs = makeTableConfigs(userEntities);
315
319
  let tableNames = tableConfigs.map(c => c.tableName);
316
320
  Logging.info("Tracking tables in Hasura");
317
321
  await clearHasuraMetadata(endpoint, auth);
@@ -322,8 +326,9 @@ async function trackDatabase(endpoint, auth, pgSchema, userEntities, aggregateEn
322
326
  await createSelectPermission(endpoint, auth, tableName, pgSchema, responseLimit, aggregateEntities);
323
327
  }
324
328
  let chainIdColumnOf = entityName => Stdlib_Option.map(Stdlib_Option.flatMap(userEntities.find(e => e.name === entityName), e => Table.getChainIdField(e.table)), Table.getPgDbFieldName);
325
- for (let i$1 = 0, i_finish$1 = userEntities.length; i$1 < i_finish$1; ++i$1) {
326
- let entityConfig = userEntities[i$1];
329
+ let exposedEntities = userEntities.filter(e => !e.internal);
330
+ for (let i$1 = 0, i_finish$1 = exposedEntities.length; i$1 < i_finish$1; ++i$1) {
331
+ let entityConfig = exposedEntities[i$1];
327
332
  let match = entityConfig.table;
328
333
  let tableName$1 = match.tableName;
329
334
  let ownChainIdColumn = Stdlib_Option.map(Table.getChainIdField(entityConfig.table), Table.getPgDbFieldName);
@@ -365,6 +370,7 @@ export {
365
370
  createSelectPermission,
366
371
  makeColumnMapping,
367
372
  createEntityRelationship,
373
+ makeTableConfigs,
368
374
  trackDatabase,
369
375
  }
370
376
  /* Rest Not a pure module */
@@ -148,7 +148,7 @@ let prepareRollbackDiff = async (
148
148
  let _ = await persistence.allEntities
149
149
  ->Array.filter(entityConfig => entityConfig.storage.postgres)
150
150
  ->Array.map(async entityConfig => {
151
- let (removals, restoredEntitiesResult) = await persistence.storage.getRollbackData(
151
+ let (removals, restoredEntities) = await persistence.storage.getRollbackData(
152
152
  ~entityConfig,
153
153
  ~rollbackTargetCheckpointId,
154
154
  )
@@ -166,11 +166,6 @@ let prepareRollbackDiff = async (
166
166
  )
167
167
  })
168
168
 
169
- let restoredEntities =
170
- restoredEntitiesResult
171
- ->S.parseOrThrow(entityConfig.table->Table.pgRowsSchema)
172
- ->(Utils.magic: array<unknown> => array<Internal.entity>)
173
-
174
169
  restoredEntities->Array.forEach((entity: Internal.entity) => {
175
170
  let scope = entity->takeRowScope(~entityConfig)
176
171
  setEntities->Utils.Dict.push(entityConfig.name, entity.id)
@@ -12,7 +12,6 @@ import * as InMemoryTable from "./InMemoryTable.res.mjs";
12
12
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
13
13
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
14
14
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
15
- import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
16
15
 
17
16
  function getInMemTable(state, entityConfig, scope) {
18
17
  let tmp;
@@ -125,8 +124,7 @@ async function prepareRollbackDiff(state, rollbackTargetCheckpointId, rollbackDi
125
124
  checkpointId: rollbackDiffCheckpointId
126
125
  });
127
126
  });
128
- let restoredEntities = S$RescriptSchema.parseOrThrow(match[1], Table.pgRowsSchema(entityConfig.table));
129
- restoredEntities.forEach(entity => {
127
+ match[1].forEach(entity => {
130
128
  let scope = takeRowScope(entity, entityConfig);
131
129
  Utils.Dict.push(setEntities, entityConfig.name, entity.id);
132
130
  InMemoryTable.Entity.set(getInMemTable(state, entityConfig, scope), committedCheckpointId, {
package/src/Internal.res CHANGED
@@ -149,8 +149,8 @@ type svmTransactionField =
149
149
  | @as("accountKeys") AccountKeys
150
150
  | @as("recentBlockhash") RecentBlockhash
151
151
  | @as("version") Version
152
- | @as("tokenBalances") TokenBalances
153
152
  | @as("allSignatures") AllSignatures
153
+ | @as("accountActivities") AccountActivities
154
154
 
155
155
  let allSvmTransactionFields: array<svmTransactionField> = [
156
156
  TransactionIndex,
@@ -163,13 +163,12 @@ let allSvmTransactionFields: array<svmTransactionField> = [
163
163
  AccountKeys,
164
164
  RecentBlockhash,
165
165
  Version,
166
- TokenBalances,
167
166
  AllSignatures,
167
+ AccountActivities,
168
168
  ]
169
- let svmTransactionFieldSchema = S.enum(allSvmTransactionFields)
170
169
 
171
- // All SVM block fields. `slot`/`time`/`hash` are always included; the rest are
172
- // selectable via `field_selection.block_fields` (see `allSvmBlockFields`).
170
+ // All SVM block fields. `slot` is always included (the item's key); the rest
171
+ // are selectable via handler `fields.block`.
173
172
  type svmBlockField =
174
173
  | @as("slot") Slot
175
174
  | @as("time") Time
@@ -178,9 +177,6 @@ type svmBlockField =
178
177
  | @as("parentSlot") ParentSlot
179
178
  | @as("parentHash") ParentHash
180
179
 
181
- let allSvmBlockFields: array<svmBlockField> = [Height, ParentSlot, ParentHash]
182
- let svmBlockFieldSchema = S.enum(allSvmBlockFields)
183
-
184
180
  // Static sets of field names whose source schemas must be wrapped with S.nullable.
185
181
  let evmNullableBlockFields = Utils.Set.fromArray(
186
182
  (
@@ -427,6 +423,9 @@ type indexingContract = {
427
423
  type fieldSelection = {
428
424
  blockFields: Utils.Set.t<string>,
429
425
  transactionFields: Utils.Set.t<string>,
426
+ instructionFields: Utils.Set.t<string>,
427
+ accountActivityFields: Utils.Set.t<string>,
428
+ logFields: Utils.Set.t<string>,
430
429
  // The sets precompiled to the store selections `ChainState` materialises with.
431
430
  blockMask: float,
432
431
  transactionMask: float,
@@ -438,11 +437,17 @@ type fieldSelection = {
438
437
  let makeFieldSelection = (
439
438
  ~blockFields: Utils.Set.t<string>,
440
439
  ~transactionFields: Utils.Set.t<string>,
440
+ ~instructionFields: Utils.Set.t<string>=Utils.Set.make(),
441
+ ~accountActivityFields: Utils.Set.t<string>=Utils.Set.make(),
442
+ ~logFields: Utils.Set.t<string>=Utils.Set.make(),
441
443
  ~blockMaskFn: Utils.Set.t<string> => float,
442
444
  ~transactionMaskFn: Utils.Set.t<string> => float,
443
445
  ): fieldSelection => {
444
446
  blockFields,
445
447
  transactionFields,
448
+ instructionFields,
449
+ accountActivityFields,
450
+ logFields,
446
451
  blockMask: blockMaskFn(blockFields),
447
452
  transactionMask: transactionMaskFn(transactionFields),
448
453
  }
@@ -459,6 +464,9 @@ let unionFields = (a, b) => a === b ? a : a->Utils.Set.union(b)
459
464
  let unionFieldSelection = (a: fieldSelection, b: fieldSelection): fieldSelection => {
460
465
  blockFields: unionFields(a.blockFields, b.blockFields),
461
466
  transactionFields: unionFields(a.transactionFields, b.transactionFields),
467
+ instructionFields: unionFields(a.instructionFields, b.instructionFields),
468
+ accountActivityFields: unionFields(a.accountActivityFields, b.accountActivityFields),
469
+ logFields: unionFields(a.logFields, b.logFields),
462
470
  blockMask: FieldMask.orMask(a.blockMask, b.blockMask),
463
471
  transactionMask: FieldMask.orMask(a.transactionMask, b.transactionMask),
464
472
  }
@@ -569,7 +577,6 @@ type svmInstructionEventConfig = {
569
577
  `dN` selector at query time and the dispatch-key precomputation in the
570
578
  router. */
571
579
  discriminatorByteLen: int,
572
- includeLogs: bool,
573
580
  /** Disjunctive normal form: outer array is OR of AND-groups, inner array is
574
581
  AND across positions. Empty outer array means "no account filter". */
575
582
  accountFilters: array<svmAccountFilterGroup>,
@@ -747,9 +754,8 @@ let getItemChainId = item =>
747
754
  | Block({onBlockRegistration: {chainId}}) => chainId
748
755
  }
749
756
 
750
- // The `fields` option of an EVM `onEvent`/`contractRegister` registration:
751
- // the block and transaction fields the handler reads. Replaces the config
752
- // `field_selection` for this registration.
757
+ // EVM `fields` bag. Parsed from the JS object as `unknown` at registration;
758
+ // this record exists so ReScript tests can construct a typed EVM selection.
753
759
  type evmFieldsSelection = {
754
760
  block?: array<string>,
755
761
  transaction?: array<string>,
@@ -758,7 +764,7 @@ type evmFieldsSelection = {
758
764
  type eventOptions<'where> = {
759
765
  wildcard?: bool,
760
766
  where?: 'where,
761
- fields?: evmFieldsSelection,
767
+ fields?: unknown,
762
768
  }
763
769
 
764
770
  type fuelSupplyParams = {
@@ -782,12 +788,23 @@ let fuelTransferParamsSchema = S.schema(s => {
782
788
 
783
789
  type entity = private {id: string}
784
790
 
791
+ // A data skipping index emitted into the history table DDL as
792
+ // `INDEX <name> <expr> TYPE <type> GRANULARITY <granularity>`.
793
+ type clickhouseSkippingIndex = {
794
+ name: string,
795
+ expr: string,
796
+ @as("type")
797
+ type_: string,
798
+ granularity?: int,
799
+ }
800
+
785
801
  // Raw ClickHouse expressions/field names from the entity's
786
802
  // @storage(clickhouse: {...}) directive, applied to the history table DDL.
787
803
  type clickhouseTableOptions = {
788
804
  partitionBy?: string,
789
805
  orderBy?: array<string>,
790
806
  ttl?: string,
807
+ skippingIndexes?: array<clickhouseSkippingIndex>,
791
808
  }
792
809
 
793
810
  // Per-entity storage resolved at parse time against the global storage
@@ -808,6 +825,9 @@ type genericEntityConfig<'entity> = {
808
825
  // entity's `@crossChain`. When false the table carries a chain-id column in
809
826
  // its primary key and every row belongs to exactly one chain.
810
827
  crossChain: bool,
828
+ // `@internal` on the entity: stored and usable in handlers as normal, but
829
+ // never exposed through the GraphQL API (no Hasura tracking).
830
+ internal: bool,
811
831
  }
812
832
  type entityConfig = genericEntityConfig<entity>
813
833
  external fromGenericEntityConfig: genericEntityConfig<'entity> => entityConfig = "%identity"