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.
- 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/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 +33 -6
- package/src/bindings/ClickHouse.res.mjs +25 -4
- package/src/db/InternalTable.res +11 -0
- package/src/db/InternalTable.res.mjs +7 -0
- package/src/sources/EvmHyperSyncSource.res +3 -2
- 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 +91 -34
- package/src/sources/SvmHyperSyncSource.res.mjs +77 -37
- package/src/sources/TransactionStore.res +38 -0
- package/src/sources/TransactionStore.res.mjs +5 -0
- package/svm.schema.json +27 -78
package/src/Config.res
CHANGED
|
@@ -33,13 +33,19 @@ type sourceConfig =
|
|
|
33
33
|
// A `simulate` run: the items the test fed in, parsed against the chain's
|
|
34
34
|
// registrations. The source itself is built with the chain's address store,
|
|
35
35
|
// like every other source, so it can apply the same gates.
|
|
36
|
-
| SimulateSourceConfig({
|
|
36
|
+
| SimulateSourceConfig({
|
|
37
|
+
items: array<Internal.item>,
|
|
38
|
+
endBlock: int,
|
|
39
|
+
transactionStore?: TransactionStore.t,
|
|
40
|
+
blockStore?: BlockStore.t,
|
|
41
|
+
})
|
|
37
42
|
// For tests: pass custom sources directly
|
|
38
43
|
| CustomSources(array<Source.t>)
|
|
39
44
|
|
|
40
45
|
type chain = {
|
|
41
46
|
name: string,
|
|
42
47
|
id: ChainId.t,
|
|
48
|
+
ecosystem: Ecosystem.name,
|
|
43
49
|
startBlock: int,
|
|
44
50
|
endBlock?: int,
|
|
45
51
|
maxReorgDepth: int,
|
|
@@ -183,6 +189,7 @@ module EnvioAddresses = {
|
|
|
183
189
|
// The table already keys rows by chain through the composite `id`, so the
|
|
184
190
|
// per-chain mode must not append a second chain-id column to it.
|
|
185
191
|
crossChain: true,
|
|
192
|
+
internal: true,
|
|
186
193
|
}->Internal.fromGenericEntityConfig
|
|
187
194
|
}
|
|
188
195
|
|
|
@@ -235,9 +242,6 @@ let svmEventDescriptorSchema = S.schema(s =>
|
|
|
235
242
|
{
|
|
236
243
|
"discriminator": s.matches(S.option(S.string)),
|
|
237
244
|
"discriminatorByteLen": s.matches(S.int),
|
|
238
|
-
"transactionFields": s.matches(S.array(Internal.svmTransactionFieldSchema)),
|
|
239
|
-
"blockFields": s.matches(S.option(S.array(Internal.svmBlockFieldSchema))),
|
|
240
|
-
"includeLogs": s.matches(S.bool),
|
|
241
245
|
// An array of AND-groups OR-ed together: the CLI normalizes both the flat
|
|
242
246
|
// and `any_of` YAML shapes to `Vec<Vec<SvmAccountFilterJson>>`.
|
|
243
247
|
"accountFilters": s.matches(
|
|
@@ -348,10 +352,18 @@ let propertySchema = S.schema(s =>
|
|
|
348
352
|
}
|
|
349
353
|
)
|
|
350
354
|
|
|
355
|
+
let clickhouseSkippingIndexSchema: S.t<Internal.clickhouseSkippingIndex> = S.object(s => {
|
|
356
|
+
Internal.name: s.field("name", S.string),
|
|
357
|
+
expr: s.field("expr", S.string),
|
|
358
|
+
type_: s.field("type", S.string),
|
|
359
|
+
granularity: ?s.field("granularity", S.option(S.int)),
|
|
360
|
+
})
|
|
361
|
+
|
|
351
362
|
let clickhouseTableOptionsSchema: S.t<Internal.clickhouseTableOptions> = S.object(s => {
|
|
352
363
|
Internal.partitionBy: ?s.field("partitionBy", S.option(S.string)),
|
|
353
364
|
orderBy: ?s.field("orderBy", S.option(S.array(S.string))),
|
|
354
365
|
ttl: ?s.field("ttl", S.option(S.string)),
|
|
366
|
+
skippingIndexes: ?s.field("skippingIndexes", S.option(S.array(clickhouseSkippingIndexSchema))),
|
|
355
367
|
})
|
|
356
368
|
|
|
357
369
|
// The entity's `clickhouse` storage arg mirrors the @storage directive:
|
|
@@ -377,6 +389,7 @@ let entityJsonSchema = S.schema(s =>
|
|
|
377
389
|
"name": s.matches(S.string),
|
|
378
390
|
"crossChain": s.matches(S.option(S.bool)),
|
|
379
391
|
"storage": s.matches(S.option(entityStorageSchema)),
|
|
392
|
+
"internal": s.matches(S.option(S.bool)),
|
|
380
393
|
"properties": s.matches(S.array(propertySchema)),
|
|
381
394
|
"derivedFields": s.matches(S.option(S.array(derivedFieldSchema))),
|
|
382
395
|
"compositeIndices": s.matches(S.option(S.array(S.array(compositeIndexFieldSchema)))),
|
|
@@ -580,6 +593,7 @@ let parseEntitiesFromJson = (
|
|
|
580
593
|
table,
|
|
581
594
|
storage,
|
|
582
595
|
crossChain,
|
|
596
|
+
internal: entityJson["internal"]->Option.getOr(false),
|
|
583
597
|
}->Internal.fromGenericEntityConfig
|
|
584
598
|
})
|
|
585
599
|
}
|
|
@@ -796,9 +810,6 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
796
810
|
"svm": option<{
|
|
797
811
|
"discriminator": option<string>,
|
|
798
812
|
"discriminatorByteLen": int,
|
|
799
|
-
"transactionFields": array<Internal.svmTransactionField>,
|
|
800
|
-
"blockFields": option<array<Internal.svmBlockField>>,
|
|
801
|
-
"includeLogs": bool,
|
|
802
813
|
"accountFilters": option<
|
|
803
814
|
array<array<{"position": int, "values": array<string>}>>,
|
|
804
815
|
>,
|
|
@@ -832,9 +843,6 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
832
843
|
~programId,
|
|
833
844
|
~discriminator=svm["discriminator"],
|
|
834
845
|
~discriminatorByteLen=svm["discriminatorByteLen"],
|
|
835
|
-
~transactionFields=svm["transactionFields"],
|
|
836
|
-
~blockFields=?svm["blockFields"],
|
|
837
|
-
~includeLogs=svm["includeLogs"],
|
|
838
846
|
~accountFilters,
|
|
839
847
|
~isInner=svm["isInner"],
|
|
840
848
|
~accounts=svm["accounts"]->Option.getOr([]),
|
|
@@ -1012,6 +1020,7 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
1012
1020
|
{
|
|
1013
1021
|
name: chainName,
|
|
1014
1022
|
id: chainId,
|
|
1023
|
+
ecosystem: ecosystemName,
|
|
1015
1024
|
startBlock: publicChainConfig["startBlock"],
|
|
1016
1025
|
endBlock: ?publicChainConfig["endBlock"],
|
|
1017
1026
|
maxReorgDepth: switch ecosystemName {
|
package/src/Config.res.mjs
CHANGED
|
@@ -70,7 +70,8 @@ let entityConfig = {
|
|
|
70
70
|
schema: schema,
|
|
71
71
|
table: table,
|
|
72
72
|
storage: entityConfig_storage,
|
|
73
|
-
crossChain: true
|
|
73
|
+
crossChain: true,
|
|
74
|
+
internal: true
|
|
74
75
|
};
|
|
75
76
|
|
|
76
77
|
let EnvioAddresses = {
|
|
@@ -124,9 +125,6 @@ let publicConfigChainSchema = S$RescriptSchema.schema(s => ({
|
|
|
124
125
|
let svmEventDescriptorSchema = S$RescriptSchema.schema(s => ({
|
|
125
126
|
discriminator: s.m(S$RescriptSchema.option(S$RescriptSchema.string)),
|
|
126
127
|
discriminatorByteLen: s.m(S$RescriptSchema.int),
|
|
127
|
-
transactionFields: s.m(S$RescriptSchema.array(Internal.svmTransactionFieldSchema)),
|
|
128
|
-
blockFields: s.m(S$RescriptSchema.option(S$RescriptSchema.array(Internal.svmBlockFieldSchema))),
|
|
129
|
-
includeLogs: s.m(S$RescriptSchema.bool),
|
|
130
128
|
accountFilters: s.m(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.array(S$RescriptSchema.schema(s => ({
|
|
131
129
|
position: s.m(S$RescriptSchema.int),
|
|
132
130
|
values: s.m(S$RescriptSchema.array(S$RescriptSchema.string))
|
|
@@ -204,10 +202,18 @@ let propertySchema = S$RescriptSchema.schema(s => ({
|
|
|
204
202
|
description: s.m(S$RescriptSchema.option(S$RescriptSchema.string))
|
|
205
203
|
}));
|
|
206
204
|
|
|
205
|
+
let clickhouseSkippingIndexSchema = S$RescriptSchema.object(s => ({
|
|
206
|
+
name: s.f("name", S$RescriptSchema.string),
|
|
207
|
+
expr: s.f("expr", S$RescriptSchema.string),
|
|
208
|
+
type: s.f("type", S$RescriptSchema.string),
|
|
209
|
+
granularity: s.f("granularity", S$RescriptSchema.option(S$RescriptSchema.int))
|
|
210
|
+
}));
|
|
211
|
+
|
|
207
212
|
let clickhouseTableOptionsSchema = S$RescriptSchema.object(s => ({
|
|
208
213
|
partitionBy: s.f("partitionBy", S$RescriptSchema.option(S$RescriptSchema.string)),
|
|
209
214
|
orderBy: s.f("orderBy", S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.string))),
|
|
210
|
-
ttl: s.f("ttl", S$RescriptSchema.option(S$RescriptSchema.string))
|
|
215
|
+
ttl: s.f("ttl", S$RescriptSchema.option(S$RescriptSchema.string)),
|
|
216
|
+
skippingIndexes: s.f("skippingIndexes", S$RescriptSchema.option(S$RescriptSchema.array(clickhouseSkippingIndexSchema)))
|
|
211
217
|
}));
|
|
212
218
|
|
|
213
219
|
let entityClickhouseStorageJsonSchema = S$RescriptSchema.union([
|
|
@@ -230,6 +236,7 @@ let entityJsonSchema = S$RescriptSchema.schema(s => ({
|
|
|
230
236
|
name: s.m(S$RescriptSchema.string),
|
|
231
237
|
crossChain: s.m(S$RescriptSchema.option(S$RescriptSchema.bool)),
|
|
232
238
|
storage: s.m(S$RescriptSchema.option(entityStorageSchema)),
|
|
239
|
+
internal: s.m(S$RescriptSchema.option(S$RescriptSchema.bool)),
|
|
233
240
|
properties: s.m(S$RescriptSchema.array(propertySchema)),
|
|
234
241
|
derivedFields: s.m(S$RescriptSchema.option(S$RescriptSchema.array(derivedFieldSchema))),
|
|
235
242
|
compositeIndices: s.m(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.array(compositeIndexFieldSchema)))),
|
|
@@ -400,7 +407,8 @@ function parseEntitiesFromJson(entitiesJson, enumConfigsByName, globalStorage, d
|
|
|
400
407
|
schema: schema,
|
|
401
408
|
table: table,
|
|
402
409
|
storage: storage,
|
|
403
|
-
crossChain: crossChain
|
|
410
|
+
crossChain: crossChain,
|
|
411
|
+
internal: Stdlib_Option.getOr(entityJson.internal, false)
|
|
404
412
|
};
|
|
405
413
|
});
|
|
406
414
|
}
|
|
@@ -564,7 +572,7 @@ function fromPublic(publicConfigJson) {
|
|
|
564
572
|
position: af.position,
|
|
565
573
|
values: af.values
|
|
566
574
|
})));
|
|
567
|
-
return EventConfigBuilder.buildSvmInstructionEventConfig(contractName, eventName, programId, svm.discriminator, svm.discriminatorByteLen,
|
|
575
|
+
return EventConfigBuilder.buildSvmInstructionEventConfig(contractName, eventName, programId, svm.discriminator, svm.discriminatorByteLen, accountFilters, svm.isInner, Stdlib_Option.getOr(svm.accounts, []), Stdlib_Option.getOr(svm.args, null), svmDefinedTypes);
|
|
568
576
|
}
|
|
569
577
|
});
|
|
570
578
|
} else {
|
|
@@ -696,6 +704,7 @@ function fromPublic(publicConfigJson) {
|
|
|
696
704
|
return {
|
|
697
705
|
name: chainName,
|
|
698
706
|
id: chainId,
|
|
707
|
+
ecosystem: ecosystemName,
|
|
699
708
|
startBlock: publicChainConfig.startBlock,
|
|
700
709
|
endBlock: publicChainConfig.endBlock,
|
|
701
710
|
maxReorgDepth: tmp,
|
|
@@ -1097,6 +1106,7 @@ export {
|
|
|
1097
1106
|
compositeIndexFieldSchema,
|
|
1098
1107
|
derivedFieldSchema,
|
|
1099
1108
|
propertySchema,
|
|
1109
|
+
clickhouseSkippingIndexSchema,
|
|
1100
1110
|
clickhouseTableOptionsSchema,
|
|
1101
1111
|
entityClickhouseStorageJsonSchema,
|
|
1102
1112
|
entityStorageSchema,
|
package/src/Envio.res
CHANGED
|
@@ -20,58 +20,48 @@ type svmOnSlotArgs<'context> = {
|
|
|
20
20
|
context: 'context,
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
discriminator didn't match any registered instruction. */
|
|
27
|
-
type svmInstructionParams = {
|
|
28
|
-
/** Schema-declared instruction name (matches the codegen module suffix). */
|
|
29
|
-
name: string,
|
|
30
|
-
/** Borsh-decoded args. `JSON.Object({})` for no-arg instructions
|
|
31
|
-
(e.g. `VerifyCollection`). POC types this as raw `JSON.t`; cast at the
|
|
32
|
-
handler with `(json :> MyArgsType)` until typed codegen lands. */
|
|
33
|
-
args: JSON.t,
|
|
34
|
-
/** Named accounts in schema order. Keys are exactly the schema-declared
|
|
35
|
-
names; values are base58 pubkey strings. */
|
|
36
|
-
accounts: dict<string>,
|
|
37
|
-
/** Accounts beyond the schema's named list (Anchor `remaining_accounts`,
|
|
38
|
-
IDL drift). `[]` when counts match. */
|
|
39
|
-
extraAccounts: array<string>,
|
|
23
|
+
type svmLamports = {
|
|
24
|
+
pre: bigint,
|
|
25
|
+
post: bigint,
|
|
40
26
|
}
|
|
41
27
|
|
|
42
|
-
type
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
when the account was closed during it. Pre and post owners differ only when
|
|
47
|
-
a `SetAuthority(AccountOwner)` runs mid-transaction. */
|
|
48
|
-
owner?: SvmTypes.Pubkey.t,
|
|
49
|
-
/** Mint decimals, for scaling the raw amounts below. */
|
|
50
|
-
decimals?: int,
|
|
51
|
-
/** Raw amount in base units before the transaction. Absent when the token
|
|
52
|
-
account was created during it. */
|
|
28
|
+
type svmAccountToken = {
|
|
29
|
+
mint: SvmTypes.Pubkey.t,
|
|
30
|
+
owner: SvmTypes.Pubkey.t,
|
|
31
|
+
decimals: int,
|
|
53
32
|
preAmount?: bigint,
|
|
54
|
-
/** Raw amount in base units after the transaction. Absent when the token
|
|
55
|
-
account was closed during it. */
|
|
56
33
|
postAmount?: bigint,
|
|
57
34
|
}
|
|
58
35
|
|
|
36
|
+
type svmAccountActivity = {
|
|
37
|
+
address: SvmTypes.Pubkey.t,
|
|
38
|
+
transactionAccountIndex?: int,
|
|
39
|
+
isSigner?: bool,
|
|
40
|
+
isWritable?: bool,
|
|
41
|
+
lamports?: svmLamports,
|
|
42
|
+
token?: svmAccountToken,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type svmInstructionAccount = {
|
|
46
|
+
address: SvmTypes.Pubkey.t,
|
|
47
|
+
accountName: string,
|
|
48
|
+
instructionAccountIndex: int,
|
|
49
|
+
activity?: svmAccountActivity,
|
|
50
|
+
}
|
|
51
|
+
|
|
59
52
|
type svmTransaction = {
|
|
60
53
|
transactionIndex?: int,
|
|
61
|
-
|
|
62
|
-
signature: string,
|
|
54
|
+
signature?: string,
|
|
63
55
|
feePayer?: SvmTypes.Pubkey.t,
|
|
64
56
|
success?: bool,
|
|
65
57
|
err?: string,
|
|
66
58
|
fee?: bigint,
|
|
67
59
|
computeUnitsConsumed?: bigint,
|
|
68
|
-
accountKeys
|
|
60
|
+
accountKeys?: array<SvmTypes.Pubkey.t>,
|
|
69
61
|
recentBlockhash?: string,
|
|
70
62
|
version?: string,
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
allSignatures: array<string>,
|
|
74
|
-
tokenBalances?: array<svmTokenBalance>,
|
|
63
|
+
allSignatures?: array<string>,
|
|
64
|
+
accountActivities?: array<svmAccountActivity>,
|
|
75
65
|
}
|
|
76
66
|
|
|
77
67
|
type svmLog = {
|
|
@@ -79,57 +69,29 @@ type svmLog = {
|
|
|
79
69
|
message: string,
|
|
80
70
|
}
|
|
81
71
|
|
|
82
|
-
|
|
83
|
-
`time` can still be absent — HyperSync/Solana may not report a block time for
|
|
84
|
-
every slot. Every other field is opt-in via `field_selection.block_fields`,
|
|
85
|
-
materialised from the per-chain block store at batch prep. */
|
|
86
|
-
type svmInstructionBlock = {
|
|
87
|
-
/** Slot this instruction's block was matched in. */
|
|
72
|
+
type svmBlock = {
|
|
88
73
|
slot: int,
|
|
89
|
-
/** Unix block time (seconds). */
|
|
90
74
|
time?: int,
|
|
91
|
-
hash
|
|
92
|
-
/** Block height (distinct from slot). Absent when not selected via
|
|
93
|
-
`field_selection.block_fields`. */
|
|
75
|
+
hash?: string,
|
|
94
76
|
height?: int,
|
|
95
77
|
parentSlot?: int,
|
|
96
78
|
parentHash?: string,
|
|
97
79
|
}
|
|
98
80
|
|
|
99
|
-
/** The per-instruction payload handlers receive as their `instruction`
|
|
100
|
-
argument. Carries the matched instruction's own fields plus the
|
|
101
|
-
program/instruction names, parent transaction, scoped logs, and block. */
|
|
102
81
|
type svmInstruction = {
|
|
103
|
-
/** Program name as declared under `programs[].name` in `config.yaml`. */
|
|
104
82
|
programName: string,
|
|
105
|
-
/** Instruction name as declared under `instructions[].name` in
|
|
106
|
-
`config.yaml`. */
|
|
107
83
|
instructionName: string,
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
data
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
/** Discriminator prefixes pre-extracted by HyperSync. Each is `Some` only
|
|
117
|
-
when the underlying instruction is at least that long. */
|
|
118
|
-
d1?: string,
|
|
119
|
-
d2?: string,
|
|
120
|
-
d4?: string,
|
|
121
|
-
d8?: string,
|
|
122
|
-
/** Borsh-decoded params view. See [[svmInstructionParams]]. */
|
|
123
|
-
params?: svmInstructionParams,
|
|
124
|
-
/** Parent transaction. Carries only the fields selected via
|
|
125
|
-
`field_selection.transaction_fields`; absent when none are selected. */
|
|
84
|
+
discriminator: string,
|
|
85
|
+
programId?: SvmTypes.Pubkey.t,
|
|
86
|
+
data?: string,
|
|
87
|
+
path?: array<int>,
|
|
88
|
+
isInner?: bool,
|
|
89
|
+
args?: JSON.t,
|
|
90
|
+
accounts?: dict<svmInstructionAccount>,
|
|
91
|
+
accountArguments?: array<SvmTypes.Pubkey.t>,
|
|
126
92
|
transaction?: svmTransaction,
|
|
127
|
-
/** Program log entries scoped to this instruction. Absent when the
|
|
128
|
-
per-instruction `include_logs` flag is `false`. */
|
|
129
93
|
logs?: array<svmLog>,
|
|
130
|
-
|
|
131
|
-
// before a handler ever reads it.
|
|
132
|
-
block?: svmInstructionBlock,
|
|
94
|
+
block?: svmBlock,
|
|
133
95
|
}
|
|
134
96
|
|
|
135
97
|
/** Arguments passed to handlers registered via `indexer.onInstruction`. */
|
|
@@ -315,6 +277,22 @@ type fuelSimulateItem = {
|
|
|
315
277
|
transaction?: fuelTransactionInput,
|
|
316
278
|
}
|
|
317
279
|
|
|
280
|
+
type svmSimulateItem = {
|
|
281
|
+
program: string,
|
|
282
|
+
instruction: string,
|
|
283
|
+
slot?: int,
|
|
284
|
+
path?: array<int>,
|
|
285
|
+
programId?: string,
|
|
286
|
+
data?: string,
|
|
287
|
+
isInner?: bool,
|
|
288
|
+
args?: JSON.t,
|
|
289
|
+
accounts?: dict<{address: string}>,
|
|
290
|
+
accountArguments?: array<string>,
|
|
291
|
+
logs?: array<{kind?: string, message?: string}>,
|
|
292
|
+
block?: svmBlock,
|
|
293
|
+
transaction?: unknown,
|
|
294
|
+
}
|
|
295
|
+
|
|
318
296
|
// Detects contexts where a full-screen TUI is counter-productive: piped/redirected
|
|
319
297
|
// stdout, CI, and coding agents. `CLAUDECODE` is set by Claude Code; `CI` is the
|
|
320
298
|
// de-facto convention across CI providers; `TERM=dumb` is set by editors/tools
|
|
@@ -310,33 +310,47 @@ let internalBlockFields = ["number"]
|
|
|
310
310
|
// selected.
|
|
311
311
|
let rawEventBlockFields = ["hash", "timestamp"]
|
|
312
312
|
|
|
313
|
-
let
|
|
313
|
+
let evmSelectionKinds = ["block", "transaction"]
|
|
314
|
+
let svmSelectionKinds = ["instruction", "transaction", "accountActivity", "block", "log"]
|
|
314
315
|
|
|
315
316
|
// A handler written in plain JS gets no type error for a `blocks`/`transactions`
|
|
316
317
|
// typo, and an unrecognised key would read as an empty selection — silently
|
|
317
318
|
// dropping every field `config.yaml` selected. Rejected here so the option is
|
|
318
319
|
// held to the same shape whether or not the project type-checks it.
|
|
319
|
-
let
|
|
320
|
-
|
|
321
|
-
|
|
320
|
+
let quotedJoin = (names: array<string>) =>
|
|
321
|
+
names->Array.map(name => `"${name}"`)->Array.joinUnsafe(", ")
|
|
322
|
+
|
|
323
|
+
let validateFieldsShapeOrThrow = (
|
|
324
|
+
fields: unknown,
|
|
325
|
+
~registration: string,
|
|
326
|
+
~validKeys: array<string>,
|
|
327
|
+
~shapeNoun: string,
|
|
328
|
+
) => {
|
|
329
|
+
if typeof(fields) !== #object || fields === %raw(`null`) || Array.isArray(fields) {
|
|
322
330
|
JsError.throwWithMessage(
|
|
323
|
-
`The fields option of ${registration} must be an object of
|
|
331
|
+
`The fields option of ${registration} must be an object of ${shapeNoun} field names.`,
|
|
324
332
|
)
|
|
325
333
|
}
|
|
326
|
-
|
|
334
|
+
fields
|
|
327
335
|
->(Utils.magic: unknown => dict<unknown>)
|
|
328
336
|
->Dict.keysToArray
|
|
329
337
|
->Array.forEach(key =>
|
|
330
|
-
if !(
|
|
338
|
+
if !(validKeys->Array.includes(key)) {
|
|
331
339
|
JsError.throwWithMessage(
|
|
332
|
-
`Invalid "${key}" key in the fields option of ${registration}. Valid keys: ${
|
|
333
|
-
|
|
340
|
+
`Invalid "${key}" key in the fields option of ${registration}. Valid keys: ${quotedJoin(
|
|
341
|
+
validKeys,
|
|
334
342
|
)}.`,
|
|
335
343
|
)
|
|
336
344
|
}
|
|
337
345
|
)
|
|
338
346
|
}
|
|
339
347
|
|
|
348
|
+
let selectionList = (fields: unknown, key: string): option<array<string>> =>
|
|
349
|
+
switch (fields->(Utils.magic: unknown => dict<unknown>))->Dict.get(key) {
|
|
350
|
+
| None => None
|
|
351
|
+
| Some(value) => Some(value->(Utils.magic: unknown => array<string>))
|
|
352
|
+
}
|
|
353
|
+
|
|
340
354
|
let validBlockFields = Utils.Set.fromArray(Evm.blockFields)
|
|
341
355
|
let validTransactionFields = Utils.Set.fromArray(Evm.transactionFields)
|
|
342
356
|
|
|
@@ -345,6 +359,7 @@ let parseFieldsOrThrow = (
|
|
|
345
359
|
~valid: Utils.Set.t<string>,
|
|
346
360
|
~kind: string,
|
|
347
361
|
~registration: string,
|
|
362
|
+
~rejectEmpty=false,
|
|
348
363
|
) => {
|
|
349
364
|
let seen = Utils.Set.make()
|
|
350
365
|
let fields = switch fields {
|
|
@@ -358,9 +373,9 @@ let parseFieldsOrThrow = (
|
|
|
358
373
|
fields->Array.forEach(name => {
|
|
359
374
|
if !(valid->Utils.Set.has(name)) {
|
|
360
375
|
JsError.throwWithMessage(
|
|
361
|
-
`Invalid "${name}" field in the fields.${kind} option of ${registration}. Valid ${kind} fields: ${
|
|
362
|
-
->Utils.Set.toArray
|
|
363
|
-
|
|
376
|
+
`Invalid "${name}" field in the fields.${kind} option of ${registration}. Valid ${kind} fields: ${quotedJoin(
|
|
377
|
+
valid->Utils.Set.toArray,
|
|
378
|
+
)}.`,
|
|
364
379
|
)
|
|
365
380
|
}
|
|
366
381
|
if seen->Utils.Set.has(name) {
|
|
@@ -370,6 +385,11 @@ let parseFieldsOrThrow = (
|
|
|
370
385
|
}
|
|
371
386
|
seen->Utils.Set.add(name)->ignore
|
|
372
387
|
})
|
|
388
|
+
if rejectEmpty && fields->Array.length === 0 {
|
|
389
|
+
JsError.throwWithMessage(
|
|
390
|
+
`The fields.${kind} option of ${registration} must list at least one field.`,
|
|
391
|
+
)
|
|
392
|
+
}
|
|
373
393
|
seen
|
|
374
394
|
}
|
|
375
395
|
|
|
@@ -377,21 +397,26 @@ let parseFieldsOrThrow = (
|
|
|
377
397
|
// option itself (the names are for error messages), never on the chain, so one
|
|
378
398
|
// `onEvent` call resolves once and shares the result across every chain.
|
|
379
399
|
let resolveInlineFieldSelection = (
|
|
380
|
-
fields:
|
|
400
|
+
fields: unknown,
|
|
381
401
|
~contractName: string,
|
|
382
402
|
~eventName: string,
|
|
383
403
|
~enableRawEvents: bool,
|
|
384
404
|
): Internal.fieldSelection => {
|
|
385
405
|
let registration = `the "${eventName}" event registration on contract "${contractName}"`
|
|
386
|
-
validateFieldsShapeOrThrow(
|
|
406
|
+
validateFieldsShapeOrThrow(
|
|
407
|
+
fields,
|
|
408
|
+
~registration,
|
|
409
|
+
~validKeys=evmSelectionKinds,
|
|
410
|
+
~shapeNoun="block and transaction",
|
|
411
|
+
)
|
|
387
412
|
let blockFields = parseFieldsOrThrow(
|
|
388
|
-
fields
|
|
413
|
+
selectionList(fields, "block"),
|
|
389
414
|
~valid=validBlockFields,
|
|
390
415
|
~kind="block",
|
|
391
416
|
~registration,
|
|
392
417
|
)
|
|
393
418
|
let transactionFields = parseFieldsOrThrow(
|
|
394
|
-
fields
|
|
419
|
+
selectionList(fields, "transaction"),
|
|
395
420
|
~valid=validTransactionFields,
|
|
396
421
|
~kind="transaction",
|
|
397
422
|
~registration,
|
|
@@ -498,9 +523,117 @@ let buildEvmOnEventRegistration = (
|
|
|
498
523
|
|
|
499
524
|
// ============== Build SVM instruction event config ==============
|
|
500
525
|
|
|
501
|
-
//
|
|
502
|
-
//
|
|
503
|
-
let alwaysIncludedSvmBlockFields
|
|
526
|
+
// `block.slot` is the item's own key, so an inline selection carries it
|
|
527
|
+
// whether or not the handler listed it.
|
|
528
|
+
let alwaysIncludedSvmBlockFields = ["slot"]
|
|
529
|
+
|
|
530
|
+
let validSvmInstructionFields = Utils.Set.fromArray([
|
|
531
|
+
"args",
|
|
532
|
+
"accounts",
|
|
533
|
+
"accountArguments",
|
|
534
|
+
"programId",
|
|
535
|
+
"data",
|
|
536
|
+
"path",
|
|
537
|
+
"isInner",
|
|
538
|
+
])
|
|
539
|
+
let validSvmTransactionFields = Utils.Set.fromArray([
|
|
540
|
+
"transactionIndex",
|
|
541
|
+
"signature",
|
|
542
|
+
"feePayer",
|
|
543
|
+
"success",
|
|
544
|
+
"err",
|
|
545
|
+
"fee",
|
|
546
|
+
"computeUnitsConsumed",
|
|
547
|
+
"accountKeys",
|
|
548
|
+
"recentBlockhash",
|
|
549
|
+
"version",
|
|
550
|
+
"allSignatures",
|
|
551
|
+
])
|
|
552
|
+
let validSvmAccountActivityFields = Utils.Set.fromArray([
|
|
553
|
+
"address",
|
|
554
|
+
"transactionAccountIndex",
|
|
555
|
+
"isSigner",
|
|
556
|
+
"isWritable",
|
|
557
|
+
"lamports",
|
|
558
|
+
"lamports.pre",
|
|
559
|
+
"lamports.post",
|
|
560
|
+
"token",
|
|
561
|
+
"token.mint",
|
|
562
|
+
"token.owner",
|
|
563
|
+
"token.decimals",
|
|
564
|
+
"token.preAmount",
|
|
565
|
+
"token.postAmount",
|
|
566
|
+
])
|
|
567
|
+
let validSvmBlockFields = Utils.Set.fromArray([
|
|
568
|
+
"slot",
|
|
569
|
+
"time",
|
|
570
|
+
"hash",
|
|
571
|
+
"height",
|
|
572
|
+
"parentSlot",
|
|
573
|
+
"parentHash",
|
|
574
|
+
])
|
|
575
|
+
let validSvmLogFields = Utils.Set.fromArray(["kind", "message"])
|
|
576
|
+
|
|
577
|
+
let resolveSvmInlineFieldSelection = (
|
|
578
|
+
fields: unknown,
|
|
579
|
+
~contractName: string,
|
|
580
|
+
~eventName: string,
|
|
581
|
+
): Internal.fieldSelection => {
|
|
582
|
+
let registration = `the "${eventName}" event registration on contract "${contractName}"`
|
|
583
|
+
validateFieldsShapeOrThrow(
|
|
584
|
+
fields,
|
|
585
|
+
~registration,
|
|
586
|
+
~validKeys=svmSelectionKinds,
|
|
587
|
+
~shapeNoun="instruction, transaction, accountActivity, block and log",
|
|
588
|
+
)
|
|
589
|
+
let accountActivity = selectionList(fields, "accountActivity")
|
|
590
|
+
let log = selectionList(fields, "log")
|
|
591
|
+
let instructionFields = parseFieldsOrThrow(
|
|
592
|
+
selectionList(fields, "instruction"),
|
|
593
|
+
~valid=validSvmInstructionFields,
|
|
594
|
+
~kind="instruction",
|
|
595
|
+
~registration,
|
|
596
|
+
)
|
|
597
|
+
let transactionFields = parseFieldsOrThrow(
|
|
598
|
+
selectionList(fields, "transaction"),
|
|
599
|
+
~valid=validSvmTransactionFields,
|
|
600
|
+
~kind="transaction",
|
|
601
|
+
~registration,
|
|
602
|
+
)
|
|
603
|
+
let accountActivityFields = parseFieldsOrThrow(
|
|
604
|
+
accountActivity,
|
|
605
|
+
~valid=validSvmAccountActivityFields,
|
|
606
|
+
~kind="accountActivity",
|
|
607
|
+
~registration,
|
|
608
|
+
~rejectEmpty=accountActivity->Option.isSome,
|
|
609
|
+
)
|
|
610
|
+
let blockFields = parseFieldsOrThrow(
|
|
611
|
+
selectionList(fields, "block"),
|
|
612
|
+
~valid=validSvmBlockFields,
|
|
613
|
+
~kind="block",
|
|
614
|
+
~registration,
|
|
615
|
+
)
|
|
616
|
+
let logFields = parseFieldsOrThrow(
|
|
617
|
+
log,
|
|
618
|
+
~valid=validSvmLogFields,
|
|
619
|
+
~kind="log",
|
|
620
|
+
~registration,
|
|
621
|
+
~rejectEmpty=log->Option.isSome,
|
|
622
|
+
)
|
|
623
|
+
blockFields->Utils.Set.addMany(alwaysIncludedSvmBlockFields)
|
|
624
|
+
if accountActivityFields->Utils.Set.size > 0 {
|
|
625
|
+
transactionFields->Utils.Set.add("accountActivities")->ignore
|
|
626
|
+
}
|
|
627
|
+
Internal.makeFieldSelection(
|
|
628
|
+
~blockFields,
|
|
629
|
+
~transactionFields,
|
|
630
|
+
~instructionFields,
|
|
631
|
+
~accountActivityFields,
|
|
632
|
+
~logFields,
|
|
633
|
+
~blockMaskFn=Svm.eventBlockFieldMask,
|
|
634
|
+
~transactionMaskFn=Svm.eventTransactionFieldMask,
|
|
635
|
+
)
|
|
636
|
+
}
|
|
504
637
|
|
|
505
638
|
let buildSvmInstructionEventConfig = (
|
|
506
639
|
~contractName: string,
|
|
@@ -508,9 +641,6 @@ let buildSvmInstructionEventConfig = (
|
|
|
508
641
|
~programId: SvmTypes.Pubkey.t,
|
|
509
642
|
~discriminator: option<string>,
|
|
510
643
|
~discriminatorByteLen: int,
|
|
511
|
-
~includeLogs: bool,
|
|
512
|
-
~transactionFields: array<Internal.svmTransactionField>=[],
|
|
513
|
-
~blockFields: array<Internal.svmBlockField>=[],
|
|
514
644
|
~accountFilters: array<Internal.svmAccountFilterGroup>,
|
|
515
645
|
~isInner: option<bool>,
|
|
516
646
|
~accounts: array<string>=[],
|
|
@@ -523,12 +653,8 @@ let buildSvmInstructionEventConfig = (
|
|
|
523
653
|
->(Utils.magic: S.t<JSON.t> => S.t<Internal.eventParams>)
|
|
524
654
|
|
|
525
655
|
let fieldSelection = Internal.makeFieldSelection(
|
|
526
|
-
~blockFields=Utils.Set.fromArray(
|
|
527
|
-
|
|
528
|
-
),
|
|
529
|
-
~transactionFields=Utils.Set.fromArray(transactionFields)->(
|
|
530
|
-
Utils.magic: Utils.Set.t<Internal.svmTransactionField> => Utils.Set.t<string>
|
|
531
|
-
),
|
|
656
|
+
~blockFields=Utils.Set.fromArray(alwaysIncludedSvmBlockFields),
|
|
657
|
+
~transactionFields=Utils.Set.make(),
|
|
532
658
|
~blockMaskFn=Svm.eventBlockFieldMask,
|
|
533
659
|
~transactionMaskFn=Svm.eventTransactionFieldMask,
|
|
534
660
|
)
|
|
@@ -544,7 +670,6 @@ let buildSvmInstructionEventConfig = (
|
|
|
544
670
|
programId,
|
|
545
671
|
discriminator,
|
|
546
672
|
discriminatorByteLen,
|
|
547
|
-
includeLogs,
|
|
548
673
|
fieldSelection,
|
|
549
674
|
accountFilters,
|
|
550
675
|
isInner,
|
|
@@ -561,6 +686,7 @@ let buildSvmOnEventRegistration = (
|
|
|
561
686
|
~isWildcard: bool,
|
|
562
687
|
~handler: option<Internal.handler>,
|
|
563
688
|
~contractRegister: option<Internal.contractRegister>,
|
|
689
|
+
~fieldSelection: option<Internal.fieldSelection>=?,
|
|
564
690
|
~startBlock: option<int>=?,
|
|
565
691
|
): Internal.svmOnEventRegistration => {
|
|
566
692
|
index: -1,
|
|
@@ -571,7 +697,10 @@ let buildSvmOnEventRegistration = (
|
|
|
571
697
|
filterByAddresses: false,
|
|
572
698
|
dependsOnAddresses: Internal.dependsOnAddresses(~isWildcard, ~filterByAddresses=false),
|
|
573
699
|
startBlock,
|
|
574
|
-
fieldSelection:
|
|
700
|
+
fieldSelection: switch fieldSelection {
|
|
701
|
+
| Some(fieldSelection) => fieldSelection
|
|
702
|
+
| None => eventConfig.fieldSelection
|
|
703
|
+
},
|
|
575
704
|
}
|
|
576
705
|
|
|
577
706
|
// ============== Build Fuel event config ==============
|