envio 3.1.0-rc.2 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +12 -0
- package/package.json +6 -6
- package/src/GlobalState.res +1 -0
- package/src/GlobalState.res.mjs +1 -1
- package/src/InMemoryStore.res +20 -3
- package/src/InMemoryStore.res.mjs +21 -5
- package/src/LoadLayer.res +2 -2
- package/src/LoadLayer.res.mjs +1 -2
- package/src/Main.res +17 -1
- package/src/Main.res.mjs +11 -2
- package/src/Persistence.res +3 -0
- package/src/RollbackCommit.res +32 -0
- package/src/RollbackCommit.res.mjs +35 -0
- package/src/sources/EvmChain.res +9 -16
- package/src/sources/EvmChain.res.mjs +7 -15
- package/src/sources/HyperSyncClient.res +8 -2
- package/src/sources/HyperSyncSource.res +17 -14
- package/src/sources/HyperSyncSource.res.mjs +21 -21
- package/src/sources/RpcSource.res +6 -4
- package/src/sources/RpcSource.res.mjs +47 -42
package/index.d.ts
CHANGED
|
@@ -1201,6 +1201,18 @@ export type IndexerFromConfig<Config extends IndexerConfigTypes = GlobalConfig>
|
|
|
1201
1201
|
readonly name: string;
|
|
1202
1202
|
/** The indexer description from config.yaml. */
|
|
1203
1203
|
readonly description: string | undefined;
|
|
1204
|
+
/**
|
|
1205
|
+
* Internal, unstable API that will be removed without notice. Registers a
|
|
1206
|
+
* callback fired once per chain affected by a reorg rollback, after the
|
|
1207
|
+
* rollback is durably written to the database. A throwing callback crashes
|
|
1208
|
+
* the indexer through the same path as a failed write.
|
|
1209
|
+
*/
|
|
1210
|
+
readonly "~internalAndWillBeRemovedSoon_onRollbackCommit": (
|
|
1211
|
+
callback: (args: {
|
|
1212
|
+
readonly chainId: number;
|
|
1213
|
+
readonly rollbackToBlock: number;
|
|
1214
|
+
}) => Promise<void>,
|
|
1215
|
+
) => void;
|
|
1204
1216
|
} & SingleEcosystemChains<Config>
|
|
1205
1217
|
>;
|
|
1206
1218
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
|
|
6
6
|
"bin": "./bin.mjs",
|
|
@@ -70,10 +70,10 @@
|
|
|
70
70
|
"tsx": "4.21.0"
|
|
71
71
|
},
|
|
72
72
|
"optionalDependencies": {
|
|
73
|
-
"envio-linux-x64": "3.1.
|
|
74
|
-
"envio-linux-x64-musl": "3.1.
|
|
75
|
-
"envio-linux-arm64": "3.1.
|
|
76
|
-
"envio-darwin-x64": "3.1.
|
|
77
|
-
"envio-darwin-arm64": "3.1.
|
|
73
|
+
"envio-linux-x64": "3.1.1",
|
|
74
|
+
"envio-linux-x64-musl": "3.1.1",
|
|
75
|
+
"envio-linux-arm64": "3.1.1",
|
|
76
|
+
"envio-darwin-x64": "3.1.1",
|
|
77
|
+
"envio-darwin-arm64": "3.1.1"
|
|
78
78
|
}
|
|
79
79
|
}
|
package/src/GlobalState.res
CHANGED
|
@@ -1159,6 +1159,7 @@ let injectedTaskReducer = (
|
|
|
1159
1159
|
~persistence=state.ctx.persistence,
|
|
1160
1160
|
~rollbackTargetCheckpointId,
|
|
1161
1161
|
~rollbackDiffCheckpointId=state.ctx.inMemoryStore.committedCheckpointId->BigInt.add(1n),
|
|
1162
|
+
~progressBlockNumberByChainId=newProgressBlockNumberPerChain,
|
|
1162
1163
|
)
|
|
1163
1164
|
|
|
1164
1165
|
let chainManager = {
|
package/src/GlobalState.res.mjs
CHANGED
|
@@ -1151,7 +1151,7 @@ function injectedTaskReducer(waitForNewBlock, executeQuery, getLastKnownValidBlo
|
|
|
1151
1151
|
};
|
|
1152
1152
|
});
|
|
1153
1153
|
await InMemoryStore.flush(state.ctx.inMemoryStore);
|
|
1154
|
-
let diff$1 = await InMemoryStore.prepareRollbackDiff(state.ctx.inMemoryStore, state.ctx.persistence, rollbackTargetCheckpointId, state.ctx.inMemoryStore.committedCheckpointId + 1n);
|
|
1154
|
+
let diff$1 = await InMemoryStore.prepareRollbackDiff(state.ctx.inMemoryStore, state.ctx.persistence, rollbackTargetCheckpointId, state.ctx.inMemoryStore.committedCheckpointId + 1n, newProgressBlockNumberPerChain);
|
|
1155
1155
|
let init = state.chainManager;
|
|
1156
1156
|
let chainManager_isInReorgThreshold = init.isInReorgThreshold;
|
|
1157
1157
|
let chainManager_isRealtime = init.isRealtime;
|
package/src/InMemoryStore.res
CHANGED
|
@@ -124,10 +124,19 @@ let getEffectInMemTable = (inMemoryStore: t, ~effect: Internal.effect) => {
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
let
|
|
127
|
+
let hasEffectOutput = (inMemTable: effectCacheInMemTable, key) =>
|
|
128
128
|
switch inMemTable.dict->Utils.Dict.dangerouslyGetNonOption(key) {
|
|
129
|
-
| Some(Set(
|
|
130
|
-
| Some(Delete(_)) | None =>
|
|
129
|
+
| Some(Set(_)) => true
|
|
130
|
+
| Some(Delete(_)) | None => false
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Returns the raw output. The output is itself an option for effects with an
|
|
134
|
+
// optional output, so it must never be wrapped in another option here: Some(None)
|
|
135
|
+
// is encoded as the nested-option sentinel and would leak to the handler.
|
|
136
|
+
let getEffectOutputUnsafe = (inMemTable: effectCacheInMemTable, key): Internal.effectOutput =>
|
|
137
|
+
switch inMemTable.dict->Utils.Dict.dangerouslyGetNonOption(key) {
|
|
138
|
+
| Some(Set({entity: output})) => output
|
|
139
|
+
| Some(Delete(_)) | None => %raw(`undefined`)
|
|
131
140
|
}
|
|
132
141
|
|
|
133
142
|
// Records a handler output. Persisted on the next write only when shouldCache;
|
|
@@ -359,6 +368,12 @@ let runOneWrite = async (inMemoryStore: t, ~persistence: Persistence.t, ~config)
|
|
|
359
368
|
)
|
|
360
369
|
|
|
361
370
|
inMemoryStore.committedCheckpointId = upToCheckpointId
|
|
371
|
+
|
|
372
|
+
switch rollback {
|
|
373
|
+
| Some({progressBlockNumberByChainId}) if RollbackCommit.callbacks->Utils.Array.notEmpty =>
|
|
374
|
+
await RollbackCommit.fire(~progressBlockNumberByChainId)
|
|
375
|
+
| _ => ()
|
|
376
|
+
}
|
|
362
377
|
}
|
|
363
378
|
}
|
|
364
379
|
|
|
@@ -494,12 +509,14 @@ let prepareRollbackDiff = async (
|
|
|
494
509
|
~persistence: Persistence.t,
|
|
495
510
|
~rollbackTargetCheckpointId,
|
|
496
511
|
~rollbackDiffCheckpointId,
|
|
512
|
+
~progressBlockNumberByChainId,
|
|
497
513
|
) => {
|
|
498
514
|
inMemoryStore.entities = EntityTables.make(inMemoryStore.allEntities)
|
|
499
515
|
inMemoryStore.effects = Dict.make()
|
|
500
516
|
inMemoryStore.rollback = Some({
|
|
501
517
|
targetCheckpointId: rollbackTargetCheckpointId,
|
|
502
518
|
diffCheckpointId: rollbackDiffCheckpointId,
|
|
519
|
+
progressBlockNumberByChainId,
|
|
503
520
|
})
|
|
504
521
|
|
|
505
522
|
let deletedEntities = Dict.make()
|
|
@@ -11,6 +11,7 @@ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
|
11
11
|
import * as ErrorHandling from "./ErrorHandling.res.mjs";
|
|
12
12
|
import * as InMemoryTable from "./InMemoryTable.res.mjs";
|
|
13
13
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
14
|
+
import * as RollbackCommit from "./RollbackCommit.res.mjs";
|
|
14
15
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
15
16
|
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
16
17
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
@@ -89,11 +90,21 @@ function getEffectInMemTable(inMemoryStore, effect) {
|
|
|
89
90
|
return table$1;
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
function
|
|
93
|
+
function hasEffectOutput(inMemTable, key) {
|
|
94
|
+
let match = inMemTable.dict[key];
|
|
95
|
+
if (match !== undefined) {
|
|
96
|
+
return match.type === "SET";
|
|
97
|
+
} else {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function getEffectOutputUnsafe(inMemTable, key) {
|
|
93
103
|
let match = inMemTable.dict[key];
|
|
94
104
|
if (match !== undefined && match.type === "SET") {
|
|
95
|
-
return
|
|
105
|
+
return match.entity;
|
|
96
106
|
}
|
|
107
|
+
return undefined;
|
|
97
108
|
}
|
|
98
109
|
|
|
99
110
|
function setEffectOutput(inMemTable, checkpointId, cacheKey, output, shouldCache) {
|
|
@@ -300,6 +311,9 @@ async function runOneWrite(inMemoryStore, persistence, config) {
|
|
|
300
311
|
let updatedEffectsCache = snapshotEffects(inMemoryStore, cache);
|
|
301
312
|
await persistence.storage.writeBatch(batch, rollback, batch.isInReorgThreshold, config, persistence.allEntities, updatedEffectsCache, updatedEntities, chainMetaData);
|
|
302
313
|
inMemoryStore.committedCheckpointId = upToCheckpointId;
|
|
314
|
+
if (rollback !== undefined && Utils.$$Array.notEmpty(RollbackCommit.callbacks)) {
|
|
315
|
+
return await RollbackCommit.fire(rollback.progressBlockNumberByChainId);
|
|
316
|
+
}
|
|
303
317
|
}
|
|
304
318
|
|
|
305
319
|
function hasPendingWrite(inMemoryStore) {
|
|
@@ -401,12 +415,13 @@ async function flush(inMemoryStore) {
|
|
|
401
415
|
}
|
|
402
416
|
}
|
|
403
417
|
|
|
404
|
-
async function prepareRollbackDiff(inMemoryStore, persistence, rollbackTargetCheckpointId, rollbackDiffCheckpointId) {
|
|
418
|
+
async function prepareRollbackDiff(inMemoryStore, persistence, rollbackTargetCheckpointId, rollbackDiffCheckpointId, progressBlockNumberByChainId) {
|
|
405
419
|
inMemoryStore.entities = make(inMemoryStore.allEntities);
|
|
406
420
|
inMemoryStore.effects = {};
|
|
407
421
|
inMemoryStore.rollback = {
|
|
408
422
|
targetCheckpointId: rollbackTargetCheckpointId,
|
|
409
|
-
diffCheckpointId: rollbackDiffCheckpointId
|
|
423
|
+
diffCheckpointId: rollbackDiffCheckpointId,
|
|
424
|
+
progressBlockNumberByChainId: progressBlockNumberByChainId
|
|
410
425
|
};
|
|
411
426
|
let deletedEntities = {};
|
|
412
427
|
let setEntities = {};
|
|
@@ -482,7 +497,8 @@ export {
|
|
|
482
497
|
make$1 as make,
|
|
483
498
|
keepLatestChangesLimit,
|
|
484
499
|
getEffectInMemTable,
|
|
485
|
-
|
|
500
|
+
hasEffectOutput,
|
|
501
|
+
getEffectOutputUnsafe,
|
|
486
502
|
setEffectOutput,
|
|
487
503
|
initEffectOutputFromDb,
|
|
488
504
|
dropCommittedEffects,
|
package/src/LoadLayer.res
CHANGED
|
@@ -336,8 +336,8 @@ let loadEffect = (
|
|
|
336
336
|
~load,
|
|
337
337
|
~shouldGroup,
|
|
338
338
|
~hasher=args => args.cacheKey,
|
|
339
|
-
~getUnsafeInMemory=hash => inMemTable->InMemoryStore.
|
|
340
|
-
~hasInMemory=hash => inMemTable->InMemoryStore.
|
|
339
|
+
~getUnsafeInMemory=hash => inMemTable->InMemoryStore.getEffectOutputUnsafe(hash),
|
|
340
|
+
~hasInMemory=hash => inMemTable->InMemoryStore.hasEffectOutput(hash),
|
|
341
341
|
~input=effectArgs,
|
|
342
342
|
)
|
|
343
343
|
}
|
package/src/LoadLayer.res.mjs
CHANGED
|
@@ -12,7 +12,6 @@ import * as TableIndices from "./TableIndices.res.mjs";
|
|
|
12
12
|
import * as ErrorHandling from "./ErrorHandling.res.mjs";
|
|
13
13
|
import * as InMemoryStore from "./InMemoryStore.res.mjs";
|
|
14
14
|
import * as InMemoryTable from "./InMemoryTable.res.mjs";
|
|
15
|
-
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
16
15
|
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
17
16
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
18
17
|
|
|
@@ -187,7 +186,7 @@ function loadEffect(loadManager, persistence, effect, effectArgs, inMemoryStore,
|
|
|
187
186
|
return await executeWithRateLimit(effect, argsToCall, inMemTable, onError, false);
|
|
188
187
|
}
|
|
189
188
|
};
|
|
190
|
-
return LoadManager.call(loadManager, effectArgs, key, load, args => args.cacheKey, shouldGroup, hash =>
|
|
189
|
+
return LoadManager.call(loadManager, effectArgs, key, load, args => args.cacheKey, shouldGroup, hash => InMemoryStore.hasEffectOutput(inMemTable, hash), hash => InMemoryStore.getEffectOutputUnsafe(inMemTable, hash));
|
|
191
190
|
}
|
|
192
191
|
|
|
193
192
|
function loadByField(loadManager, persistence, operator, entityConfig, inMemoryStore, fieldName, fieldValueSchema, shouldGroup, item, fieldValue) {
|
package/src/Main.res
CHANGED
|
@@ -312,6 +312,13 @@ let getGlobalIndexer = (): 'indexer => {
|
|
|
312
312
|
)
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
+
let onRollbackCommitFn = (callback: 'a) => {
|
|
316
|
+
HandlerRegister.throwIfFinishedRegistration(
|
|
317
|
+
~methodName="~internalAndWillBeRemovedSoon_onRollbackCommit",
|
|
318
|
+
)
|
|
319
|
+
let _ = RollbackCommit.register(callback->(Utils.magic: 'a => RollbackCommit.callback))
|
|
320
|
+
}
|
|
321
|
+
|
|
315
322
|
// Two-stage parse: first the ecosystem-specific outer schema unwraps the
|
|
316
323
|
// wrapper (`block.number` / `block.height` / `slot`) and surfaces the
|
|
317
324
|
// inner chunk as raw `unknown`; then the shared `blockRangeSchema`
|
|
@@ -453,8 +460,16 @@ let getGlobalIndexer = (): 'indexer => {
|
|
|
453
460
|
"onEvent",
|
|
454
461
|
"contractRegister",
|
|
455
462
|
"onBlock",
|
|
463
|
+
"~internalAndWillBeRemovedSoon_onRollbackCommit",
|
|
464
|
+
]
|
|
465
|
+
| Svm => [
|
|
466
|
+
"name",
|
|
467
|
+
"description",
|
|
468
|
+
"chainIds",
|
|
469
|
+
"chains",
|
|
470
|
+
"onSlot",
|
|
471
|
+
"~internalAndWillBeRemovedSoon_onRollbackCommit",
|
|
456
472
|
]
|
|
457
|
-
| Svm => ["name", "description", "chainIds", "chains", "onSlot"]
|
|
458
473
|
}
|
|
459
474
|
keysMemo := Some(keys)
|
|
460
475
|
keys
|
|
@@ -477,6 +492,7 @@ let getGlobalIndexer = (): 'indexer => {
|
|
|
477
492
|
| "onEvent" => onEventFn->Utils.magic
|
|
478
493
|
| "contractRegister" => contractRegisterFn->Utils.magic
|
|
479
494
|
| "onBlock" | "onSlot" => onBlockFn->Utils.magic
|
|
495
|
+
| "~internalAndWillBeRemovedSoon_onRollbackCommit" => onRollbackCommitFn->Utils.magic
|
|
480
496
|
| _ =>
|
|
481
497
|
JsError.throwWithMessage(
|
|
482
498
|
`Field \`${prop}\` does not exist on \`indexer\`. Available fields: ${getKeys()->Array.join(
|
package/src/Main.res.mjs
CHANGED
|
@@ -25,6 +25,7 @@ import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
|
|
|
25
25
|
import * as SourceManager from "./sources/SourceManager.res.mjs";
|
|
26
26
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
27
27
|
import * as Helpers from "yargs/helpers";
|
|
28
|
+
import * as RollbackCommit from "./RollbackCommit.res.mjs";
|
|
28
29
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
29
30
|
import * as HandlerRegister from "./HandlerRegister.res.mjs";
|
|
30
31
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
@@ -255,6 +256,10 @@ function getGlobalIndexer() {
|
|
|
255
256
|
let match = parseIdentityConfig(identityConfig);
|
|
256
257
|
HandlerRegister.setContractRegister(match[0], match[1], handler, match[2], undefined);
|
|
257
258
|
};
|
|
259
|
+
let onRollbackCommitFn = callback => {
|
|
260
|
+
HandlerRegister.throwIfFinishedRegistration("~internalAndWillBeRemovedSoon_onRollbackCommit");
|
|
261
|
+
RollbackCommit.register(callback);
|
|
262
|
+
};
|
|
258
263
|
let extractRange = (filter, name, ecosystem) => {
|
|
259
264
|
try {
|
|
260
265
|
let inner = S$RescriptSchema.parseOrThrow(filter, ecosystem.onBlockFilterSchema);
|
|
@@ -341,7 +346,8 @@ function getGlobalIndexer() {
|
|
|
341
346
|
"description",
|
|
342
347
|
"chainIds",
|
|
343
348
|
"chains",
|
|
344
|
-
"onSlot"
|
|
349
|
+
"onSlot",
|
|
350
|
+
"~internalAndWillBeRemovedSoon_onRollbackCommit"
|
|
345
351
|
];
|
|
346
352
|
break;
|
|
347
353
|
}
|
|
@@ -353,7 +359,8 @@ function getGlobalIndexer() {
|
|
|
353
359
|
"chains",
|
|
354
360
|
"onEvent",
|
|
355
361
|
"contractRegister",
|
|
356
|
-
"onBlock"
|
|
362
|
+
"onBlock",
|
|
363
|
+
"~internalAndWillBeRemovedSoon_onRollbackCommit"
|
|
357
364
|
];
|
|
358
365
|
}
|
|
359
366
|
keysMemo.contents = keys;
|
|
@@ -376,6 +383,8 @@ function getGlobalIndexer() {
|
|
|
376
383
|
case "onBlock" :
|
|
377
384
|
case "onSlot" :
|
|
378
385
|
return onBlockFn;
|
|
386
|
+
case "~internalAndWillBeRemovedSoon_onRollbackCommit" :
|
|
387
|
+
return onRollbackCommitFn;
|
|
379
388
|
default:
|
|
380
389
|
return Stdlib_JsError.throwWithMessage(`Field \`` + prop + `\` does not exist on \`indexer\`. Available fields: ` + getKeys().join(", ") + `.`);
|
|
381
390
|
}
|
package/src/Persistence.res
CHANGED
|
@@ -51,6 +51,9 @@ type updatedEffectCache = {
|
|
|
51
51
|
type rollback = {
|
|
52
52
|
targetCheckpointId: Internal.checkpointId,
|
|
53
53
|
diffCheckpointId: Internal.checkpointId,
|
|
54
|
+
// Last valid block per chain affected by the rollback. Read by
|
|
55
|
+
// `RollbackCommit.fire` once the diff is durably written.
|
|
56
|
+
progressBlockNumberByChainId: dict<int>,
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
type updatedEntity = {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Temporary, internal-only support for the unstable
|
|
2
|
+
// `indexer.~internalAndWillBeRemovedSoon_onRollbackCommit` API. The whole
|
|
3
|
+
// feature lives here plus two call sites: registration in `Main.res` and the
|
|
4
|
+
// fire on a successful rollback write in `InMemoryStore.res`. Delete those
|
|
5
|
+
// together with this module.
|
|
6
|
+
type args = {chainId: int, rollbackToBlock: int}
|
|
7
|
+
type callback = args => promise<unit>
|
|
8
|
+
|
|
9
|
+
let callbacks: array<callback> = []
|
|
10
|
+
|
|
11
|
+
let register = (callback: callback) => {
|
|
12
|
+
callbacks->Array.push(callback)
|
|
13
|
+
() =>
|
|
14
|
+
switch callbacks->Array.indexOf(callback) {
|
|
15
|
+
| -1 => ()
|
|
16
|
+
| index => callbacks->Array.splice(~start=index, ~remove=1, ~insert=[])
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Fired after a rollback diff is durably written, once per affected chain.
|
|
21
|
+
// `progressBlockNumberByChainId` is the last valid block per chain, taken from
|
|
22
|
+
// the in-memory store's rollback object. A throwing callback bubbles to the
|
|
23
|
+
// write loop's onError, crashing the indexer like a failed write.
|
|
24
|
+
let fire = async (~progressBlockNumberByChainId: dict<int>) => {
|
|
25
|
+
let _ = await progressBlockNumberByChainId
|
|
26
|
+
->Dict.toArray
|
|
27
|
+
->Array.flatMap(((chainIdKey, rollbackToBlock)) => {
|
|
28
|
+
let args = {chainId: chainIdKey->Int.fromString->Option.getUnsafe, rollbackToBlock}
|
|
29
|
+
callbacks->Array.map(callback => callback(args))
|
|
30
|
+
})
|
|
31
|
+
->Promise.all
|
|
32
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
|
|
4
|
+
|
|
5
|
+
let callbacks = [];
|
|
6
|
+
|
|
7
|
+
function register(callback) {
|
|
8
|
+
callbacks.push(callback);
|
|
9
|
+
return () => {
|
|
10
|
+
let index = callbacks.indexOf(callback);
|
|
11
|
+
if (index !== -1) {
|
|
12
|
+
callbacks.splice(index, 1);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function fire(progressBlockNumberByChainId) {
|
|
19
|
+
await Promise.all(Object.entries(progressBlockNumberByChainId).flatMap(param => {
|
|
20
|
+
let args_chainId = Stdlib_Int.fromString(param[0], undefined);
|
|
21
|
+
let args_rollbackToBlock = param[1];
|
|
22
|
+
let args = {
|
|
23
|
+
chainId: args_chainId,
|
|
24
|
+
rollbackToBlock: args_rollbackToBlock
|
|
25
|
+
};
|
|
26
|
+
return callbacks.map(callback => callback(args));
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export {
|
|
31
|
+
callbacks,
|
|
32
|
+
register,
|
|
33
|
+
fire,
|
|
34
|
+
}
|
|
35
|
+
/* No side effect */
|
package/src/sources/EvmChain.res
CHANGED
|
@@ -41,25 +41,18 @@ let getSyncConfig = (
|
|
|
41
41
|
let collectEventParams = (contracts: array<Internal.evmContractConfig>): array<
|
|
42
42
|
HyperSyncClient.Decoder.eventParamsInput,
|
|
43
43
|
> => {
|
|
44
|
-
let seen = Dict.make()
|
|
45
44
|
let result = []
|
|
46
45
|
contracts->Array.forEach(contract => {
|
|
47
46
|
contract.events->Array.forEach(event => {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
eventName: event.name,
|
|
58
|
-
params: event.paramsMetadata,
|
|
59
|
-
})
|
|
60
|
-
->ignore
|
|
61
|
-
}
|
|
62
|
-
}
|
|
47
|
+
result
|
|
48
|
+
->Array.push({
|
|
49
|
+
HyperSyncClient.Decoder.sighash: event.sighash,
|
|
50
|
+
topicCount: event.topicCount,
|
|
51
|
+
eventName: event.name,
|
|
52
|
+
contractName: contract.name,
|
|
53
|
+
params: event.paramsMetadata,
|
|
54
|
+
})
|
|
55
|
+
->ignore
|
|
63
56
|
})
|
|
64
57
|
})
|
|
65
58
|
result
|
|
@@ -22,24 +22,16 @@ function getSyncConfig(param) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
function collectEventParams(contracts) {
|
|
25
|
-
let seen = {};
|
|
26
25
|
let result = [];
|
|
27
26
|
contracts.forEach(contract => {
|
|
28
27
|
contract.events.forEach(event => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
sighash: event.sighash,
|
|
37
|
-
topicCount: event.topicCount,
|
|
38
|
-
eventName: event.name,
|
|
39
|
-
params: event.paramsMetadata
|
|
40
|
-
});
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
28
|
+
result.push({
|
|
29
|
+
sighash: event.sighash,
|
|
30
|
+
topicCount: event.topicCount,
|
|
31
|
+
eventName: event.name,
|
|
32
|
+
contractName: contract.name,
|
|
33
|
+
params: event.paramsMetadata
|
|
34
|
+
});
|
|
43
35
|
});
|
|
44
36
|
});
|
|
45
37
|
return result;
|
|
@@ -322,11 +322,17 @@ module Decoder = {
|
|
|
322
322
|
sighash: string,
|
|
323
323
|
topicCount: int,
|
|
324
324
|
eventName: string,
|
|
325
|
+
contractName: string,
|
|
325
326
|
params: array<Internal.paramMeta>,
|
|
326
327
|
}
|
|
327
328
|
|
|
329
|
+
// Decoded params keyed by contract name. Contracts that emit the same-signature
|
|
330
|
+
// event share one decode but get their own param names, so the caller picks the
|
|
331
|
+
// entry for the contract its router resolved the log to.
|
|
328
332
|
type tWithParams = {
|
|
329
|
-
decodeLogs: array<ResponseTypes.event> => promise<
|
|
333
|
+
decodeLogs: array<ResponseTypes.event> => promise<
|
|
334
|
+
array<Nullable.t<dict<Internal.eventParams>>>,
|
|
335
|
+
>,
|
|
330
336
|
}
|
|
331
337
|
|
|
332
338
|
@send
|
|
@@ -348,7 +354,7 @@ module EventItems = {
|
|
|
348
354
|
topicCount: int,
|
|
349
355
|
block: ResponseTypes.block,
|
|
350
356
|
transaction: ResponseTypes.transaction,
|
|
351
|
-
params: Nullable.t<Internal.eventParams
|
|
357
|
+
params: Nullable.t<dict<Internal.eventParams>>,
|
|
352
358
|
}
|
|
353
359
|
|
|
354
360
|
type response = {
|
|
@@ -351,20 +351,23 @@ Learn more or get a free API token at: https://envio.dev/app/api-tokens`)
|
|
|
351
351
|
~blockNumber=item.block.number->Belt.Option.getUnsafe,
|
|
352
352
|
)
|
|
353
353
|
|
|
354
|
-
switch
|
|
355
|
-
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
->
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
~eventConfig
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
354
|
+
switch maybeEventConfig {
|
|
355
|
+
| None => () //ignore events that aren't registered
|
|
356
|
+
| Some(eventConfig) =>
|
|
357
|
+
switch item.params
|
|
358
|
+
->Nullable.toOption
|
|
359
|
+
->Option.flatMap(Dict.get(_, eventConfig.contractName)) {
|
|
360
|
+
| Some(params) =>
|
|
361
|
+
parsedQueueItems->Array.push(makeEventBatchQueueItem(item, ~params, ~eventConfig))->ignore
|
|
362
|
+
| None =>
|
|
363
|
+
handleDecodeFailure(
|
|
364
|
+
~eventConfig,
|
|
365
|
+
~logIndex=item.logIndex,
|
|
366
|
+
~blockNumber=item.block.number->Belt.Option.getUnsafe,
|
|
367
|
+
~chainId,
|
|
368
|
+
~exn=UndefinedValue,
|
|
369
|
+
)
|
|
370
|
+
}
|
|
368
371
|
}
|
|
369
372
|
})
|
|
370
373
|
|
|
@@ -14,6 +14,7 @@ import * as ErrorHandling from "../ErrorHandling.res.mjs";
|
|
|
14
14
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
15
15
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
16
16
|
import * as HyperSyncClient from "./HyperSyncClient.res.mjs";
|
|
17
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
17
18
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
18
19
|
import * as HyperSyncHeightStream from "./HyperSyncHeightStream.res.mjs";
|
|
19
20
|
|
|
@@ -215,32 +216,31 @@ Learn more or get a free API token at: https://envio.dev/app/api-tokens`);
|
|
|
215
216
|
let parsedQueueItems = [];
|
|
216
217
|
Belt_Array.forEach(pageUnsafe.items, item => {
|
|
217
218
|
let maybeEventConfig = EventRouter.get(eventRouter, EventRouter.getEvmEventId(item.topic0, item.topicCount), item.srcAddress, item.block.number, indexingAddresses);
|
|
218
|
-
let match = item.params;
|
|
219
219
|
if (maybeEventConfig === undefined) {
|
|
220
220
|
return;
|
|
221
221
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
parsedQueueItems.push(makeEventBatchQueueItem(item, match, maybeEventConfig));
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
let logIndex = item.logIndex;
|
|
229
|
-
let blockNumber = item.block.number;
|
|
230
|
-
let exn = {
|
|
231
|
-
RE_EXN_ID: UndefinedValue
|
|
232
|
-
};
|
|
233
|
-
if (maybeEventConfig.isWildcard) {
|
|
222
|
+
let params = Stdlib_Option.flatMap(Primitive_option.fromNullable(item.params), __x => __x[maybeEventConfig.contractName]);
|
|
223
|
+
if (params !== undefined) {
|
|
224
|
+
parsedQueueItems.push(makeEventBatchQueueItem(item, Primitive_option.valFromOption(params), maybeEventConfig));
|
|
234
225
|
return;
|
|
226
|
+
} else {
|
|
227
|
+
let logIndex = item.logIndex;
|
|
228
|
+
let blockNumber = item.block.number;
|
|
229
|
+
let exn = {
|
|
230
|
+
RE_EXN_ID: UndefinedValue
|
|
231
|
+
};
|
|
232
|
+
if (maybeEventConfig.isWildcard) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
let msg = `Event ` + maybeEventConfig.name + ` was unexpectedly parsed as undefined`;
|
|
236
|
+
let logger$1 = Logging.createChildFrom(logger, {
|
|
237
|
+
chainId: chain,
|
|
238
|
+
blockNumber: blockNumber,
|
|
239
|
+
logIndex: logIndex,
|
|
240
|
+
decoder: "hypersync-client"
|
|
241
|
+
});
|
|
242
|
+
return ErrorHandling.mkLogAndRaise(logger$1, msg, exn);
|
|
235
243
|
}
|
|
236
|
-
let msg = `Event ` + maybeEventConfig.name + ` was unexpectedly parsed as undefined`;
|
|
237
|
-
let logger$1 = Logging.createChildFrom(logger, {
|
|
238
|
-
chainId: chain,
|
|
239
|
-
blockNumber: blockNumber,
|
|
240
|
-
logIndex: logIndex,
|
|
241
|
-
decoder: "hypersync-client"
|
|
242
|
-
});
|
|
243
|
-
ErrorHandling.mkLogAndRaise(logger$1, msg, exn);
|
|
244
244
|
});
|
|
245
245
|
let parsingTimeElapsed = Hrtime.toSecondsFloat(Hrtime.timeSince(parsingTimeRef));
|
|
246
246
|
let blockHashes = [];
|
|
@@ -1115,7 +1115,7 @@ let make = (
|
|
|
1115
1115
|
->Array.zip(parsedEvents)
|
|
1116
1116
|
->Array.filterMap(((
|
|
1117
1117
|
log: Rpc.GetLogs.log,
|
|
1118
|
-
maybeDecodedEvent: Nullable.t<Internal.eventParams
|
|
1118
|
+
maybeDecodedEvent: Nullable.t<dict<Internal.eventParams>>,
|
|
1119
1119
|
)) => {
|
|
1120
1120
|
let topic0 = log.topics[0]->Option.getOr("0x0")
|
|
1121
1121
|
let routedAddress = if lowercaseAddresses {
|
|
@@ -1132,8 +1132,10 @@ let make = (
|
|
|
1132
1132
|
) {
|
|
1133
1133
|
| None => None
|
|
1134
1134
|
| Some(eventConfig) =>
|
|
1135
|
-
switch maybeDecodedEvent
|
|
1136
|
-
|
|
1135
|
+
switch maybeDecodedEvent
|
|
1136
|
+
->Nullable.toOption
|
|
1137
|
+
->Option.flatMap(Dict.get(_, eventConfig.contractName)) {
|
|
1138
|
+
| Some(decoded) =>
|
|
1137
1139
|
Some(
|
|
1138
1140
|
(
|
|
1139
1141
|
async () => {
|
|
@@ -1176,7 +1178,7 @@ let make = (
|
|
|
1176
1178
|
}
|
|
1177
1179
|
)(),
|
|
1178
1180
|
)
|
|
1179
|
-
|
|
|
1181
|
+
| None => None
|
|
1180
1182
|
}
|
|
1181
1183
|
}
|
|
1182
1184
|
})
|
|
@@ -1064,54 +1064,59 @@ function make(param) {
|
|
|
1064
1064
|
};
|
|
1065
1065
|
}
|
|
1066
1066
|
let parsedQueueItems = await Promise.all(Stdlib_Array.filterMap(Stdlib_Array.zip(logs, parsedEvents), param => {
|
|
1067
|
-
let maybeDecodedEvent = param[1];
|
|
1068
1067
|
let log = param[0];
|
|
1069
1068
|
let topic0 = Stdlib_Option.getOr(log.topics[0], "0x0");
|
|
1070
1069
|
let routedAddress = lowercaseAddresses ? Address.Evm.fromAddressLowercaseOrThrow(log.address) : Address.Evm.fromAddressOrThrow(log.address);
|
|
1071
1070
|
let eventConfig = EventRouter.get(eventRouter, EventRouter.getEvmEventId(topic0, log.topics.length), routedAddress, log.blockNumber, indexingAddresses);
|
|
1072
|
-
if (eventConfig
|
|
1073
|
-
return
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
return {
|
|
1096
|
-
kind: 0,
|
|
1097
|
-
eventConfig: eventConfig,
|
|
1098
|
-
timestamp: block.timestamp,
|
|
1099
|
-
chain: chain,
|
|
1100
|
-
blockNumber: block.number,
|
|
1101
|
-
logIndex: log.logIndex,
|
|
1102
|
-
event: {
|
|
1103
|
-
contractName: eventConfig.contractName,
|
|
1104
|
-
eventName: eventConfig.name,
|
|
1105
|
-
params: maybeDecodedEvent,
|
|
1106
|
-
chainId: chain,
|
|
1107
|
-
srcAddress: routedAddress,
|
|
1071
|
+
if (eventConfig === undefined) {
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
let decoded = Stdlib_Option.flatMap(Primitive_option.fromNullable(param[1]), __x => __x[eventConfig.contractName]);
|
|
1075
|
+
if (decoded === undefined) {
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
let decoded$1 = Primitive_option.valFromOption(decoded);
|
|
1079
|
+
return (async () => {
|
|
1080
|
+
let match;
|
|
1081
|
+
try {
|
|
1082
|
+
match = await Promise.all([
|
|
1083
|
+
getEventBlockOrThrow(log, eventConfig.selectedBlockFields),
|
|
1084
|
+
getEventTransactionOrThrow(log, eventConfig.selectedTransactionFields)
|
|
1085
|
+
]);
|
|
1086
|
+
} catch (raw_exn) {
|
|
1087
|
+
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
1088
|
+
throw {
|
|
1089
|
+
RE_EXN_ID: Source.GetItemsError,
|
|
1090
|
+
_1: {
|
|
1091
|
+
TAG: "FailedGettingFieldSelection",
|
|
1092
|
+
exn: exn,
|
|
1093
|
+
blockNumber: log.blockNumber,
|
|
1108
1094
|
logIndex: log.logIndex,
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1095
|
+
message: "Failed getting selected fields. Please double-check your RPC provider returns correct data."
|
|
1096
|
+
},
|
|
1097
|
+
Error: new Error()
|
|
1112
1098
|
};
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1099
|
+
}
|
|
1100
|
+
let block = match[0];
|
|
1101
|
+
return {
|
|
1102
|
+
kind: 0,
|
|
1103
|
+
eventConfig: eventConfig,
|
|
1104
|
+
timestamp: block.timestamp,
|
|
1105
|
+
chain: chain,
|
|
1106
|
+
blockNumber: block.number,
|
|
1107
|
+
logIndex: log.logIndex,
|
|
1108
|
+
event: {
|
|
1109
|
+
contractName: eventConfig.contractName,
|
|
1110
|
+
eventName: eventConfig.name,
|
|
1111
|
+
params: decoded$1,
|
|
1112
|
+
chainId: chain,
|
|
1113
|
+
srcAddress: routedAddress,
|
|
1114
|
+
logIndex: log.logIndex,
|
|
1115
|
+
transaction: match[1],
|
|
1116
|
+
block: block
|
|
1117
|
+
}
|
|
1118
|
+
};
|
|
1119
|
+
})();
|
|
1115
1120
|
}));
|
|
1116
1121
|
let optFirstBlockParent = await firstBlockParentPromise;
|
|
1117
1122
|
let totalTimeElapsed = Hrtime.toSecondsFloat(Hrtime.timeSince(startFetchingBatchTimeRef));
|