@effectstream/sm 0.100.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/Stm.ts +88 -0
  2. package/delegate-wallet.ts +431 -0
  3. package/mod.ts +4 -0
  4. package/package.json +27 -0
  5. package/primitives/Primitive.ts +80 -0
  6. package/primitives/PrimitiveRegistry.ts +59 -0
  7. package/primitives/mod.ts +5 -0
  8. package/primitives/processing.ts +72 -0
  9. package/primitives/src/README.md +3 -0
  10. package/primitives/src/avail-generic/avail-generic-grammar.ts +5 -0
  11. package/primitives/src/avail-generic/avail-primitive.ts +103 -0
  12. package/primitives/src/bitcoin-address/bitcoin-grammar.ts +16 -0
  13. package/primitives/src/bitcoin-address/bitcoin-primitive.ts +113 -0
  14. package/primitives/src/builtin.ts +47 -0
  15. package/primitives/src/celestia-generic/celestia-generic-grammar.ts +5 -0
  16. package/primitives/src/celestia-generic/celestia-primitive.ts +97 -0
  17. package/primitives/src/evm-effectstream-l2/effectstream-l2-abi.ts +1 -0
  18. package/primitives/src/evm-effectstream-l2/effectstream-l2-primitive.ts +484 -0
  19. package/primitives/src/evm-erc1155/erc1155-abi.ts +1 -0
  20. package/primitives/src/evm-erc1155/erc1155-grammar.ts +13 -0
  21. package/primitives/src/evm-erc1155/erc1155-ivm.ts +104 -0
  22. package/primitives/src/evm-erc1155/erc1155-primitive.ts +242 -0
  23. package/primitives/src/evm-erc20/erc20-abi.ts +1 -0
  24. package/primitives/src/evm-erc20/erc20-grammar.ts +7 -0
  25. package/primitives/src/evm-erc20/erc20-ivm.ts +93 -0
  26. package/primitives/src/evm-erc20/erc20-primitive.test.ts +123 -0
  27. package/primitives/src/evm-erc20/erc20-primitive.ts +152 -0
  28. package/primitives/src/evm-erc721/erc721-abi.ts +1 -0
  29. package/primitives/src/evm-erc721/erc721-grammar.ts +9 -0
  30. package/primitives/src/evm-erc721/erc721-ivm.ts +85 -0
  31. package/primitives/src/evm-erc721/erc721-primitive.ts +149 -0
  32. package/primitives/src/evm-generic/evm-generic-primitive.ts +124 -0
  33. package/primitives/src/grammar.ts +34 -0
  34. package/primitives/src/midnight-generic/midnight-genetic-grammar.ts +8 -0
  35. package/primitives/src/midnight-generic/midnight-genetic.ts +271 -0
  36. package/primitives/src/midnight-nullifier/midnight-nullifier-grammar.ts +8 -0
  37. package/primitives/src/midnight-nullifier/midnight-nullifier.ts +114 -0
  38. package/primitives/src/mod.ts +81 -0
  39. package/primitives/src/near-account-watch/near-account-watch-grammar.ts +10 -0
  40. package/primitives/src/near-account-watch/near-account-watch-primitive.ts +106 -0
  41. package/primitives/src/near-generic/near-generic-grammar.ts +5 -0
  42. package/primitives/src/near-generic/near-generic-primitive.ts +105 -0
  43. package/primitives/src/near-intent/near-intent-grammar.ts +10 -0
  44. package/primitives/src/near-intent/near-intent-ivm.ts +71 -0
  45. package/primitives/src/near-intent/near-intent-primitive.ts +149 -0
  46. package/primitives/src/near-intent/near-intent-token-id-parser.ts +47 -0
  47. package/primitives/src/near-nep141/nep141-grammar.ts +7 -0
  48. package/primitives/src/near-nep141/nep141-ivm.ts +70 -0
  49. package/primitives/src/near-nep141/nep141-primitive.ts +117 -0
  50. package/primitives/src/near-nep171/nep171-grammar.ts +8 -0
  51. package/primitives/src/near-nep171/nep171-ivm.ts +71 -0
  52. package/primitives/src/near-nep171/nep171-primitive.ts +138 -0
  53. package/primitives/src/near-nep245/nep245-grammar.ts +11 -0
  54. package/primitives/src/near-nep245/nep245-ivm.ts +84 -0
  55. package/primitives/src/near-nep245/nep245-primitive.ts +147 -0
  56. package/primitives/src/utxorpc-generic/utxorpc-generic-grammar.ts +6 -0
  57. package/primitives/src/utxorpc-generic/utxorpc-generic.ts +88 -0
  58. package/primitives/types.ts +14 -0
  59. package/types.ts +28 -0
  60. package/verify-signature.ts +51 -0
