envio 3.7.0-svm-alpha.1 → 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.
- package/index.d.ts +331 -142
- package/package.json +6 -6
- package/src/ChainState.res +9 -1
- package/src/ChainState.res.mjs +6 -1
- package/src/Config.res +19 -10
- package/src/Config.res.mjs +17 -7
- package/src/Core.res +4 -0
- package/src/Envio.res +54 -76
- package/src/EventConfigBuilder.res +159 -30
- package/src/EventConfigBuilder.res.mjs +120 -24
- package/src/HandlerRegister.res +20 -11
- package/src/HandlerRegister.res.mjs +22 -5
- package/src/Hasura.res +30 -22
- package/src/Hasura.res.mjs +11 -5
- package/src/InMemoryStore.res +1 -6
- package/src/InMemoryStore.res.mjs +1 -3
- package/src/Internal.res +33 -13
- package/src/Internal.res.mjs +12 -16
- package/src/Main.res +2 -5
- package/src/MemoryStorage.res +40 -8
- package/src/MemoryStorage.res.mjs +38 -16
- package/src/Persistence.res +4 -2
- package/src/PgStorage.res +6 -1
- package/src/PgStorage.res.mjs +1 -1
- package/src/SimulateItems.res +261 -9
- package/src/SimulateItems.res.mjs +218 -47
- package/src/TestIndexer.res +26 -10
- package/src/TestIndexer.res.mjs +34 -12
- package/src/bindings/ClickHouse.res +49 -17
- package/src/bindings/ClickHouse.res.mjs +35 -14
- package/src/db/InternalTable.res +11 -0
- package/src/db/InternalTable.res.mjs +7 -0
- package/src/sources/EvmHyperSyncSource.res +8 -3
- package/src/sources/EvmHyperSyncSource.res.mjs +1 -1
- package/src/sources/SimulateSource.res +8 -4
- package/src/sources/SimulateSource.res.mjs +7 -3
- package/src/sources/Svm.res +34 -7
- package/src/sources/Svm.res.mjs +32 -4
- package/src/sources/SvmHyperSyncClient.res +10 -12
- package/src/sources/SvmHyperSyncClient.res.mjs +3 -2
- package/src/sources/SvmHyperSyncSource.res +96 -35
- package/src/sources/SvmHyperSyncSource.res.mjs +78 -38
- package/src/sources/TransactionStore.res +38 -0
- package/src/sources/TransactionStore.res.mjs +5 -0
- package/svm.schema.json +27 -78
|
@@ -273,6 +273,14 @@ function onClusterClause(onCluster) {
|
|
|
273
273
|
}
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
+
function replicatedTableSettingsClause(replicated) {
|
|
277
|
+
if (replicated) {
|
|
278
|
+
return "\nSETTINGS replicated_deduplication_window = 0";
|
|
279
|
+
} else {
|
|
280
|
+
return "";
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
276
284
|
function databaseEngineName(engineSpec) {
|
|
277
285
|
return engineSpec.trim().split("(")[0].split(" ")[0].trim();
|
|
278
286
|
}
|
|
@@ -295,12 +303,15 @@ function makeCreateHistoryTableQuery(entityConfig, database, replicatedOpt, onCl
|
|
|
295
303
|
let match = options !== undefined ? [
|
|
296
304
|
options.partitionBy,
|
|
297
305
|
options.orderBy,
|
|
298
|
-
options.ttl
|
|
306
|
+
options.ttl,
|
|
307
|
+
options.skippingIndexes
|
|
299
308
|
] : [
|
|
309
|
+
undefined,
|
|
300
310
|
undefined,
|
|
301
311
|
undefined,
|
|
302
312
|
undefined
|
|
303
313
|
];
|
|
314
|
+
let skippingIndexes = match[3];
|
|
304
315
|
let ttl = match[2];
|
|
305
316
|
let orderBy = match[1];
|
|
306
317
|
let partitionBy = match[0];
|
|
@@ -336,6 +347,11 @@ function makeCreateHistoryTableQuery(entityConfig, database, replicatedOpt, onCl
|
|
|
336
347
|
});
|
|
337
348
|
let partitionByClause = partitionBy !== undefined ? `\nPARTITION BY ` + resolveExpressionColumns(partitionBy) : "";
|
|
338
349
|
let ttlClause = ttl !== undefined ? `\nTTL ` + resolveExpressionColumns(ttl) : "";
|
|
350
|
+
let skippingIndexDefinitions = skippingIndexes !== undefined ? skippingIndexes.map(index => {
|
|
351
|
+
let granularity = index.granularity;
|
|
352
|
+
let granularityClause = granularity !== undefined ? ` GRANULARITY ` + granularity.toString() : "";
|
|
353
|
+
return `,\n INDEX \`` + index.name + `\` ` + resolveExpressionColumns(index.expr) + ` TYPE ` + index.type + granularityClause;
|
|
354
|
+
}).join("") : "";
|
|
339
355
|
return `CREATE TABLE IF NOT EXISTS ` + database + `.\`` + EntityHistory.historyTableName(entityConfig.name, entityConfig.index) + `\`` + (
|
|
340
356
|
onCluster ? ` ON CLUSTER '{cluster}'` : ""
|
|
341
357
|
) + ` (
|
|
@@ -344,10 +360,12 @@ function makeCreateHistoryTableQuery(entityConfig, database, replicatedOpt, onCl
|
|
|
344
360
|
\`` + EntityHistory.changeFieldName + `\` ` + getClickHouseFieldType({
|
|
345
361
|
type: "Enum",
|
|
346
362
|
config: EntityHistory.RowAction.config
|
|
347
|
-
}, false, false, undefined) + `
|
|
363
|
+
}, false, false, undefined) + skippingIndexDefinitions + `
|
|
348
364
|
)
|
|
349
365
|
ENGINE = ` + tableEngine + partitionByClause + `
|
|
350
|
-
ORDER BY (` + orderByColumns + `)` + ttlClause
|
|
366
|
+
ORDER BY (` + orderByColumns + `)` + ttlClause + (
|
|
367
|
+
replicated ? "\nSETTINGS replicated_deduplication_window = 0" : ""
|
|
368
|
+
);
|
|
351
369
|
}
|
|
352
370
|
|
|
353
371
|
function makeCreateCheckpointsTableQuery(database, replicatedOpt, onClusterOpt, chainIdModeOpt) {
|
|
@@ -365,7 +383,9 @@ function makeCreateCheckpointsTableQuery(database, replicatedOpt, onClusterOpt,
|
|
|
365
383
|
\`` + "events_processed" + `\` ` + getClickHouseFieldType("UInt64", false, false, undefined) + `
|
|
366
384
|
)
|
|
367
385
|
ENGINE = ` + tableEngine + `
|
|
368
|
-
ORDER BY (` + "id" + `)
|
|
386
|
+
ORDER BY (` + "id" + `)` + (
|
|
387
|
+
replicated ? "\nSETTINGS replicated_deduplication_window = 0" : ""
|
|
388
|
+
);
|
|
369
389
|
}
|
|
370
390
|
|
|
371
391
|
function makeCreateViewQuery(entityConfig, database, onClusterOpt) {
|
|
@@ -428,31 +448,31 @@ async function initialize(client, database, entities, param, chainIdModeOpt) {
|
|
|
428
448
|
}
|
|
429
449
|
}
|
|
430
450
|
if (hasReplicatedDatabaseEngine) {
|
|
431
|
-
await client.
|
|
451
|
+
await client.command({
|
|
432
452
|
query: `DROP DATABASE IF EXISTS ` + database + ` ON CLUSTER '{cluster}' SYNC`
|
|
433
453
|
});
|
|
434
454
|
} else {
|
|
435
|
-
await client.
|
|
455
|
+
await client.command({
|
|
436
456
|
query: `TRUNCATE DATABASE IF EXISTS ` + database + (
|
|
437
457
|
ddlOnCluster ? ` ON CLUSTER '{cluster}'` : ""
|
|
438
458
|
)
|
|
439
459
|
});
|
|
440
460
|
}
|
|
441
|
-
await client.
|
|
461
|
+
await client.command({
|
|
442
462
|
query: `CREATE DATABASE IF NOT EXISTS ` + database + databaseOnClusterClause + databaseEngineClause
|
|
443
463
|
});
|
|
444
|
-
await Promise.all(entities.map(entityConfig => client.
|
|
464
|
+
await Promise.all(entities.map(entityConfig => client.command({
|
|
445
465
|
query: makeCreateHistoryTableQuery(entityConfig, database, replicated, ddlOnCluster, chainIdMode)
|
|
446
466
|
})));
|
|
447
|
-
await client.
|
|
467
|
+
await client.command({
|
|
448
468
|
query: makeCreateCheckpointsTableQuery(database, replicated, ddlOnCluster, chainIdMode)
|
|
449
469
|
});
|
|
450
470
|
if (hasReplicatedDatabaseEngine) {
|
|
451
|
-
await client.
|
|
471
|
+
await client.command({
|
|
452
472
|
query: `SYSTEM SYNC DATABASE REPLICA ON CLUSTER '{cluster}' ` + database
|
|
453
473
|
});
|
|
454
474
|
}
|
|
455
|
-
await Promise.all(entities.map(entityConfig => client.
|
|
475
|
+
await Promise.all(entities.map(entityConfig => client.command({
|
|
456
476
|
query: makeCreateViewQuery(entityConfig, database, ddlOnCluster)
|
|
457
477
|
})));
|
|
458
478
|
return Logging.trace("ClickHouse storage initialization completed successfully");
|
|
@@ -466,7 +486,7 @@ async function initialize(client, database, entities, param, chainIdModeOpt) {
|
|
|
466
486
|
async function resume(client, database, checkpointId) {
|
|
467
487
|
try {
|
|
468
488
|
try {
|
|
469
|
-
await client.
|
|
489
|
+
await client.command({
|
|
470
490
|
query: `USE ` + database
|
|
471
491
|
});
|
|
472
492
|
} catch (raw_exn) {
|
|
@@ -480,11 +500,11 @@ async function resume(client, database, checkpointId) {
|
|
|
480
500
|
let tables = (await tablesResult.json()).data;
|
|
481
501
|
await Promise.all(tables.map(table => {
|
|
482
502
|
let tableName = table.name;
|
|
483
|
-
return client.
|
|
503
|
+
return client.command({
|
|
484
504
|
query: `ALTER TABLE ` + database + `.\`` + tableName + `\` DELETE WHERE \`` + EntityHistory.checkpointIdFieldName + `\` > ` + checkpointId.toString()
|
|
485
505
|
});
|
|
486
506
|
}));
|
|
487
|
-
return await client.
|
|
507
|
+
return await client.command({
|
|
488
508
|
query: `DELETE FROM ` + database + `.\`` + InternalTable.Checkpoints.table.tableName + `\` WHERE \`` + Table.idFieldName + `\` > ` + checkpointId.toString()
|
|
489
509
|
});
|
|
490
510
|
} catch (raw_exn$1) {
|
|
@@ -505,6 +525,7 @@ export {
|
|
|
505
525
|
setCheckpointsOrThrow,
|
|
506
526
|
setUpdatesOrThrow,
|
|
507
527
|
onClusterClause,
|
|
528
|
+
replicatedTableSettingsClause,
|
|
508
529
|
databaseEngineName,
|
|
509
530
|
makeCreateHistoryTableQuery,
|
|
510
531
|
makeCreateCheckpointsTableQuery,
|
package/src/db/InternalTable.res
CHANGED
|
@@ -17,6 +17,7 @@ module Chains = {
|
|
|
17
17
|
type field = [
|
|
18
18
|
| progressFields
|
|
19
19
|
| #id
|
|
20
|
+
| #ecosystem
|
|
20
21
|
| #start_block
|
|
21
22
|
| #end_block
|
|
22
23
|
| #max_reorg_depth
|
|
@@ -29,6 +30,7 @@ module Chains = {
|
|
|
29
30
|
|
|
30
31
|
let fields: array<field> = [
|
|
31
32
|
#id,
|
|
33
|
+
#ecosystem,
|
|
32
34
|
#start_block,
|
|
33
35
|
#end_block,
|
|
34
36
|
#max_reorg_depth,
|
|
@@ -57,6 +59,7 @@ module Chains = {
|
|
|
57
59
|
|
|
58
60
|
type t = {
|
|
59
61
|
@as("id") id: ChainId.t,
|
|
62
|
+
@as("ecosystem") ecosystem: string,
|
|
60
63
|
@as("start_block") startBlock: int,
|
|
61
64
|
@as("end_block") endBlock: Null.t<int>,
|
|
62
65
|
@as("max_reorg_depth") maxReorgDepth: int,
|
|
@@ -70,6 +73,10 @@ module Chains = {
|
|
|
70
73
|
"envio_chains",
|
|
71
74
|
~fields=[
|
|
72
75
|
mkField((#id: field :> string), ChainId, ~fieldSchema=ChainId.schema, ~isPrimaryKey),
|
|
76
|
+
// Which ecosystem the chain belongs to (evm / fuel / svm). Chain ids are
|
|
77
|
+
// only unique within an ecosystem (HOS-1880: Svm ids are Envio-assigned),
|
|
78
|
+
// so consumers should treat (ecosystem, id) as the chain's identity.
|
|
79
|
+
mkField((#ecosystem: field :> string), String, ~fieldSchema=S.string),
|
|
73
80
|
// Values populated from config
|
|
74
81
|
mkField((#start_block: field :> string), Int32, ~fieldSchema=S.int),
|
|
75
82
|
mkField((#end_block: field :> string), Int32, ~fieldSchema=S.null(S.int), ~isNullable),
|
|
@@ -104,6 +111,7 @@ module Chains = {
|
|
|
104
111
|
let initialFromConfig = (chainConfig: Config.chain) => {
|
|
105
112
|
{
|
|
106
113
|
id: chainConfig.id,
|
|
114
|
+
ecosystem: (chainConfig.ecosystem: Ecosystem.name :> string),
|
|
107
115
|
startBlock: chainConfig.startBlock,
|
|
108
116
|
endBlock: chainConfig.endBlock->Null.fromOption,
|
|
109
117
|
maxReorgDepth: chainConfig.maxReorgDepth,
|
|
@@ -131,6 +139,7 @@ module Chains = {
|
|
|
131
139
|
let value = initialValues->(Utils.magic: t => dict<unknown>)->Dict.get((field :> string))
|
|
132
140
|
switch typeof(value) {
|
|
133
141
|
| #object => "NULL"
|
|
142
|
+
| #string => `'${value->(Utils.magic: option<unknown> => string)}'`
|
|
134
143
|
| #number => value->(Utils.magic: option<unknown> => int)->Int.toString
|
|
135
144
|
| #bigint => value->(Utils.magic: option<unknown> => bigint)->BigInt.toString
|
|
136
145
|
| #boolean => value->(Utils.magic: option<unknown> => bool) ? "true" : "false"
|
|
@@ -648,6 +657,7 @@ module Views = {
|
|
|
648
657
|
`CREATE VIEW "${pgSchema}"."${metaViewName}" AS
|
|
649
658
|
SELECT
|
|
650
659
|
"${(#id: Chains.field :> string)}" AS "chainId",
|
|
660
|
+
"${(#ecosystem: Chains.field :> string)}" AS "ecosystem",
|
|
651
661
|
"${(#start_block: Chains.field :> string)}" AS "startBlock",
|
|
652
662
|
"${(#end_block: Chains.field :> string)}" AS "endBlock",
|
|
653
663
|
"${(#progress_block: Chains.field :> string)}" AS "progressBlock",
|
|
@@ -666,6 +676,7 @@ ORDER BY "${(#id: Chains.field :> string)}";`
|
|
|
666
676
|
SELECT
|
|
667
677
|
"${(#source_block: Chains.field :> string)}" AS "block_height",
|
|
668
678
|
"${(#id: Chains.field :> string)}" AS "chain_id",
|
|
679
|
+
"${(#ecosystem: Chains.field :> string)}" AS "ecosystem",
|
|
669
680
|
"${(#end_block: Chains.field :> string)}" AS "end_block",
|
|
670
681
|
"${(#first_event_block: Chains.field :> string)}" AS "first_event_block_number",
|
|
671
682
|
"${(#_is_hyper_sync: Chains.field :> string)}" AS "is_hyper_sync",
|
|
@@ -13,6 +13,7 @@ import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_excep
|
|
|
13
13
|
|
|
14
14
|
let fields = [
|
|
15
15
|
"id",
|
|
16
|
+
"ecosystem",
|
|
16
17
|
"start_block",
|
|
17
18
|
"end_block",
|
|
18
19
|
"max_reorg_depth",
|
|
@@ -27,6 +28,7 @@ let fields = [
|
|
|
27
28
|
|
|
28
29
|
let table = Table.mkTable("envio_chains", undefined, [
|
|
29
30
|
Table.mkField("id", "ChainId", ChainId.schema, undefined, undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined),
|
|
31
|
+
Table.mkField("ecosystem", "String", S$RescriptSchema.string, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
|
|
30
32
|
Table.mkField("start_block", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
|
|
31
33
|
Table.mkField("end_block", "Int32", S$RescriptSchema.$$null(S$RescriptSchema.int), undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
|
|
32
34
|
Table.mkField("max_reorg_depth", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
|
|
@@ -42,6 +44,7 @@ let table = Table.mkTable("envio_chains", undefined, [
|
|
|
42
44
|
function initialFromConfig(chainConfig) {
|
|
43
45
|
return {
|
|
44
46
|
id: chainConfig.id,
|
|
47
|
+
ecosystem: chainConfig.ecosystem,
|
|
45
48
|
start_block: chainConfig.startBlock,
|
|
46
49
|
end_block: Stdlib_Null.fromOption(chainConfig.endBlock),
|
|
47
50
|
max_reorg_depth: chainConfig.maxReorgDepth,
|
|
@@ -73,6 +76,8 @@ function makeInitialValuesQuery(pgSchema, chainConfigs) {
|
|
|
73
76
|
} else {
|
|
74
77
|
return "false";
|
|
75
78
|
}
|
|
79
|
+
} else if (match === "string") {
|
|
80
|
+
return `'` + value + `'`;
|
|
76
81
|
} else if (match === "object") {
|
|
77
82
|
return "NULL";
|
|
78
83
|
} else {
|
|
@@ -440,6 +445,7 @@ function makeMetaViewQuery(pgSchema) {
|
|
|
440
445
|
return `CREATE VIEW "` + pgSchema + `"."` + metaViewName + `" AS
|
|
441
446
|
SELECT
|
|
442
447
|
"` + "id" + `" AS "chainId",
|
|
448
|
+
"` + "ecosystem" + `" AS "ecosystem",
|
|
443
449
|
"` + "start_block" + `" AS "startBlock",
|
|
444
450
|
"` + "end_block" + `" AS "endBlock",
|
|
445
451
|
"` + "progress_block" + `" AS "progressBlock",
|
|
@@ -458,6 +464,7 @@ function makeChainMetadataViewQuery(pgSchema) {
|
|
|
458
464
|
SELECT
|
|
459
465
|
"` + "source_block" + `" AS "block_height",
|
|
460
466
|
"` + "id" + `" AS "chain_id",
|
|
467
|
+
"` + "ecosystem" + `" AS "ecosystem",
|
|
461
468
|
"` + "end_block" + `" AS "end_block",
|
|
462
469
|
"` + "first_event_block" + `" AS "first_event_block_number",
|
|
463
470
|
"` + "_is_hyper_sync" + `" AS "is_hyper_sync",
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
open Source
|
|
2
2
|
|
|
3
|
-
// Surfaced by
|
|
4
|
-
// token. The corrupted-token test feeds the real server error
|
|
3
|
+
// Surfaced by the HyperSync client (Rust) when HyperSync rejects the API
|
|
4
|
+
// token. The corrupted-token test feeds the real server error (from the query
|
|
5
|
+
// endpoint; the edge no longer 401s malformed tokens on /height) through this
|
|
5
6
|
// check so it can't silently drift away from what getHeightOrThrow guards on.
|
|
6
7
|
let isUnauthorizedError = (message: string) => message->String.includes("401 Unauthorized")
|
|
7
8
|
|
|
@@ -194,7 +195,11 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
|
|
|
194
195
|
}
|
|
195
196
|
}
|
|
196
197
|
|
|
197
|
-
|
|
198
|
+
// Called through the client rather than passed as a value: the client is a
|
|
199
|
+
// napi class, so a detached method reference loses the instance it belongs to.
|
|
200
|
+
let getBlockHashes = HyperSync.makeGetBlockHashes(
|
|
201
|
+
~query=(~blockNumbers) => client.getBlockHashes(~blockNumbers),
|
|
202
|
+
)
|
|
198
203
|
|
|
199
204
|
{
|
|
200
205
|
name,
|
|
@@ -137,7 +137,7 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`);
|
|
|
137
137
|
requestStats: requestStats
|
|
138
138
|
};
|
|
139
139
|
};
|
|
140
|
-
let getBlockHashes = HyperSync.makeGetBlockHashes(client.getBlockHashes);
|
|
140
|
+
let getBlockHashes = HyperSync.makeGetBlockHashes(blockNumbers => client.getBlockHashes(blockNumbers));
|
|
141
141
|
return {
|
|
142
142
|
name: "HyperSync",
|
|
143
143
|
sourceFor: "Sync",
|
|
@@ -4,6 +4,8 @@ let make = (
|
|
|
4
4
|
~chainId: ChainId.t,
|
|
5
5
|
~addressStore: AddressStore.t,
|
|
6
6
|
~ecosystem: Ecosystem.name=Evm,
|
|
7
|
+
~transactionStore: option<TransactionStore.t>=None,
|
|
8
|
+
~blockStore: option<BlockStore.t>=None,
|
|
7
9
|
): Source.t => {
|
|
8
10
|
let reportedHeight = max(endBlock, 1)
|
|
9
11
|
|
|
@@ -106,10 +108,12 @@ let make = (
|
|
|
106
108
|
Promise.resolve({
|
|
107
109
|
Source.knownHeight: reportedHeight,
|
|
108
110
|
parsedQueueItems,
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
transactionStore
|
|
112
|
-
blockStore:
|
|
111
|
+
// EVM/Fuel keep transaction and block inline. SVM simulate returns a
|
|
112
|
+
// store page so materialize + activity attach run the same path as HyperSync.
|
|
113
|
+
transactionStore,
|
|
114
|
+
blockStore: blockStore->Option.getOr(
|
|
115
|
+
BlockStore.fromJs([], ~ecosystem, ~shouldChecksum=false),
|
|
116
|
+
),
|
|
113
117
|
fromBlockQueried: fromBlock,
|
|
114
118
|
latestFetchedBlockNumber: toBlockQueried,
|
|
115
119
|
stats: {
|
|
@@ -2,9 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
import * as BlockStore from "./BlockStore.res.mjs";
|
|
4
4
|
import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
|
|
5
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
5
7
|
|
|
6
|
-
function make(items, endBlock, chainId, addressStore, ecosystemOpt) {
|
|
8
|
+
function make(items, endBlock, chainId, addressStore, ecosystemOpt, transactionStoreOpt, blockStoreOpt) {
|
|
7
9
|
let ecosystem = ecosystemOpt !== undefined ? ecosystemOpt : "evm";
|
|
10
|
+
let transactionStore = transactionStoreOpt !== undefined ? Primitive_option.valFromOption(transactionStoreOpt) : undefined;
|
|
11
|
+
let blockStore = blockStoreOpt !== undefined ? Primitive_option.valFromOption(blockStoreOpt) : undefined;
|
|
8
12
|
let reportedHeight = Primitive_int.max(endBlock, 1);
|
|
9
13
|
return {
|
|
10
14
|
name: "SimulateSource",
|
|
@@ -75,8 +79,8 @@ function make(items, endBlock, chainId, addressStore, ecosystemOpt) {
|
|
|
75
79
|
return Promise.resolve({
|
|
76
80
|
knownHeight: reportedHeight,
|
|
77
81
|
parsedQueueItems: parsedQueueItems,
|
|
78
|
-
transactionStore:
|
|
79
|
-
blockStore: BlockStore.fromJs([], ecosystem, false),
|
|
82
|
+
transactionStore: transactionStore,
|
|
83
|
+
blockStore: Stdlib_Option.getOr(blockStore, BlockStore.fromJs([], ecosystem, false)),
|
|
80
84
|
fromBlockQueried: fromBlock,
|
|
81
85
|
latestFetchedBlockNumber: toBlockQueried,
|
|
82
86
|
stats: {
|
package/src/sources/Svm.res
CHANGED
|
@@ -14,13 +14,38 @@ let eventTransactionFieldMask = TransactionStore.makeMaskFn(transactionFields)
|
|
|
14
14
|
// Rust store (`SvmBlockField`) — keep this order in sync.
|
|
15
15
|
let blockFields = ["slot", "time", "hash", "height", "parentSlot", "parentHash"]
|
|
16
16
|
|
|
17
|
-
// `slot
|
|
18
|
-
// via `
|
|
17
|
+
// `slot` is always included (the item's key); every other block field is
|
|
18
|
+
// opt-in via handler `fields.block`. All are materialised from the store.
|
|
19
19
|
//
|
|
20
20
|
// One instruction's selected block fields → store selection bitmask. Computed per
|
|
21
21
|
// event at config build and cached on the event config.
|
|
22
22
|
let eventBlockFieldMask = BlockStore.makeMaskFn(blockFields)
|
|
23
23
|
|
|
24
|
+
let attachAccountActivities = (payload: Internal.eventPayload, tx: Internal.eventTransaction) => {
|
|
25
|
+
let instruction = payload->(Utils.magic: Internal.eventPayload => dict<unknown>)
|
|
26
|
+
let transaction = tx->(Utils.magic: Internal.eventTransaction => dict<unknown>)
|
|
27
|
+
switch (instruction->Dict.get("accounts"), transaction->Dict.get("accountActivities")) {
|
|
28
|
+
| (Some(accounts), Some(activities)) if !(accounts->Array.isArray) =>
|
|
29
|
+
let byAddress = Dict.make()
|
|
30
|
+
(activities->(Utils.magic: unknown => array<Envio.svmAccountActivity>))->Array.forEach(
|
|
31
|
+
activity => byAddress->Dict.set(activity.address->SvmTypes.Pubkey.toString, activity),
|
|
32
|
+
)
|
|
33
|
+
(accounts->(Utils.magic: unknown => dict<Envio.svmInstructionAccount>))
|
|
34
|
+
->Dict.valuesToArray
|
|
35
|
+
->Array.forEach(account =>
|
|
36
|
+
switch byAddress->Dict.get(account.address->SvmTypes.Pubkey.toString) {
|
|
37
|
+
| Some(activity) =>
|
|
38
|
+
(account->(Utils.magic: Envio.svmInstructionAccount => dict<unknown>))->Dict.set(
|
|
39
|
+
"activity",
|
|
40
|
+
activity->(Utils.magic: Envio.svmAccountActivity => unknown),
|
|
41
|
+
)
|
|
42
|
+
| None => ()
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
| _ => ()
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
24
49
|
let make = (~logger: Pino.t): Ecosystem.t => {
|
|
25
50
|
name: Svm,
|
|
26
51
|
blockNumberName: "height",
|
|
@@ -36,16 +61,18 @@ let make = (~logger: Pino.t): Ecosystem.t => {
|
|
|
36
61
|
logger,
|
|
37
62
|
toEvent: eventItem => eventItem.payload->(Utils.magic: Internal.eventPayload => Internal.event),
|
|
38
63
|
toEventLogger: eventItem => {
|
|
39
|
-
let
|
|
40
|
-
eventItem.
|
|
64
|
+
let eventConfig =
|
|
65
|
+
eventItem.onEventRegistration.eventConfig->(
|
|
66
|
+
Utils.magic: Internal.eventConfig => Internal.svmInstructionEventConfig
|
|
67
|
+
)
|
|
41
68
|
Logging.createChildFrom(
|
|
42
69
|
~logger,
|
|
43
70
|
~params={
|
|
44
|
-
"program":
|
|
45
|
-
"instruction":
|
|
71
|
+
"program": eventConfig.contractName,
|
|
72
|
+
"instruction": eventConfig.name,
|
|
46
73
|
"chainId": eventItem.chainId,
|
|
47
74
|
"slot": eventItem.blockNumber,
|
|
48
|
-
"programId":
|
|
75
|
+
"programId": eventConfig.programId,
|
|
49
76
|
},
|
|
50
77
|
)
|
|
51
78
|
},
|
package/src/sources/Svm.res.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import * as Internal from "../Internal.res.mjs";
|
|
|
9
9
|
import * as BlockStore from "./BlockStore.res.mjs";
|
|
10
10
|
import * as Performance from "../bindings/Performance.res.mjs";
|
|
11
11
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
12
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
12
13
|
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
13
14
|
import * as TransactionStore from "./TransactionStore.res.mjs";
|
|
14
15
|
|
|
@@ -25,6 +26,32 @@ let blockFields = [
|
|
|
25
26
|
|
|
26
27
|
let eventBlockFieldMask = BlockStore.makeMaskFn(blockFields);
|
|
27
28
|
|
|
29
|
+
function attachAccountActivities(payload, tx) {
|
|
30
|
+
let match = payload["accounts"];
|
|
31
|
+
let match$1 = tx["accountActivities"];
|
|
32
|
+
if (match === undefined) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (match$1 === undefined) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
let accounts = Primitive_option.valFromOption(match);
|
|
39
|
+
if (Array.isArray(accounts)) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
let byAddress = {};
|
|
43
|
+
Primitive_option.valFromOption(match$1).forEach(activity => {
|
|
44
|
+
byAddress[activity.address] = activity;
|
|
45
|
+
});
|
|
46
|
+
Object.values(accounts).forEach(account => {
|
|
47
|
+
let activity = byAddress[account.address];
|
|
48
|
+
if (activity !== undefined) {
|
|
49
|
+
account["activity"] = activity;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
28
55
|
function make(logger) {
|
|
29
56
|
return {
|
|
30
57
|
name: "svm",
|
|
@@ -37,13 +64,13 @@ function make(logger) {
|
|
|
37
64
|
logger: logger,
|
|
38
65
|
toEvent: eventItem => eventItem.payload,
|
|
39
66
|
toEventLogger: eventItem => {
|
|
40
|
-
let
|
|
67
|
+
let eventConfig = eventItem.onEventRegistration.eventConfig;
|
|
41
68
|
return Logging.createChildFrom(logger, {
|
|
42
|
-
program:
|
|
43
|
-
instruction:
|
|
69
|
+
program: eventConfig.contractName,
|
|
70
|
+
instruction: eventConfig.name,
|
|
44
71
|
chainId: eventItem.chainId,
|
|
45
72
|
slot: eventItem.blockNumber,
|
|
46
|
-
programId:
|
|
73
|
+
programId: eventConfig.programId
|
|
47
74
|
});
|
|
48
75
|
},
|
|
49
76
|
toRawEvent: param => Stdlib_JsError.throwWithMessage("Raw events are not supported for SVM")
|
|
@@ -96,6 +123,7 @@ export {
|
|
|
96
123
|
eventTransactionFieldMask,
|
|
97
124
|
blockFields,
|
|
98
125
|
eventBlockFieldMask,
|
|
126
|
+
attachAccountActivities,
|
|
99
127
|
make,
|
|
100
128
|
GetFinalizedSlot,
|
|
101
129
|
makeRPCSource,
|
|
@@ -27,14 +27,15 @@ module Registration = {
|
|
|
27
27
|
// Earliest slot this registration accepts; `None` is unrestricted.
|
|
28
28
|
startBlock: option<int>,
|
|
29
29
|
discriminator?: string,
|
|
30
|
-
discriminatorByteLen: int,
|
|
31
30
|
isInner?: bool,
|
|
32
|
-
includeLogs: bool,
|
|
33
31
|
// DNF: outer array is OR of AND-groups.
|
|
34
32
|
accountFilters: array<array<accountFilter>>,
|
|
35
33
|
// camelCase Internal.svmTransactionField / svmBlockField names.
|
|
36
34
|
transactionFields: array<string>,
|
|
37
35
|
blockFields: array<string>,
|
|
36
|
+
accountActivityFields: array<string>,
|
|
37
|
+
logFields: array<string>,
|
|
38
|
+
instructionFields: array<string>,
|
|
38
39
|
// Borsh schema pieces; empty accounts + absent argsJson = no schema.
|
|
39
40
|
accounts: array<string>,
|
|
40
41
|
argsJson?: string,
|
|
@@ -55,9 +56,7 @@ module Registration = {
|
|
|
55
56
|
isWildcard: reg.isWildcard,
|
|
56
57
|
startBlock: reg.startBlock,
|
|
57
58
|
discriminator: ?eventConfig.discriminator,
|
|
58
|
-
discriminatorByteLen: eventConfig.discriminatorByteLen,
|
|
59
59
|
isInner: ?eventConfig.isInner,
|
|
60
|
-
includeLogs: eventConfig.includeLogs,
|
|
61
60
|
accountFilters: eventConfig.accountFilters->Array.map(group =>
|
|
62
61
|
group->Array.map(
|
|
63
62
|
(filter): accountFilter => {
|
|
@@ -68,6 +67,9 @@ module Registration = {
|
|
|
68
67
|
),
|
|
69
68
|
transactionFields: reg.fieldSelection.transactionFields->Utils.Set.toArray,
|
|
70
69
|
blockFields: reg.fieldSelection.blockFields->Utils.Set.toArray,
|
|
70
|
+
accountActivityFields: reg.fieldSelection.accountActivityFields->Utils.Set.toArray,
|
|
71
|
+
logFields: reg.fieldSelection.logFields->Utils.Set.toArray,
|
|
72
|
+
instructionFields: reg.fieldSelection.instructionFields->Utils.Set.toArray,
|
|
71
73
|
accounts: eventConfig.accounts,
|
|
72
74
|
argsJson: ?switch eventConfig.args {
|
|
73
75
|
| JSON.Null => None
|
|
@@ -205,8 +207,8 @@ module EventItems = {
|
|
|
205
207
|
}
|
|
206
208
|
|
|
207
209
|
type log = {
|
|
208
|
-
kind
|
|
209
|
-
message
|
|
210
|
+
kind?: string,
|
|
211
|
+
message?: string,
|
|
210
212
|
}
|
|
211
213
|
|
|
212
214
|
// One routed instruction; `block` and `transaction` are materialised from
|
|
@@ -215,17 +217,13 @@ module EventItems = {
|
|
|
215
217
|
onEventRegistrationIndex: int,
|
|
216
218
|
slot: int,
|
|
217
219
|
transactionIndex: int,
|
|
218
|
-
|
|
220
|
+
path: array<int>,
|
|
219
221
|
programId: string,
|
|
220
222
|
accounts: array<string>,
|
|
221
223
|
data: string,
|
|
222
|
-
d1?: string,
|
|
223
|
-
d2?: string,
|
|
224
|
-
d4?: string,
|
|
225
|
-
d8?: string,
|
|
226
224
|
isInner: bool,
|
|
227
225
|
decoded?: ResponseTypes.decodedInstruction,
|
|
228
|
-
// Present only when the routed registration
|
|
226
|
+
// Present only when the routed registration selected `fields.log`.
|
|
229
227
|
logs?: array<log>,
|
|
230
228
|
}
|
|
231
229
|
|
|
@@ -20,15 +20,16 @@ function fromOnEventRegistrations(onEventRegistrations) {
|
|
|
20
20
|
isWildcard: reg.isWildcard,
|
|
21
21
|
startBlock: reg.startBlock,
|
|
22
22
|
discriminator: eventConfig.discriminator,
|
|
23
|
-
discriminatorByteLen: eventConfig.discriminatorByteLen,
|
|
24
23
|
isInner: eventConfig.isInner,
|
|
25
|
-
includeLogs: eventConfig.includeLogs,
|
|
26
24
|
accountFilters: eventConfig.accountFilters.map(group => group.map(filter => ({
|
|
27
25
|
position: filter.position,
|
|
28
26
|
values: filter.values
|
|
29
27
|
}))),
|
|
30
28
|
transactionFields: Array.from(reg.fieldSelection.transactionFields),
|
|
31
29
|
blockFields: Array.from(reg.fieldSelection.blockFields),
|
|
30
|
+
accountActivityFields: Array.from(reg.fieldSelection.accountActivityFields),
|
|
31
|
+
logFields: Array.from(reg.fieldSelection.logFields),
|
|
32
|
+
instructionFields: Array.from(reg.fieldSelection.instructionFields),
|
|
32
33
|
accounts: eventConfig.accounts,
|
|
33
34
|
argsJson: tmp,
|
|
34
35
|
definedTypesJson: tmp$1
|