envio 3.0.0 → 3.0.2-svm-alpha.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/evm.schema.json +8 -8
- package/fuel.schema.json +12 -12
- package/index.d.ts +155 -1
- package/package.json +6 -7
- package/src/ChainFetcher.res +25 -1
- package/src/ChainFetcher.res.mjs +19 -1
- package/src/Config.res +145 -10
- package/src/Config.res.mjs +56 -16
- package/src/Core.res +32 -0
- package/src/Env.res.mjs +1 -2
- package/src/Envio.res +94 -0
- package/src/EventConfigBuilder.res +50 -0
- package/src/EventConfigBuilder.res.mjs +31 -0
- package/src/HandlerLoader.res +12 -1
- package/src/HandlerLoader.res.mjs +6 -1
- package/src/InMemoryTable.res +20 -24
- package/src/InMemoryTable.res.mjs +3 -19
- package/src/Internal.res +38 -0
- package/src/Main.res +53 -1
- package/src/Main.res.mjs +32 -0
- package/src/SimulateItems.res +23 -10
- package/src/SimulateItems.res.mjs +21 -6
- package/src/SvmTypes.res +9 -0
- package/src/SvmTypes.res.mjs +14 -0
- package/src/sources/EventRouter.res +65 -0
- package/src/sources/EventRouter.res.mjs +43 -0
- package/src/sources/HyperSyncClient.res +30 -157
- package/src/sources/HyperSyncClient.res.mjs +20 -6
- package/src/sources/HyperSyncSolanaClient.res +227 -0
- package/src/sources/HyperSyncSolanaClient.res.mjs +25 -0
- package/src/sources/HyperSyncSolanaSource.res +515 -0
- package/src/sources/HyperSyncSolanaSource.res.mjs +441 -0
- package/src/sources/HyperSyncSource.res +5 -8
- package/src/sources/HyperSyncSource.res.mjs +1 -8
- package/src/sources/RpcSource.res.mjs +1 -1
- package/src/sources/Svm.res +2 -2
- package/src/sources/Svm.res.mjs +3 -2
- package/src/tui/Tui.res +9 -2
- package/src/tui/Tui.res.mjs +19 -4
- package/src/tui/components/TuiData.res +3 -0
- package/svm.schema.json +345 -4
package/src/Config.res.mjs
CHANGED
|
@@ -113,6 +113,26 @@ let publicConfigChainSchema = S$RescriptSchema.schema(s => ({
|
|
|
113
113
|
contracts: s.m(S$RescriptSchema.option(S$RescriptSchema.dict(chainContractSchema)))
|
|
114
114
|
}));
|
|
115
115
|
|
|
116
|
+
let svmEventDescriptorSchema = S$RescriptSchema.schema(s => ({
|
|
117
|
+
discriminator: s.m(S$RescriptSchema.option(S$RescriptSchema.string)),
|
|
118
|
+
discriminatorByteLen: s.m(S$RescriptSchema.int),
|
|
119
|
+
includeTransaction: s.m(S$RescriptSchema.bool),
|
|
120
|
+
includeLogs: s.m(S$RescriptSchema.bool),
|
|
121
|
+
accountFilters: s.m(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.schema(s => ({
|
|
122
|
+
position: s.m(S$RescriptSchema.int),
|
|
123
|
+
values: s.m(S$RescriptSchema.array(S$RescriptSchema.string))
|
|
124
|
+
}))))),
|
|
125
|
+
isInner: s.m(S$RescriptSchema.option(S$RescriptSchema.bool)),
|
|
126
|
+
accounts: s.m(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.string))),
|
|
127
|
+
args: s.m(S$RescriptSchema.option(S$RescriptSchema.json(false)))
|
|
128
|
+
}));
|
|
129
|
+
|
|
130
|
+
let svmAbiSchema = S$RescriptSchema.schema(s => ({
|
|
131
|
+
programId: s.m(S$RescriptSchema.string),
|
|
132
|
+
definedTypes: s.m(S$RescriptSchema.json(false)),
|
|
133
|
+
source: s.m(S$RescriptSchema.string)
|
|
134
|
+
}));
|
|
135
|
+
|
|
116
136
|
let contractEventItemSchema = S$RescriptSchema.schema(s => ({
|
|
117
137
|
event: s.m(S$RescriptSchema.string),
|
|
118
138
|
name: s.m(S$RescriptSchema.string),
|
|
@@ -120,18 +140,21 @@ let contractEventItemSchema = S$RescriptSchema.schema(s => ({
|
|
|
120
140
|
params: s.m(S$RescriptSchema.option(S$RescriptSchema.array(EventConfigBuilder.eventParamSchema))),
|
|
121
141
|
kind: s.m(S$RescriptSchema.option(S$RescriptSchema.string)),
|
|
122
142
|
blockFields: s.m(S$RescriptSchema.option(S$RescriptSchema.array(Internal.evmBlockFieldSchema))),
|
|
123
|
-
transactionFields: s.m(S$RescriptSchema.option(S$RescriptSchema.array(Internal.evmTransactionFieldSchema)))
|
|
143
|
+
transactionFields: s.m(S$RescriptSchema.option(S$RescriptSchema.array(Internal.evmTransactionFieldSchema))),
|
|
144
|
+
svm: s.m(S$RescriptSchema.option(svmEventDescriptorSchema))
|
|
124
145
|
}));
|
|
125
146
|
|
|
126
147
|
let contractConfigSchema = S$RescriptSchema.schema(s => ({
|
|
127
148
|
abi: s.m(S$RescriptSchema.json(false)),
|
|
128
149
|
handler: s.m(S$RescriptSchema.option(S$RescriptSchema.string)),
|
|
129
|
-
events: s.m(S$RescriptSchema.option(S$RescriptSchema.array(contractEventItemSchema)))
|
|
150
|
+
events: s.m(S$RescriptSchema.option(S$RescriptSchema.array(contractEventItemSchema))),
|
|
151
|
+
svmAbi: s.m(S$RescriptSchema.option(svmAbiSchema))
|
|
130
152
|
}));
|
|
131
153
|
|
|
132
154
|
let publicConfigEcosystemSchema = S$RescriptSchema.schema(s => ({
|
|
133
155
|
chains: s.m(S$RescriptSchema.dict(publicConfigChainSchema)),
|
|
134
|
-
contracts: s.m(S$RescriptSchema.option(S$RescriptSchema.dict(contractConfigSchema)))
|
|
156
|
+
contracts: s.m(S$RescriptSchema.option(S$RescriptSchema.dict(contractConfigSchema))),
|
|
157
|
+
programs: s.m(S$RescriptSchema.option(S$RescriptSchema.dict(contractConfigSchema)))
|
|
135
158
|
}));
|
|
136
159
|
|
|
137
160
|
let publicConfigEvmSchema = S$RescriptSchema.schema(s => ({
|
|
@@ -419,6 +442,7 @@ function fromPublic(publicConfigJson) {
|
|
|
419
442
|
let lowercaseAddresses = evm !== undefined ? Stdlib_Option.getOr(Primitive_option.valFromOption(evm).addressFormat, "checksum") === "lowercase" : false;
|
|
420
443
|
let match$4 = publicConfig.evm;
|
|
421
444
|
let match$5 = publicConfig.fuel;
|
|
445
|
+
let match$6 = publicConfig.svm;
|
|
422
446
|
let publicContractsConfig;
|
|
423
447
|
switch (ecosystemName) {
|
|
424
448
|
case "evm" :
|
|
@@ -428,25 +452,25 @@ function fromPublic(publicConfigJson) {
|
|
|
428
452
|
publicContractsConfig = match$5 !== undefined ? Primitive_option.valFromOption(match$5).contracts : undefined;
|
|
429
453
|
break;
|
|
430
454
|
case "svm" :
|
|
431
|
-
publicContractsConfig = undefined;
|
|
455
|
+
publicContractsConfig = match$6 !== undefined ? Primitive_option.valFromOption(match$6).programs : undefined;
|
|
432
456
|
break;
|
|
433
457
|
}
|
|
434
458
|
let evm$1 = publicConfig.evm;
|
|
435
|
-
let match$
|
|
459
|
+
let match$7;
|
|
436
460
|
if (evm$1 !== undefined) {
|
|
437
461
|
let evm$2 = Primitive_option.valFromOption(evm$1);
|
|
438
|
-
match$
|
|
462
|
+
match$7 = [
|
|
439
463
|
new Set(EventConfigBuilder.alwaysIncludedBlockFields.concat(Stdlib_Option.getOr(evm$2.globalBlockFields, []))),
|
|
440
464
|
new Set(Stdlib_Option.getOr(evm$2.globalTransactionFields, []))
|
|
441
465
|
];
|
|
442
466
|
} else {
|
|
443
|
-
match$
|
|
467
|
+
match$7 = [
|
|
444
468
|
new Set(EventConfigBuilder.alwaysIncludedBlockFields),
|
|
445
469
|
new Set()
|
|
446
470
|
];
|
|
447
471
|
}
|
|
448
|
-
let globalTransactionFieldsSet = match$
|
|
449
|
-
let globalBlockFieldsSet = match$
|
|
472
|
+
let globalTransactionFieldsSet = match$7[1];
|
|
473
|
+
let globalBlockFieldsSet = match$7[0];
|
|
450
474
|
let contractDataByName = {};
|
|
451
475
|
if (publicContractsConfig !== undefined) {
|
|
452
476
|
Object.entries(publicContractsConfig).forEach(param => {
|
|
@@ -458,11 +482,13 @@ function fromPublic(publicConfigJson) {
|
|
|
458
482
|
contractDataByName[capitalizedName] = {
|
|
459
483
|
abi: abi,
|
|
460
484
|
eventSignatures: eventSignatures,
|
|
461
|
-
events: contractConfig.events
|
|
485
|
+
events: contractConfig.events,
|
|
486
|
+
svmAbi: contractConfig.svmAbi
|
|
462
487
|
};
|
|
463
488
|
});
|
|
464
489
|
}
|
|
465
|
-
let buildContractEvents = (contractName, events, abi, chainId, startBlock) => {
|
|
490
|
+
let buildContractEvents = (contractName, events, abi, chainId, startBlock, addresses, svmDefinedTypesOpt) => {
|
|
491
|
+
let svmDefinedTypes = svmDefinedTypesOpt !== undefined ? svmDefinedTypesOpt : null;
|
|
466
492
|
if (events !== undefined) {
|
|
467
493
|
return events.map(eventItem => {
|
|
468
494
|
let eventName = eventItem.name;
|
|
@@ -470,17 +496,27 @@ function fromPublic(publicConfigJson) {
|
|
|
470
496
|
let params = Stdlib_Option.getOr(eventItem.params, []);
|
|
471
497
|
let kind = eventItem.kind;
|
|
472
498
|
switch (ecosystemName) {
|
|
499
|
+
case "evm" :
|
|
500
|
+
return EventConfigBuilder.buildEvmEventConfig(contractName, eventName, sighash, params, false, undefined, undefined, undefined, chainId, ecosystem.onEventBlockFilterSchema, eventItem.blockFields, eventItem.transactionFields, startBlock, Primitive_option.some(globalBlockFieldsSet), Primitive_option.some(globalTransactionFieldsSet));
|
|
473
501
|
case "fuel" :
|
|
474
502
|
if (kind !== undefined) {
|
|
475
503
|
return EventConfigBuilder.buildFuelEventConfig(contractName, eventName, kind, sighash, abi, false, undefined, undefined, startBlock);
|
|
476
504
|
} else {
|
|
477
505
|
return Stdlib_JsError.throwWithMessage(`Fuel event ` + contractName + `.` + eventName + ` is missing "kind" in internal config`);
|
|
478
506
|
}
|
|
479
|
-
case "evm" :
|
|
480
507
|
case "svm" :
|
|
481
|
-
|
|
508
|
+
let len = addresses.length;
|
|
509
|
+
let programId = len !== 1 ? (
|
|
510
|
+
len !== 0 ? Stdlib_JsError.throwWithMessage(`SVM program ` + contractName + ` on chain ` + chainId.toString() + ` has multiple addresses; a program is uniquely identified by a single program_id`) : Stdlib_JsError.throwWithMessage(`SVM program ` + contractName + ` on chain ` + chainId.toString() + ` is missing a program_id`)
|
|
511
|
+
) : addresses[0];
|
|
512
|
+
let s = eventItem.svm;
|
|
513
|
+
let svm = s !== undefined ? Primitive_option.valFromOption(s) : Stdlib_JsError.throwWithMessage(`SVM instruction ` + contractName + `.` + eventName + ` is missing the "svm" descriptor in internal config`);
|
|
514
|
+
let accountFilters = Stdlib_Option.getOr(svm.accountFilters, []).map(af => ({
|
|
515
|
+
position: af.position,
|
|
516
|
+
values: af.values
|
|
517
|
+
}));
|
|
518
|
+
return EventConfigBuilder.buildSvmInstructionEventConfig(contractName, eventName, programId, svm.discriminator, svm.discriminatorByteLen, svm.includeTransaction, svm.includeLogs, accountFilters, svm.isInner, false, undefined, undefined, Stdlib_Option.getOr(svm.accounts, []), Stdlib_Option.getOr(svm.args, null), svmDefinedTypes, startBlock);
|
|
482
519
|
}
|
|
483
|
-
return EventConfigBuilder.buildEvmEventConfig(contractName, eventName, sighash, params, false, undefined, undefined, undefined, chainId, ecosystem.onEventBlockFilterSchema, eventItem.blockFields, eventItem.transactionFields, startBlock, Primitive_option.some(globalBlockFieldsSet), Primitive_option.some(globalTransactionFieldsSet));
|
|
484
520
|
});
|
|
485
521
|
} else {
|
|
486
522
|
return [];
|
|
@@ -517,9 +553,10 @@ function fromPublic(publicConfigJson) {
|
|
|
517
553
|
let contractData = param[1];
|
|
518
554
|
let capitalizedName = param[0];
|
|
519
555
|
let chainContract = chainContracts[capitalizedName];
|
|
520
|
-
let
|
|
556
|
+
let rawAddresses = Stdlib_Option.getOr(Stdlib_Option.flatMap(chainContract, cc => cc.addresses), []);
|
|
557
|
+
let addresses = rawAddresses.map(parseAddress);
|
|
521
558
|
let startBlock = Stdlib_Option.flatMap(chainContract, cc => cc.startBlock);
|
|
522
|
-
let events = buildContractEvents(capitalizedName, contractData.events, contractData.abi, chainId, startBlock);
|
|
559
|
+
let events = buildContractEvents(capitalizedName, contractData.events, contractData.abi, chainId, startBlock, rawAddresses, Stdlib_Option.getOr(Stdlib_Option.map(contractData.svmAbi, a => a.definedTypes), null));
|
|
523
560
|
return {
|
|
524
561
|
name: capitalizedName,
|
|
525
562
|
abi: contractData.abi,
|
|
@@ -576,6 +613,7 @@ function fromPublic(publicConfigJson) {
|
|
|
576
613
|
let rpc = publicChainConfig.rpc;
|
|
577
614
|
sourceConfig = rpc !== undefined ? ({
|
|
578
615
|
TAG: "SvmSourceConfig",
|
|
616
|
+
hypersync: publicChainConfig.hypersync,
|
|
579
617
|
rpc: rpc
|
|
580
618
|
}) : Stdlib_JsError.throwWithMessage(`Chain ` + chainName + ` is missing rpc endpoint in config`);
|
|
581
619
|
break;
|
|
@@ -862,6 +900,8 @@ export {
|
|
|
862
900
|
rpcConfigSchema,
|
|
863
901
|
chainContractSchema,
|
|
864
902
|
publicConfigChainSchema,
|
|
903
|
+
svmEventDescriptorSchema,
|
|
904
|
+
svmAbiSchema,
|
|
865
905
|
contractEventItemSchema,
|
|
866
906
|
contractConfigSchema,
|
|
867
907
|
publicConfigEcosystemSchema,
|
package/src/Core.res
CHANGED
|
@@ -4,9 +4,41 @@
|
|
|
4
4
|
|
|
5
5
|
// NAPI encodes Rust `Option<T>` as `null | T` (never `undefined`), so the
|
|
6
6
|
// tighter `Null.t` captures the exact boundary shape.
|
|
7
|
+
//
|
|
8
|
+
// Opaque carriers for the NAPI class constructors. Static factories
|
|
9
|
+
// (`newWithAgent`, `fromConfig`, `fromSignatures`) hang off these via `@send`
|
|
10
|
+
// in `HyperSyncClient.res` / `HyperSyncSolanaClient.res`.
|
|
11
|
+
type hypersyncClientCtor
|
|
12
|
+
type hypersyncSolanaClientCtor
|
|
13
|
+
type decoderCtor
|
|
14
|
+
|
|
15
|
+
/// JS shape of one decoded instruction. Mirrors `DecodedInstructionJson` in
|
|
16
|
+
/// `packages/cli/src/hypersync_source_svm/decoder.rs`. The `argsJson` /
|
|
17
|
+
/// `accountsJson` fields are stringified to side-step napi-rs's lack of
|
|
18
|
+
/// native `serde_json::Value` passthrough; callers `JSON.parse` once.
|
|
19
|
+
type svmDecodedInstruction = {
|
|
20
|
+
name: string,
|
|
21
|
+
argsJson: string,
|
|
22
|
+
accountsJson: string,
|
|
23
|
+
extraAccounts: array<string>,
|
|
24
|
+
}
|
|
25
|
+
|
|
7
26
|
type addon = {
|
|
8
27
|
getConfigJson: (~configPath: Null.t<string>, ~directory: Null.t<string>) => string,
|
|
9
28
|
runCli: (~args: array<string>, ~envioPackageDir: Null.t<string>) => promise<Null.t<string>>,
|
|
29
|
+
@as("HypersyncClient")
|
|
30
|
+
hypersyncClient: hypersyncClientCtor,
|
|
31
|
+
@as("HypersyncSolanaClient")
|
|
32
|
+
hypersyncSolanaClient: hypersyncSolanaClientCtor,
|
|
33
|
+
@as("Decoder")
|
|
34
|
+
decoder: decoderCtor,
|
|
35
|
+
setLogLevel: string => unit,
|
|
36
|
+
registerProgramSchema: (~descriptorJson: string) => int,
|
|
37
|
+
decodeInstruction: (
|
|
38
|
+
~schemaHandle: int,
|
|
39
|
+
~dataHex: string,
|
|
40
|
+
~accounts: array<string>,
|
|
41
|
+
) => Null.t<svmDecodedInstruction>,
|
|
10
42
|
}
|
|
11
43
|
|
|
12
44
|
@module("node:module") external createRequire: string => {..} = "createRequire"
|
package/src/Env.res.mjs
CHANGED
|
@@ -7,7 +7,6 @@ import * as Belt_Option from "@rescript/runtime/lib/es6/Belt_Option.js";
|
|
|
7
7
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
8
8
|
import * as HyperSyncClient from "./sources/HyperSyncClient.res.mjs";
|
|
9
9
|
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
10
|
-
import * as HypersyncClient from "@envio-dev/hypersync-client";
|
|
11
10
|
|
|
12
11
|
import 'dotenv/config'
|
|
13
12
|
;
|
|
@@ -62,7 +61,7 @@ let hypersyncClientEnableQueryCaching = EnvSafe.get(envSafe, "ENVIO_HYPERSYNC_CL
|
|
|
62
61
|
|
|
63
62
|
let hypersyncLogLevel = EnvSafe.get(envSafe, "ENVIO_HYPERSYNC_LOG_LEVEL", HyperSyncClient.logLevelSchema, undefined, "info", undefined, undefined);
|
|
64
63
|
|
|
65
|
-
|
|
64
|
+
HyperSyncClient.setLogLevel(hypersyncLogLevel);
|
|
66
65
|
|
|
67
66
|
let logStrategy = EnvSafe.get(envSafe, "LOG_STRATEGY", S$RescriptSchema.$$enum([
|
|
68
67
|
"ecs-file",
|
package/src/Envio.res
CHANGED
|
@@ -20,6 +20,100 @@ type svmOnSlotArgs<'context> = {
|
|
|
20
20
|
context: 'context,
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/** Borsh-decoded instruction view. Present whenever a `ProgramSchema` was
|
|
24
|
+
attached to the program (bundled schema, Anchor IDL, or hand-written YAML
|
|
25
|
+
`accounts`/`args`). Absent (`None`) when no schema applied or the
|
|
26
|
+
discriminator didn't match any registered instruction. */
|
|
27
|
+
type svmDecodedInstruction = {
|
|
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>,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type svmInstruction = {
|
|
43
|
+
programId: SvmTypes.Pubkey.t,
|
|
44
|
+
/** Raw instruction bytes as `0x`-prefixed hex. */
|
|
45
|
+
data: string,
|
|
46
|
+
accounts: array<SvmTypes.Pubkey.t>,
|
|
47
|
+
/** Path through the call tree: `[outerIndex]` for top-level instructions,
|
|
48
|
+
appended child indices for inner CPI calls. */
|
|
49
|
+
instructionAddress: array<int>,
|
|
50
|
+
isInner: bool,
|
|
51
|
+
/** Discriminator prefixes pre-extracted by HyperSync. Each is `Some` only
|
|
52
|
+
when the underlying instruction is at least that long. */
|
|
53
|
+
d1?: string,
|
|
54
|
+
d2?: string,
|
|
55
|
+
d4?: string,
|
|
56
|
+
d8?: string,
|
|
57
|
+
/** Borsh-decoded view. See [[svmDecodedInstruction]]. */
|
|
58
|
+
decoded?: svmDecodedInstruction,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
type svmTransaction = {
|
|
62
|
+
signatures: array<string>,
|
|
63
|
+
feePayer?: SvmTypes.Pubkey.t,
|
|
64
|
+
success?: bool,
|
|
65
|
+
err?: string,
|
|
66
|
+
fee?: bigint,
|
|
67
|
+
computeUnitsConsumed?: bigint,
|
|
68
|
+
accountKeys: array<SvmTypes.Pubkey.t>,
|
|
69
|
+
recentBlockhash?: string,
|
|
70
|
+
version?: string,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
type svmLog = {
|
|
74
|
+
kind: string,
|
|
75
|
+
message: string,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Inner block record on `svmInstructionEvent`. Field names follow EVM/Fuel
|
|
79
|
+
(`height`, `time`, `hash`) so the shared `Ecosystem.t` getters in
|
|
80
|
+
`Svm.res` work uniformly across ecosystems — `height` carries the slot. */
|
|
81
|
+
type svmInstructionEventBlock = {
|
|
82
|
+
/** Slot number. Named `height` so the shared ecosystem getter reads it. */
|
|
83
|
+
height: int,
|
|
84
|
+
/** Unix block time (seconds). `0` when HyperSync didn't return a block
|
|
85
|
+
for this instruction's slot. */
|
|
86
|
+
time: int,
|
|
87
|
+
/** Block hash. Currently always empty — populated by the future
|
|
88
|
+
reorg-guard `queryBlockHash(slot)` route. */
|
|
89
|
+
hash: string,
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** The per-instruction payload handlers receive on `.event`. Mirrors the
|
|
93
|
+
EVM `type event` shape inside generated per-event modules. */
|
|
94
|
+
type svmInstructionEvent = {
|
|
95
|
+
contractName: string,
|
|
96
|
+
eventName: string,
|
|
97
|
+
instruction: svmInstruction,
|
|
98
|
+
/** Parent transaction. `None` when the per-instruction
|
|
99
|
+
`include_transaction` flag is `false`. */
|
|
100
|
+
transaction: option<svmTransaction>,
|
|
101
|
+
/** Program log entries scoped to this instruction. `None` when the
|
|
102
|
+
per-instruction `include_logs` flag is `false`. */
|
|
103
|
+
logs: option<array<svmLog>>,
|
|
104
|
+
/** Convenience alias for `block.height`. */
|
|
105
|
+
slot: int,
|
|
106
|
+
/** Convenience alias for `block.time`. */
|
|
107
|
+
blockTime: option<int>,
|
|
108
|
+
block: svmInstructionEventBlock,
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Arguments passed to handlers registered via `indexer.onInstruction`. */
|
|
112
|
+
type svmOnInstructionArgs<'context> = {
|
|
113
|
+
event: svmInstructionEvent,
|
|
114
|
+
context: 'context,
|
|
115
|
+
}
|
|
116
|
+
|
|
23
117
|
// Internal-only type for the `indexer.onBlock` (and SVM `onSlot`) `where`
|
|
24
118
|
// callback argument. The canonical TypeScript shape lives in
|
|
25
119
|
// `packages/envio/index.d.ts`; the ReScript declaration here is free to
|
|
@@ -484,6 +484,56 @@ let buildEvmEventConfig = (
|
|
|
484
484
|
|
|
485
485
|
// ============== Build Fuel event config ==============
|
|
486
486
|
|
|
487
|
+
let buildSvmInstructionEventConfig = (
|
|
488
|
+
~contractName: string,
|
|
489
|
+
~instructionName: string,
|
|
490
|
+
~programId: SvmTypes.Pubkey.t,
|
|
491
|
+
~discriminator: option<string>,
|
|
492
|
+
~discriminatorByteLen: int,
|
|
493
|
+
~includeTransaction: bool,
|
|
494
|
+
~includeLogs: bool,
|
|
495
|
+
~accountFilters: array<Internal.svmAccountFilter>,
|
|
496
|
+
~isInner: option<bool>,
|
|
497
|
+
~isWildcard: bool,
|
|
498
|
+
~handler: option<Internal.handler>,
|
|
499
|
+
~contractRegister: option<Internal.contractRegister>,
|
|
500
|
+
~accounts: array<string>=[],
|
|
501
|
+
~args: JSON.t=JSON.Null,
|
|
502
|
+
~definedTypes: JSON.t=JSON.Null,
|
|
503
|
+
~startBlock: option<int>=?,
|
|
504
|
+
): Internal.svmInstructionEventConfig => {
|
|
505
|
+
let paramsSchema =
|
|
506
|
+
S.json(~validate=false)
|
|
507
|
+
->Utils.Schema.coerceToJsonPgType
|
|
508
|
+
->(Utils.magic: S.t<JSON.t> => S.t<Internal.eventParams>)
|
|
509
|
+
{
|
|
510
|
+
id: switch discriminator {
|
|
511
|
+
| Some(d) => d
|
|
512
|
+
| None => "none"
|
|
513
|
+
},
|
|
514
|
+
name: instructionName,
|
|
515
|
+
contractName,
|
|
516
|
+
isWildcard,
|
|
517
|
+
handler,
|
|
518
|
+
contractRegister,
|
|
519
|
+
paramsRawEventSchema: paramsSchema,
|
|
520
|
+
simulateParamsSchema: paramsSchema,
|
|
521
|
+
filterByAddresses: false,
|
|
522
|
+
dependsOnAddresses: !isWildcard,
|
|
523
|
+
startBlock,
|
|
524
|
+
programId,
|
|
525
|
+
discriminator,
|
|
526
|
+
discriminatorByteLen,
|
|
527
|
+
includeTransaction,
|
|
528
|
+
includeLogs,
|
|
529
|
+
accountFilters,
|
|
530
|
+
isInner,
|
|
531
|
+
accounts,
|
|
532
|
+
args,
|
|
533
|
+
definedTypes,
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
487
537
|
let buildFuelEventConfig = (
|
|
488
538
|
~contractName: string,
|
|
489
539
|
~eventName: string,
|
|
@@ -350,6 +350,36 @@ function buildEvmEventConfig(contractName, eventName, sighash, params, isWildcar
|
|
|
350
350
|
};
|
|
351
351
|
}
|
|
352
352
|
|
|
353
|
+
function buildSvmInstructionEventConfig(contractName, instructionName, programId, discriminator, discriminatorByteLen, includeTransaction, includeLogs, accountFilters, isInner, isWildcard, handler, contractRegister, accountsOpt, argsOpt, definedTypesOpt, startBlock) {
|
|
354
|
+
let accounts = accountsOpt !== undefined ? accountsOpt : [];
|
|
355
|
+
let args = argsOpt !== undefined ? argsOpt : null;
|
|
356
|
+
let definedTypes = definedTypesOpt !== undefined ? definedTypesOpt : null;
|
|
357
|
+
let paramsSchema = Utils.Schema.coerceToJsonPgType(S$RescriptSchema.json(false));
|
|
358
|
+
return {
|
|
359
|
+
id: discriminator !== undefined ? discriminator : "none",
|
|
360
|
+
name: instructionName,
|
|
361
|
+
contractName: contractName,
|
|
362
|
+
isWildcard: isWildcard,
|
|
363
|
+
filterByAddresses: false,
|
|
364
|
+
dependsOnAddresses: !isWildcard,
|
|
365
|
+
handler: handler,
|
|
366
|
+
contractRegister: contractRegister,
|
|
367
|
+
paramsRawEventSchema: paramsSchema,
|
|
368
|
+
simulateParamsSchema: paramsSchema,
|
|
369
|
+
startBlock: startBlock,
|
|
370
|
+
programId: programId,
|
|
371
|
+
discriminator: discriminator,
|
|
372
|
+
discriminatorByteLen: discriminatorByteLen,
|
|
373
|
+
includeTransaction: includeTransaction,
|
|
374
|
+
includeLogs: includeLogs,
|
|
375
|
+
accountFilters: accountFilters,
|
|
376
|
+
isInner: isInner,
|
|
377
|
+
accounts: accounts,
|
|
378
|
+
args: args,
|
|
379
|
+
definedTypes: definedTypes
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
353
383
|
function buildFuelEventConfig(contractName, eventName, kind, sighash, rawAbi, isWildcard, handler, contractRegister, startBlock) {
|
|
354
384
|
let fuelKind;
|
|
355
385
|
switch (kind) {
|
|
@@ -428,6 +458,7 @@ export {
|
|
|
428
458
|
alwaysIncludedBlockFields,
|
|
429
459
|
resolveFieldSelection,
|
|
430
460
|
buildEvmEventConfig,
|
|
461
|
+
buildSvmInstructionEventConfig,
|
|
431
462
|
buildFuelEventConfig,
|
|
432
463
|
}
|
|
433
464
|
/* eventParamComponentSchema Not a pure module */
|
package/src/HandlerLoader.res
CHANGED
|
@@ -142,7 +142,18 @@ let applyRegistrations = (~config: Config.t): Config.t => {
|
|
|
142
142
|
dependsOnAddresses: Internal.dependsOnAddresses(~isWildcard, ~filterByAddresses),
|
|
143
143
|
} :> Internal.eventConfig)
|
|
144
144
|
| Svm =>
|
|
145
|
-
|
|
145
|
+
let svmEv =
|
|
146
|
+
ev->(Utils.magic: Internal.eventConfig => Internal.svmInstructionEventConfig)
|
|
147
|
+
({
|
|
148
|
+
...svmEv,
|
|
149
|
+
isWildcard,
|
|
150
|
+
handler,
|
|
151
|
+
contractRegister,
|
|
152
|
+
dependsOnAddresses: Internal.dependsOnAddresses(
|
|
153
|
+
~isWildcard,
|
|
154
|
+
~filterByAddresses=false,
|
|
155
|
+
),
|
|
156
|
+
} :> Internal.eventConfig)
|
|
146
157
|
}
|
|
147
158
|
},
|
|
148
159
|
)
|
|
@@ -109,7 +109,12 @@ function applyRegistrations(config) {
|
|
|
109
109
|
kind: ev.kind
|
|
110
110
|
};
|
|
111
111
|
case "svm" :
|
|
112
|
-
|
|
112
|
+
let newrecord = {...ev};
|
|
113
|
+
newrecord.contractRegister = contractRegister;
|
|
114
|
+
newrecord.handler = handler;
|
|
115
|
+
newrecord.dependsOnAddresses = Internal.dependsOnAddresses(isWildcard, false);
|
|
116
|
+
newrecord.isWildcard = isWildcard;
|
|
117
|
+
return newrecord;
|
|
113
118
|
}
|
|
114
119
|
});
|
|
115
120
|
return {
|
package/src/InMemoryTable.res
CHANGED
|
@@ -71,7 +71,6 @@ module Entity = {
|
|
|
71
71
|
fieldNameIndices: make(~hash=TableIndices.Index.getFieldName),
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
exception UndefinedKey(string)
|
|
75
74
|
let updateIndices = (
|
|
76
75
|
self: t<'entity>,
|
|
77
76
|
~entity: 'entity,
|
|
@@ -83,8 +82,7 @@ module Entity = {
|
|
|
83
82
|
let fieldValue =
|
|
84
83
|
entity
|
|
85
84
|
->(Utils.magic: 'entity => dict<TableIndices.FieldValue.t>)
|
|
86
|
-
->Dict.
|
|
87
|
-
->Option.getUnsafe
|
|
85
|
+
->Dict.getUnsafe(fieldName)
|
|
88
86
|
if !(index->TableIndices.Index.evaluate(~fieldName, ~fieldValue)) {
|
|
89
87
|
entityIndices->Utils.Set.delete(index)->ignore
|
|
90
88
|
}
|
|
@@ -93,27 +91,25 @@ module Entity = {
|
|
|
93
91
|
self.fieldNameIndices.dict
|
|
94
92
|
->Dict.keysToArray
|
|
95
93
|
->Array.forEach(fieldName => {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
->
|
|
103
|
-
->
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
)
|
|
116
|
-
}
|
|
94
|
+
let indices = self.fieldNameIndices.dict->Dict.getUnsafe(fieldName)
|
|
95
|
+
// A missing key reads as `undefined`, which matches the `None` arm of
|
|
96
|
+
// `FieldValue.t` (`option<...>`). Mirror `addEmptyIndex` so nullable
|
|
97
|
+
// FK columns that were omitted on the set entity don't crash.
|
|
98
|
+
let fieldValue =
|
|
99
|
+
entity
|
|
100
|
+
->(Utils.magic: 'entity => dict<TableIndices.FieldValue.t>)
|
|
101
|
+
->Dict.getUnsafe(fieldName)
|
|
102
|
+
indices
|
|
103
|
+
->values
|
|
104
|
+
->Array.forEach(((index, relatedEntityIds)) => {
|
|
105
|
+
if index->TableIndices.Index.evaluate(~fieldName, ~fieldValue) {
|
|
106
|
+
//Add entity id to indices and add index to entity indicies
|
|
107
|
+
relatedEntityIds->Utils.Set.add(getEntityIdUnsafe(entity))->ignore
|
|
108
|
+
entityIndices->Utils.Set.add(index)->ignore
|
|
109
|
+
} else {
|
|
110
|
+
relatedEntityIds->Utils.Set.delete(getEntityIdUnsafe(entity))->ignore
|
|
111
|
+
}
|
|
112
|
+
})
|
|
117
113
|
})
|
|
118
114
|
}
|
|
119
115
|
|
|
@@ -90,8 +90,6 @@ function make$1() {
|
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
let UndefinedKey = /* @__PURE__ */Primitive_exceptions.create("InMemoryTable.Entity.UndefinedKey");
|
|
94
|
-
|
|
95
93
|
function updateIndices(self, entity, entityIndices) {
|
|
96
94
|
entityIndices.forEach(index => {
|
|
97
95
|
let fieldName = TableIndices.Index.getFieldName(index);
|
|
@@ -102,22 +100,9 @@ function updateIndices(self, entity, entityIndices) {
|
|
|
102
100
|
}
|
|
103
101
|
});
|
|
104
102
|
Object.keys(self.fieldNameIndices.dict).forEach(fieldName => {
|
|
105
|
-
let
|
|
106
|
-
let
|
|
107
|
-
|
|
108
|
-
return ErrorHandling.mkLogAndRaise(undefined, "Expected field name to exist on the referenced index and the provided entity", {
|
|
109
|
-
RE_EXN_ID: UndefinedKey,
|
|
110
|
-
_1: fieldName
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
if (match$1 === undefined) {
|
|
114
|
-
return ErrorHandling.mkLogAndRaise(undefined, "Expected field name to exist on the referenced index and the provided entity", {
|
|
115
|
-
RE_EXN_ID: UndefinedKey,
|
|
116
|
-
_1: fieldName
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
let fieldValue = Primitive_option.valFromOption(match);
|
|
120
|
-
Object.values(match$1.dict).forEach(param => {
|
|
103
|
+
let indices = self.fieldNameIndices.dict[fieldName];
|
|
104
|
+
let fieldValue = entity[fieldName];
|
|
105
|
+
Object.values(indices.dict).forEach(param => {
|
|
121
106
|
let relatedEntityIds = param[1];
|
|
122
107
|
let index = param[0];
|
|
123
108
|
if (TableIndices.Index.evaluate(index, fieldName, fieldValue)) {
|
|
@@ -333,7 +318,6 @@ let Entity = {
|
|
|
333
318
|
getEntityIdUnsafe: getEntityIdUnsafe,
|
|
334
319
|
makeIndicesSerializedToValue: makeIndicesSerializedToValue,
|
|
335
320
|
make: make$1,
|
|
336
|
-
UndefinedKey: UndefinedKey,
|
|
337
321
|
updateIndices: updateIndices,
|
|
338
322
|
deleteEntityFromIndices: deleteEntityFromIndices,
|
|
339
323
|
initValue: initValue,
|
package/src/Internal.res
CHANGED
|
@@ -422,6 +422,44 @@ type evmContractConfig = {
|
|
|
422
422
|
events: array<evmEventConfig>,
|
|
423
423
|
}
|
|
424
424
|
|
|
425
|
+
type svmAccountFilter = {
|
|
426
|
+
position: int,
|
|
427
|
+
values: array<SvmTypes.Pubkey.t>,
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
type svmInstructionEventConfig = {
|
|
431
|
+
...eventConfig,
|
|
432
|
+
/** Base58 Solana program id this instruction belongs to. */
|
|
433
|
+
programId: SvmTypes.Pubkey.t,
|
|
434
|
+
/** Hex-encoded discriminator. `None` matches every instruction in the program. */
|
|
435
|
+
discriminator: option<string>,
|
|
436
|
+
/** Length of the discriminator in bytes (0 / 1 / 2 / 4 / 8). Drives the
|
|
437
|
+
`dN` selector at query time and the dispatch-key precomputation in the
|
|
438
|
+
router. */
|
|
439
|
+
discriminatorByteLen: int,
|
|
440
|
+
includeTransaction: bool,
|
|
441
|
+
includeLogs: bool,
|
|
442
|
+
accountFilters: array<svmAccountFilter>,
|
|
443
|
+
/** `None` matches both outer and inner (CPI-invoked) instructions. */
|
|
444
|
+
isInner: option<bool>,
|
|
445
|
+
/** Positional account names from the Borsh schema, in declared order.
|
|
446
|
+
`[]` means no schema is attached for this instruction. */
|
|
447
|
+
accounts: array<string>,
|
|
448
|
+
/** Borsh args layout as `Vec<ArgDef>` JSON (see `human_config::svm::ArgDef`
|
|
449
|
+
on the Rust side). `JSON.Null` means no schema is attached. */
|
|
450
|
+
args: JSON.t,
|
|
451
|
+
/** Program-level nominal-type registry (`BTreeMap<String, ArgType>` JSON).
|
|
452
|
+
Duplicated on every event of the same program — the runtime dedups by
|
|
453
|
+
`programId` when registering. `JSON.Null` when empty. */
|
|
454
|
+
definedTypes: JSON.t,
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
type svmProgramConfig = {
|
|
458
|
+
name: string,
|
|
459
|
+
programId: SvmTypes.Pubkey.t,
|
|
460
|
+
instructions: array<svmInstructionEventConfig>,
|
|
461
|
+
}
|
|
462
|
+
|
|
425
463
|
type indexingAddress = {
|
|
426
464
|
address: Address.t,
|
|
427
465
|
contractName: string,
|