@@ -0,0 +1,149 @@
1
+ import { Value } from "@sinclair/typebox/value";
2
+ import { type StaticDecode, Type } from "@sinclair/typebox";
3
+ import {
4
+ type ConfigSyncProtocolType,
5
+ type FlattenSyncProtocolIOFor,
6
+ getEvmEvent,
7
+ type ProtocolPrimitiveMap,
8
+ } from "@effectstream/config";
9
+ import {
10
+ type AddressAndType,
11
+ AddressType,
12
+ type EvmAddress,
13
+ type EffectstreamBlockNumber,
14
+ TypeboxHelpers,
15
+ } from "@effectstream/utils";
16
+ import { type JsonObject, Primitive } from "@effectstream/sm";
17
+ import {
18
+ type CommandTuple,
19
+ generateRawStmInput,
20
+ type ParamToData,
21
+ } from "@effectstream/concise";
22
+ import type { StateUpdateStream } from "@effectstream/coroutine";
23
+ import { ERC721_VIEW_PREFIX, erc721Ivm } from "./erc721-ivm.ts";
24
+ import { ERC721_INTERMEDIATE_PREFIX } from "./erc721-ivm.ts";
25
+ import { erc721 } from "./erc721-abi.ts";
26
+ import { erc721Grammar } from "./erc721-grammar.ts";
27
+ import { PrimitiveTypeEVMERC721 } from "../builtin.ts";
28
+
29
+ /**
30
+ * Erc721 Primitive
31
+ *
32
+ * This is a concrete implementation of the Primitive class for ERC721.
33
+ */
34
+
35
+
36
+ export class Erc721Primitive extends Primitive<
37
+ ConfigSyncProtocolType.EVM_RPC_PARALLEL,
38
+ typeof erc721Grammar
39
+ > {
40
+ // Primitive defined
41
+ readonly internalTypeName = PrimitiveTypeEVMERC721;
42
+ readonly abi = getEvmEvent(erc721.abi, "Transfer(address,address,uint256)");
43
+ override grammar = erc721Grammar;
44
+ readonly contractAddress: EvmAddress;
45
+ // Dynamic table to track the owner of each token.
46
+ override dynamicTables = erc721Ivm;
47
+ override getIntermediatePrefix(): string[] {
48
+ return [ERC721_INTERMEDIATE_PREFIX];
49
+ }
50
+ override getViewPrefix(): string[] {
51
+ return [ERC721_VIEW_PREFIX];
52
+ }
53
+
54
+ constructor(config: {
55
+ instanceName: string;
56
+ startBlockHeight: number;
57
+ contractAddress: EvmAddress;
58
+ stateMachinePrefix: string | undefined;
59
+ }) {
60
+ super(config);
61
+ this.contractAddress = Value.Decode(
62
+ TypeboxHelpers.Evm.Address,
63
+ config.contractAddress,
64
+ );
65
+ }
66
+
67
+ override *getPayload(
68
+ _: EffectstreamBlockNumber,
69
+ primitiveTransactionData: FlattenSyncProtocolIOFor<
70
+ ConfigSyncProtocolType.EVM_RPC_PARALLEL
71
+ >,
72
+ ): StateUpdateStream<{
73
+ isBatched: boolean;
74
+ data: {
75
+ fromAddressAndType: AddressAndType;
76
+ stateMachinePayload:
77
+ | StaticDecode<
78
+ CommandTuple<string, typeof erc721Grammar>
79
+ >
80
+ | null;
81
+ accountingPayload: JsonObject;
82
+ }[];
83
+ }> {
84
+ const { to, from } = primitiveTransactionData.output.payload;
85
+ const toAddr = Value.Decode(TypeboxHelpers.Evm.Address, to.toLowerCase());
86
+ const fromAddr = Value.Decode(
87
+ TypeboxHelpers.Evm.Address,
88
+ from.toLowerCase(),
89
+ );
90
+ const isBurn = Boolean(toAddr.toLocaleLowerCase().match(/^0x0+(dead)?$/g));
91
+ const tokenId = Value.Decode(
92
+ TypeboxHelpers.Uint256,
93
+ primitiveTransactionData.output.payload.tokenId,
94
+ );
95
+
96
+ const isBatched = false;
97
+ const accountingPayload: ParamToData<typeof erc721Grammar> = {
98
+ to: toAddr,
99
+ from: fromAddr,
100
+ tokenId: tokenId,
101
+ isBurn: isBurn,
102
+ };
103
+ const stateMachinePayload:
104
+ | StaticDecode<
105
+ CommandTuple<string, typeof this.grammar>
106
+ >
107
+ | null = this.stateMachinePrefix
108
+ ? generateRawStmInput(
109
+ this.grammar,
110
+ this.stateMachinePrefix,
111
+ accountingPayload,
112
+ )
113
+ : null;
114
+
115
+ return {
116
+ isBatched,
117
+ data: [
118
+ {
119
+ fromAddressAndType: {
120
+ type: AddressType.NONE,
121
+ address: "0x0",
122
+ },
123
+ accountingPayload,
124
+ stateMachinePayload,
125
+ },
126
+ ],
127
+ };
128
+ }
129
+
130
+ override getConfig(): ProtocolPrimitiveMap[
131
+ ConfigSyncProtocolType.EVM_RPC_PARALLEL
132
+ ] {
133
+ return {
134
+ name: this.instanceName,
135
+ type: this.internalTypeName,
136
+ startBlockHeight: this.startBlockHeight,
137
+ contractAddress: this.contractAddress as EvmAddress,
138
+ abi: this.abi,
139
+ // TODO This should be optional.
140
+ scheduledPrefix: this.stateMachinePrefix ?? "",
141
+ } as const;
142
+ }
143
+ }
144
+
145
+ // declare module "@effectstream/sm" {
146
+ // interface PrimitiveGlobalDefinitions {
147
+ // Erc721Primitive: typeof Erc721Primitive;
148
+ // }
149
+ // }
@@ -0,0 +1,124 @@
1
+ import type { StaticDecode, TSchema } from "@sinclair/typebox";
2
+ import type {
3
+ ConfigSyncProtocolType,
4
+ FlattenSyncProtocolIOFor,
5
+ getEvmEvent,
6
+ ProtocolPrimitiveMap,
7
+ } from "@effectstream/config";
8
+ import {
9
+ type AddressAndType,
10
+ AddressType,
11
+ type EvmAddress,
12
+ type EffectstreamBlockNumber,
13
+ TypeboxHelpers,
14
+ } from "@effectstream/utils";
15
+ import { type JsonObject, Primitive } from "@effectstream/sm";
16
+ import { Value } from "@sinclair/typebox/value";
17
+ import {
18
+ type CommandTuple,
19
+ generateRawStmInput,
20
+ type ParamToData,
21
+ } from "@effectstream/concise";
22
+ import type { StateUpdateStream } from "@effectstream/coroutine";
23
+ import { PrimitiveTypeEVMGeneric } from "../builtin.ts";
24
+
25
+ /**
26
+ * Erc20 Primitive
27
+ *
28
+ * This is a concrete implementation of the Primitive class for ERC20.
29
+ */
30
+
31
+ export class EvmGenericPrimitive extends Primitive<
32
+ ConfigSyncProtocolType.EVM_RPC_PARALLEL,
33
+ readonly [string, TSchema][]
34
+ > {
35
+ // Primitive defined
36
+ readonly internalTypeName = PrimitiveTypeEVMGeneric;
37
+ readonly abi: ReturnType<typeof getEvmEvent>;
38
+ override grammar: readonly [string, TSchema][];
39
+ readonly contractAddress: EvmAddress;
40
+
41
+ constructor(config: {
42
+ instanceName: string;
43
+ startBlockHeight: number;
44
+ contractAddress: EvmAddress;
45
+ stateMachinePrefix: string | undefined;
46
+ abi: ReturnType<typeof getEvmEvent>;
47
+ grammar: readonly [string, TSchema][];
48
+ }) {
49
+ super(config);
50
+ this.contractAddress = Value.Decode(
51
+ TypeboxHelpers.Evm.Address,
52
+ config.contractAddress,
53
+ );
54
+ this.abi = config.abi;
55
+ this.grammar = config.grammar;
56
+ }
57
+
58
+ override *getPayload(
59
+ _: EffectstreamBlockNumber,
60
+ primitiveTransactionData: FlattenSyncProtocolIOFor<
61
+ ConfigSyncProtocolType.EVM_RPC_PARALLEL
62
+ >,
63
+ ): StateUpdateStream<{
64
+ isBatched: boolean;
65
+ data: {
66
+ fromAddressAndType: AddressAndType;
67
+ stateMachinePayload:
68
+ | StaticDecode<
69
+ CommandTuple<string, any>
70
+ >
71
+ | null;
72
+ accountingPayload: JsonObject;
73
+ }[];
74
+ }> {
75
+ const payload = primitiveTransactionData.output.payload;
76
+ const accountingPayload = this.clearBigInts({
77
+ ...payload,
78
+ });
79
+ const stateMachinePayload: any = this.stateMachinePrefix
80
+ ? generateRawStmInput(
81
+ this.grammar,
82
+ this.stateMachinePrefix,
83
+ accountingPayload,
84
+ )
85
+ : null;
86
+
87
+ return {
88
+ isBatched: false,
89
+ data: [
90
+ {
91
+ fromAddressAndType: {
92
+ type: AddressType.NONE,
93
+ address: "0x0",
94
+ },
95
+ accountingPayload,
96
+ stateMachinePayload,
97
+ },
98
+ ],
99
+ };
100
+ }
101
+
102
+ private clearBigInts<T>(value: T): T {
103
+ return JSON.parse(
104
+ JSON.stringify(
105
+ value,
106
+ (_, v) => typeof v === "bigint" ? v.toString() : v,
107
+ ),
108
+ );
109
+ }
110
+
111
+
112
+ override getConfig(): ProtocolPrimitiveMap[
113
+ ConfigSyncProtocolType.EVM_RPC_PARALLEL
114
+ ] {
115
+ return {
116
+ name: this.instanceName,
117
+ type: this.internalTypeName,
118
+ startBlockHeight: this.startBlockHeight,
119
+ contractAddress: this.contractAddress as EvmAddress,
120
+ abi: this.abi,
121
+ scheduledPrefix: this.stateMachinePrefix,
122
+ } as const;
123
+ }
124
+ }
@@ -0,0 +1,34 @@
1
+ // list of built-in grammars
2
+ // this list is exposed to the effectstream-sdk modules via the @effectstream/sm/grammar module
3
+
4
+ import { midnightGenericGrammar } from "./midnight-generic/midnight-genetic-grammar.ts";
5
+ import { erc721Grammar } from "./evm-erc721/erc721-grammar.ts";
6
+ import { erc20Grammar } from "./evm-erc20/erc20-grammar.ts";
7
+ import { availGenericGrammar } from "./avail-generic/avail-generic-grammar.ts";
8
+ import { erc1155Grammar } from "./evm-erc1155/erc1155-grammar.ts";
9
+ import { bitcoinAddressGrammar } from "./bitcoin-address/bitcoin-grammar.ts";
10
+ import { utxorpcGenericGrammar } from "./utxorpc-generic/utxorpc-generic-grammar.ts";
11
+ import { celestiaGenericGrammar } from "./celestia-generic/celestia-generic-grammar.ts";
12
+ import { nep141Grammar } from "./near-nep141/nep141-grammar.ts";
13
+ import { nep171Grammar } from "./near-nep171/nep171-grammar.ts";
14
+ import { nep245Grammar } from "./near-nep245/nep245-grammar.ts";
15
+ import { nearIntentGrammar } from "./near-intent/near-intent-grammar.ts";
16
+ import { nearGenericGrammar } from "./near-generic/near-generic-grammar.ts";
17
+ import { nearAccountWatchGrammar } from "./near-account-watch/near-account-watch-grammar.ts";
18
+
19
+ export const builtinGrammars = {
20
+ midnightGeneric: midnightGenericGrammar,
21
+ evmErc721: erc721Grammar,
22
+ evmErc20: erc20Grammar,
23
+ availGeneric: availGenericGrammar,
24
+ evmErc1155: erc1155Grammar,
25
+ bitcoinAddress: bitcoinAddressGrammar,
26
+ utxorpcGeneric: utxorpcGenericGrammar,
27
+ celestiaGeneric: celestiaGenericGrammar,
28
+ nearNep141: nep141Grammar,
29
+ nearNep171: nep171Grammar,
30
+ nearNep245: nep245Grammar,
31
+ nearIntent: nearIntentGrammar,
32
+ nearGeneric: nearGenericGrammar,
33
+ nearAccountWatch: nearAccountWatchGrammar,
34
+ } as const;
@@ -0,0 +1,8 @@
1
+ import { Type } from "@sinclair/typebox";
2
+
3
+ export const midnightGenericGrammar = [
4
+ [
5
+ "payload",
6
+ Type.Any()
7
+ ],
8
+ ] as const;
@@ -0,0 +1,271 @@
1
+ import { Primitive } from "@effectstream/sm";
2
+ import {
3
+ type AddressAndType,
4
+ AddressType,
5
+ type MidnightAddress,
6
+ type EffectstreamBlockNumber,
7
+ uint8ArrayToHexString,
8
+ } from "@effectstream/utils";
9
+ import type { StaticDecode } from "@sinclair/typebox";
10
+ import {
11
+ type CommandTuple,
12
+ generateRawStmInput,
13
+ type ParamToData,
14
+ } from "@effectstream/concise";
15
+ import type {
16
+ ConfigSyncProtocolType,
17
+ FlattenSyncProtocolIOFor,
18
+ LedgerFieldType,
19
+ LedgerSchema,
20
+ ProtocolPrimitiveMap,
21
+ StateValue,
22
+ } from "@effectstream/config";
23
+ import type { SyncStateUpdateStream } from "@effectstream/coroutine";
24
+ import { PrimitiveTypeMidnightGeneric } from "../builtin.ts";
25
+ import { midnightGenericGrammar } from "./midnight-genetic-grammar.ts"
26
+
27
+
28
+ export class MidnightGenericPrimitive extends Primitive<
29
+ ConfigSyncProtocolType.MIDNIGHT_PARALLEL,
30
+ typeof midnightGenericGrammar
31
+ > {
32
+ // Primitive defined
33
+ readonly internalTypeName = PrimitiveTypeMidnightGeneric;
34
+ override readonly grammar = midnightGenericGrammar;
35
+ readonly contractAddress: string;
36
+ readonly contract: {
37
+ ledger: (data: StateValue) => any;
38
+ };
39
+ readonly ledgerSchema?: LedgerSchema;
40
+ readonly networkId: string;
41
+ readonly genesisHash: string;
42
+ // No dynamic tables for midnight generic primitive
43
+ override dynamicTables = undefined;
44
+ override getIntermediatePrefix(): string[] {
45
+ return [];
46
+ }
47
+ override getViewPrefix(): string[] {
48
+ return [];
49
+ }
50
+
51
+ constructor(config: {
52
+ instanceName: string;
53
+ startBlockHeight: number;
54
+ contractAddress: MidnightAddress;
55
+ stateMachinePrefix: string;
56
+ contract: {
57
+ ledger: (data: StateValue) => any;
58
+ };
59
+ ledgerSchema?: LedgerSchema;
60
+ networkId?: string;
61
+ genesisHash?: string;
62
+ }) {
63
+ super(config);
64
+ this.contractAddress = config.contractAddress;
65
+ this.contract = config.contract;
66
+ this.ledgerSchema = config.ledgerSchema;
67
+ this.networkId = config.networkId ?? "undeployed";
68
+ this.genesisHash = config.genesisHash || "";
69
+ }
70
+
71
+ /**
72
+ * Parses ledger fields from the raw Midnight StateValue using the `ledgerSchema`.
73
+ * The schema keys must be in Compact declaration order — the parser maps each key
74
+ * to the corresponding positional index in the state array.
75
+ */
76
+ parseAdditionalLedgerFields(stateValue: StateValue): Record<string, any> {
77
+ if (!this.ledgerSchema) return {};
78
+
79
+ const stateArray: StateValue[] = stateValue.asArray?.() ?? [];
80
+ const result: Record<string, any> = {};
81
+ const schemaEntries = Object.entries(this.ledgerSchema);
82
+
83
+ for (let i = 0; i < schemaEntries.length && i < stateArray.length; i++) {
84
+ const [fieldName, fieldType] = schemaEntries[i];
85
+ result[fieldName] = parseLedgerField(stateArray[i], fieldType);
86
+ }
87
+
88
+ return result;
89
+ }
90
+
91
+ override *getPayload(
92
+ _: EffectstreamBlockNumber,
93
+ primitiveTransactionData: FlattenSyncProtocolIOFor<
94
+ ConfigSyncProtocolType.MIDNIGHT_PARALLEL
95
+ >,
96
+ ): SyncStateUpdateStream<{
97
+ isBatched: boolean;
98
+ data: {
99
+ fromAddressAndType: AddressAndType;
100
+ stateMachinePayload:
101
+ | StaticDecode<CommandTuple<string, typeof midnightGenericGrammar>>
102
+ | null;
103
+ accountingPayload: ParamToData<typeof midnightGenericGrammar>;
104
+ }[];
105
+ }> {
106
+ const payload = primitiveTransactionData.output.payload;
107
+ try {
108
+ const isBatched = false;
109
+
110
+ const accountingPayload: ParamToData<typeof this.grammar> = {
111
+ payload: makeJsonSafe(payload),
112
+ };
113
+
114
+ const stateMachinePayload:
115
+ | StaticDecode<
116
+ CommandTuple<string, typeof this.grammar>
117
+ >
118
+ | null = this.stateMachinePrefix
119
+ ? generateRawStmInput(
120
+ this.grammar,
121
+ this.stateMachinePrefix,
122
+ accountingPayload,
123
+ )
124
+ : null;
125
+
126
+ return {
127
+ isBatched,
128
+ data: [
129
+ {
130
+ fromAddressAndType: {
131
+ type: AddressType.NONE,
132
+ address: "0x0",
133
+ },
134
+ accountingPayload,
135
+ stateMachinePayload,
136
+ },
137
+ ],
138
+ };
139
+ } catch (error) {
140
+ console.error(
141
+ "[ERROR] Decoding Midnight Generic Payload:",
142
+ JSON.stringify(payload),
143
+ );
144
+ throw error;
145
+ }
146
+ }
147
+
148
+ override getConfig(): ProtocolPrimitiveMap[
149
+ ConfigSyncProtocolType.MIDNIGHT_PARALLEL
150
+ ] {
151
+ return {
152
+ name: this.instanceName,
153
+ type: this.internalTypeName,
154
+ startBlockHeight: this.startBlockHeight,
155
+ contractAddress: this.contractAddress,
156
+ // TODO This should be optional
157
+ scheduledPrefix: this.stateMachinePrefix ?? "",
158
+ contract: this.contract,
159
+ parseAdditionalLedgerFields: this.ledgerSchema
160
+ ? this.parseAdditionalLedgerFields.bind(this)
161
+ : undefined,
162
+ // FIXME: historically we relied on NetworkId enum values, but
163
+ // the v1 runtime expects canonical string identifiers instead.
164
+ networkId: this.networkId,
165
+ // TODO This is unused for now.
166
+ genesisHash: this.genesisHash || "",
167
+ } as const;
168
+ }
169
+ }
170
+
171
+ function alignedValueToHex(value: { value: Uint8Array[] }): string {
172
+ return (
173
+ "0x" +
174
+ value.value
175
+ .map((chunk: Uint8Array) =>
176
+ Array.from(chunk)
177
+ .map((b: number) => b.toString(16).padStart(2, "0"))
178
+ .join("")
179
+ )
180
+ .join("")
181
+ );
182
+ }
183
+
184
+ function parseUintCellLittleEndian(chunks: Uint8Array[]): string {
185
+ let result = 0n;
186
+ let shift = 0n;
187
+ for (const chunk of chunks) {
188
+ for (const byte of chunk) {
189
+ result |= BigInt(byte) << shift;
190
+ shift += 8n;
191
+ }
192
+ }
193
+ return result.toString();
194
+ }
195
+
196
+ function parseLedgerField(sv: StateValue, type: LedgerFieldType): any {
197
+ if (typeof type === "object" && type.type === "option") {
198
+ if (sv.type() === "null") return null;
199
+ return parseLedgerField(sv, type.value);
200
+ }
201
+
202
+ if (typeof type === "object" && type.type === "map") {
203
+ const map = sv.asMap();
204
+ if (!map) return {};
205
+
206
+ const result: Record<string, any> = {};
207
+ for (const key of map.keys()) {
208
+ const val = map.get(key);
209
+ if (val !== undefined) {
210
+ result[alignedValueToHex(key as { value: Uint8Array[] })] = parseLedgerField(
211
+ val,
212
+ type.value,
213
+ );
214
+ }
215
+ }
216
+ return result;
217
+ }
218
+
219
+ if (sv.type() !== "cell") return null;
220
+ const cell = sv.asCell();
221
+
222
+ if (
223
+ type === "uint8" ||
224
+ type === "uint16" ||
225
+ type === "uint32" ||
226
+ type === "uint64" ||
227
+ type === "uint128"
228
+ ) {
229
+ return parseUintCellLittleEndian(cell.value as Uint8Array[]);
230
+ }
231
+
232
+ if (type === "bytes") {
233
+ return alignedValueToHex(cell as { value: Uint8Array[] });
234
+ }
235
+
236
+ if (type === "boolean") {
237
+ return (cell.value as Uint8Array[]).some((chunk: Uint8Array) =>
238
+ chunk.some((b: number) => b !== 0)
239
+ );
240
+ }
241
+
242
+ return null;
243
+ }
244
+
245
+ function makeJsonSafe(data: unknown): any {
246
+ if (typeof data === 'boolean' || typeof data === 'number' || typeof data === 'string') {
247
+ return data;
248
+ }
249
+ if (typeof data === 'bigint') {
250
+ return data.toString();
251
+ }
252
+ if (data === null || data === undefined) {
253
+ return data;
254
+ }
255
+ if (Array.isArray(data)) {
256
+ return data.map(makeJsonSafe);
257
+ }
258
+ if (data instanceof Uint8Array) {
259
+ return uint8ArrayToHexString(data);
260
+ }
261
+ if (typeof data === 'object') {
262
+ return Object.fromEntries(Object.entries(data).map(([k, v]) => [k, makeJsonSafe(v)]));
263
+ }
264
+ return data;
265
+ }
266
+
267
+ // declare module "@effectstream/sm" {
268
+ // interface PrimitiveGlobalDefinitions {
269
+ // MidnightGenericPrimitive: typeof MidnightGenericPrimitive;
270
+ // }
271
+ // }
@@ -0,0 +1,8 @@
1
+ import { Type } from "@sinclair/typebox";
2
+
3
+ export const midnightNullifierGrammar = [
4
+ [
5
+ "payload",
6
+ Type.Any(),
7
+ ],
8
+ ] as const;
@@ -0,0 +1,114 @@
1
+ import { Primitive } from "@effectstream/sm";
2
+ import {
3
+ type AddressAndType,
4
+ AddressType,
5
+ type EffectstreamBlockNumber,
6
+ } from "@effectstream/utils";
7
+ import type { StaticDecode } from "@sinclair/typebox";
8
+ import {
9
+ type CommandTuple,
10
+ generateRawStmInput,
11
+ type ParamToData,
12
+ } from "@effectstream/concise";
13
+ import type {
14
+ ConfigSyncProtocolType,
15
+ FlattenSyncProtocolIOFor,
16
+ ProtocolPrimitiveMap,
17
+ } from "@effectstream/config";
18
+ import type { SyncStateUpdateStream } from "@effectstream/coroutine";
19
+ import { PrimitiveTypeMidnightNullifier } from "../builtin.ts";
20
+ import { midnightNullifierGrammar } from "./midnight-nullifier-grammar.ts";
21
+
22
+ /**
23
+ * MidnightNullifierPrimitive
24
+ *
25
+ * Watches all ZswapInput (nullifier-spend) events on the Midnight shielded
26
+ * ledger. One state-machine input is emitted per nullifier event observed in
27
+ * a block, regardless of which contract produced it.
28
+ *
29
+ * Usage:
30
+ * stm.addStateTransition("myPrefix", function* (data) {
31
+ * const { payload } = data.parsedInput;
32
+ * // payload = { nullifier, txHash, eventId, logicalSegment }
33
+ * });
34
+ */
35
+ export class MidnightNullifierPrimitive extends Primitive<
36
+ ConfigSyncProtocolType.MIDNIGHT_PARALLEL,
37
+ typeof midnightNullifierGrammar
38
+ > {
39
+ readonly internalTypeName = PrimitiveTypeMidnightNullifier;
40
+ override readonly grammar = midnightNullifierGrammar;
41
+
42
+ override dynamicTables = undefined;
43
+ override getIntermediatePrefix(): string[] {
44
+ return [];
45
+ }
46
+ override getViewPrefix(): string[] {
47
+ return [];
48
+ }
49
+
50
+ constructor(config: {
51
+ instanceName: string;
52
+ startBlockHeight: number;
53
+ stateMachinePrefix: string;
54
+ }) {
55
+ super(config);
56
+ }
57
+
58
+ override *getPayload(
59
+ _: EffectstreamBlockNumber,
60
+ primitiveTransactionData: FlattenSyncProtocolIOFor<
61
+ ConfigSyncProtocolType.MIDNIGHT_PARALLEL
62
+ >,
63
+ ): SyncStateUpdateStream<{
64
+ isBatched: boolean;
65
+ data: {
66
+ fromAddressAndType: AddressAndType;
67
+ stateMachinePayload:
68
+ | StaticDecode<CommandTuple<string, typeof midnightNullifierGrammar>>
69
+ | null;
70
+ accountingPayload: ParamToData<typeof midnightNullifierGrammar>;
71
+ }[];
72
+ }> {
73
+ const payload = primitiveTransactionData.output.payload;
74
+
75
+ const accountingPayload: ParamToData<typeof this.grammar> = {
76
+ payload,
77
+ };
78
+
79
+ const stateMachinePayload:
80
+ | StaticDecode<CommandTuple<string, typeof this.grammar>>
81
+ | null = this.stateMachinePrefix
82
+ ? generateRawStmInput(
83
+ this.grammar,
84
+ this.stateMachinePrefix,
85
+ accountingPayload,
86
+ )
87
+ : null;
88
+
89
+ return {
90
+ isBatched: false,
91
+ data: [
92
+ {
93
+ fromAddressAndType: {
94
+ type: AddressType.NONE,
95
+ address: "0x0",
96
+ },
97
+ accountingPayload,
98
+ stateMachinePayload,
99
+ },
100
+ ],
101
+ };
102
+ }
103
+
104
+ override getConfig(): ProtocolPrimitiveMap[
105
+ ConfigSyncProtocolType.MIDNIGHT_PARALLEL
106
+ ] {
107
+ return {
108
+ name: this.instanceName,
109
+ type: this.internalTypeName,
110
+ startBlockHeight: this.startBlockHeight,
111
+ scheduledPrefix: this.stateMachinePrefix ?? "",
112
+ } as const;
113
+ }
114
+ }