@effectstream/sm 0.100.12 → 0.100.14

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 (23) hide show
  1. package/package.json +9 -8
  2. package/primitives/src/builtin.ts +12 -0
  3. package/primitives/src/cardano-delayed-asset/delayed-asset-grammar.ts +11 -0
  4. package/primitives/src/cardano-delayed-asset/delayed-asset-ivm.ts +89 -0
  5. package/primitives/src/cardano-delayed-asset/delayed-asset-primitive.ts +228 -0
  6. package/primitives/src/cardano-mint-burn/mint-burn-grammar.ts +9 -0
  7. package/primitives/src/cardano-mint-burn/mint-burn-primitive.ts +160 -0
  8. package/primitives/src/cardano-pool-delegation/pool-delegation-grammar.ts +7 -0
  9. package/primitives/src/cardano-pool-delegation/pool-delegation-ivm.ts +65 -0
  10. package/primitives/src/cardano-pool-delegation/pool-delegation-primitive.ts +150 -0
  11. package/primitives/src/cardano-projected-nft/projected-nft-datum.ts +107 -0
  12. package/primitives/src/cardano-projected-nft/projected-nft-grammar.ts +13 -0
  13. package/primitives/src/cardano-projected-nft/projected-nft-ivm.ts +108 -0
  14. package/primitives/src/cardano-projected-nft/projected-nft-primitive.ts +242 -0
  15. package/primitives/src/cardano-transfer/transfer-grammar.ts +8 -0
  16. package/primitives/src/cardano-transfer/transfer-primitive.ts +150 -0
  17. package/primitives/src/cardano-utils/cardano-helpers.ts +84 -0
  18. package/primitives/src/grammar.ts +10 -0
  19. package/primitives/src/midnight-generic/midnight-genetic.ts +15 -0
  20. package/primitives/src/midnight-unshielded-spend/midnight-unshielded-spend-grammar.ts +8 -0
  21. package/primitives/src/midnight-unshielded-spend/midnight-unshielded-spend.ts +115 -0
  22. package/primitives/src/mod.ts +24 -0
  23. package/types.ts +27 -5
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Cardano Transfer Primitive
3
+ *
4
+ * Captures ADA and native asset transfers from Dolos UTxORPC block data.
5
+ *
6
+ * Data source: Dolos streams raw Cardano blocks via gRPC. Each transaction contains
7
+ * `outputs` (destination UTxOs with address, lovelace coin, and native assets) and
8
+ * `witnesses.vkeywitness` (verification key witnesses proving input authorization).
9
+ *
10
+ * Extraction: for each matched transaction, `getPayload()` maps `tx.outputs` into
11
+ * structured objects with `{index, address, coin, assets[]}`. Coin values are extracted
12
+ * from the protobuf BigInt (either `int` for small values or `bigUInt` for large).
13
+ * Input credentials are extracted from `tx.witnesses.vkeywitness[].vkey` — these are
14
+ * the public key hashes that signed the transaction, identifying who authorized the spend.
15
+ * Transaction metadata is extracted from `tx.auxiliary.metadata`.
16
+ *
17
+ * Predicate: user-provided (typically `has_address` to watch specific addresses).
18
+ */
19
+
20
+ import type { StaticDecode } from "@sinclair/typebox";
21
+ import {
22
+ type CommandTuple,
23
+ generateRawStmInput,
24
+ } from "@effectstream/concise";
25
+ import type {
26
+ ConfigSyncProtocolType,
27
+ FlattenSyncProtocolIOFor,
28
+ ProtocolPrimitiveMap,
29
+ UtxorpcTxPredicate,
30
+ } from "@effectstream/config";
31
+ import type { StateUpdateStream } from "@effectstream/coroutine";
32
+ import { type JsonObject, Primitive } from "@effectstream/sm";
33
+ import {
34
+ type AddressAndType,
35
+ AddressType,
36
+ type EffectstreamBlockNumber,
37
+ uint8ArrayToHexString,
38
+ } from "@effectstream/utils";
39
+ import { transferGrammar } from "./transfer-grammar.ts";
40
+ import { PrimitiveTypeCardanoTransfer } from "../builtin.ts";
41
+ import {
42
+ addressToHex,
43
+ assetQuantityToString,
44
+ metadataToJson,
45
+ } from "../cardano-utils/cardano-helpers.ts";
46
+
47
+ export class CardanoTransferPrimitive extends Primitive<
48
+ ConfigSyncProtocolType.CARDANO_UTXORPC_PARALLEL,
49
+ typeof transferGrammar
50
+ > {
51
+ readonly internalTypeName = PrimitiveTypeCardanoTransfer;
52
+ readonly predicate: UtxorpcTxPredicate;
53
+ override grammar = transferGrammar;
54
+
55
+ constructor(config: {
56
+ instanceName: string;
57
+ startBlockHeight: number;
58
+ stateMachinePrefix: string | undefined;
59
+ predicate: UtxorpcTxPredicate;
60
+ }) {
61
+ super(config);
62
+ this.predicate = config.predicate;
63
+ }
64
+
65
+ override getConfig(): ProtocolPrimitiveMap[ConfigSyncProtocolType.CARDANO_UTXORPC_PARALLEL] {
66
+ return {
67
+ name: this.instanceName,
68
+ type: this.internalTypeName,
69
+ startBlockHeight: this.startBlockHeight,
70
+ scheduledPrefix: this.stateMachinePrefix,
71
+ predicate: this.predicate,
72
+ } as const;
73
+ }
74
+
75
+ override *getPayload(
76
+ _: EffectstreamBlockNumber,
77
+ primitiveTransactionData: FlattenSyncProtocolIOFor<ConfigSyncProtocolType.CARDANO_UTXORPC_PARALLEL>,
78
+ ): StateUpdateStream<{
79
+ isBatched: boolean;
80
+ data: {
81
+ fromAddressAndType: AddressAndType;
82
+ stateMachinePayload: StaticDecode<CommandTuple<string, typeof transferGrammar>> | null;
83
+ accountingPayload: JsonObject;
84
+ }[];
85
+ }> {
86
+ const tx = primitiveTransactionData.output.payload.tx;
87
+
88
+ const txId = uint8ArrayToHexString(tx.hash);
89
+
90
+ const outputs = tx.outputs.map((out, index) => {
91
+ const address = addressToHex(out.address);
92
+ const coin = out.coin?.bigInt.case === "int"
93
+ ? String(out.coin.bigInt.value)
94
+ : out.coin?.bigInt.case === "bigUInt"
95
+ ? uint8ArrayToHexString(out.coin.bigInt.value)
96
+ : "0";
97
+
98
+ const assets: { policyId: string; assetName: string; amount: string }[] = [];
99
+ for (const ma of out.assets) {
100
+ const policyId = uint8ArrayToHexString(ma.policyId);
101
+ for (const asset of ma.assets) {
102
+ assets.push({
103
+ policyId,
104
+ assetName: uint8ArrayToHexString(asset.name),
105
+ amount: assetQuantityToString(asset),
106
+ });
107
+ }
108
+ }
109
+
110
+ return { index, address, coin, assets };
111
+ });
112
+
113
+ const inputCredentials: string[] = [];
114
+ if (tx.witnesses) {
115
+ for (const w of tx.witnesses.vkeywitness) {
116
+ const hash = uint8ArrayToHexString(w.vkey);
117
+ if (!inputCredentials.includes(hash)) inputCredentials.push(hash);
118
+ }
119
+ }
120
+
121
+ const metadata = metadataToJson(tx.auxiliary?.metadata ?? []);
122
+
123
+ const accountingPayload = {
124
+ txId,
125
+ metadata: metadata ? JSON.stringify(metadata) : null,
126
+ inputCredentials,
127
+ outputs,
128
+ };
129
+
130
+ const stateMachinePayload: any = this.stateMachinePrefix
131
+ ? generateRawStmInput(this.grammar, this.stateMachinePrefix, {
132
+ txId,
133
+ metadata: metadata ? JSON.stringify(metadata) : "",
134
+ inputCredentials: JSON.stringify(inputCredentials),
135
+ outputs: JSON.stringify(outputs),
136
+ })
137
+ : null;
138
+
139
+ return {
140
+ isBatched: false,
141
+ data: [
142
+ {
143
+ fromAddressAndType: { type: AddressType.NONE, address: "0x0" },
144
+ accountingPayload,
145
+ stateMachinePayload,
146
+ },
147
+ ],
148
+ };
149
+ }
150
+ }
@@ -0,0 +1,84 @@
1
+ import { uint8ArrayToHexString } from "@effectstream/utils";
2
+ import type { cardano } from "@utxorpc/spec";
3
+
4
+ type CardanoNetwork = "preview" | "preprod" | "mainnet" | "yaci";
5
+
6
+ const epochParams: Record<CardanoNetwork, { firstSlot: number; slotsPerEpoch: number; firstEpoch: number }> = {
7
+ mainnet: { firstSlot: 4_492_800, slotsPerEpoch: 432_000, firstEpoch: 208 },
8
+ preprod: { firstSlot: 86_400, slotsPerEpoch: 432_000, firstEpoch: 4 },
9
+ preview: { firstSlot: 0, slotsPerEpoch: 86_400, firstEpoch: 0 },
10
+ yaci: { firstSlot: 0, slotsPerEpoch: 600, firstEpoch: 0 },
11
+ };
12
+
13
+ export function slotToEpoch(slot: number, network: CardanoNetwork): number {
14
+ const params = epochParams[network];
15
+ if (slot < params.firstSlot) return 0;
16
+ return params.firstEpoch + Math.floor((slot - params.firstSlot) / params.slotsPerEpoch);
17
+ }
18
+
19
+ export function stakeCredentialToHex(cred: cardano.StakeCredential): { type: "key" | "script"; hash: string } | null {
20
+ const inner = cred.stakeCredential;
21
+ if (inner.case === "addrKeyHash") {
22
+ return { type: "key", hash: uint8ArrayToHexString(inner.value) };
23
+ }
24
+ if (inner.case === "scriptHash") {
25
+ return { type: "script", hash: uint8ArrayToHexString(inner.value) };
26
+ }
27
+ return null;
28
+ }
29
+
30
+ export function addressToHex(address: Uint8Array): string {
31
+ return uint8ArrayToHexString(address);
32
+ }
33
+
34
+ export function metadataToJson(metadata: cardano.Metadata[]): Record<string, unknown> | null {
35
+ if (!metadata || metadata.length === 0) return null;
36
+ const result: Record<string, unknown> = {};
37
+ for (const entry of metadata) {
38
+ result[String(entry.label)] = entry.value ? metadatumToJson(entry.value) : null;
39
+ }
40
+ return result;
41
+ }
42
+
43
+ function metadatumToJson(m: cardano.Metadatum): unknown {
44
+ const inner = m.metadatum;
45
+ if (!inner || inner.case === undefined) return null;
46
+ switch (inner.case) {
47
+ case "int": return String(inner.value);
48
+ case "bytes": return uint8ArrayToHexString(inner.value);
49
+ case "text": return inner.value;
50
+ case "array": return inner.value.items.map(metadatumToJson);
51
+ case "map": return inner.value.pairs.map(p => ({
52
+ k: p.key ? metadatumToJson(p.key) : null,
53
+ v: p.value ? metadatumToJson(p.value) : null,
54
+ }));
55
+ default: return null;
56
+ }
57
+ }
58
+
59
+ function bigIntToString(bi: cardano.BigInt): string {
60
+ const inner = bi.bigInt;
61
+ if (inner.case === "int") return String(inner.value);
62
+ if (inner.case === "bigUInt") {
63
+ let result = 0n;
64
+ for (const byte of inner.value) result = (result << 8n) | BigInt(byte);
65
+ return String(result);
66
+ }
67
+ if (inner.case === "bigNInt") {
68
+ let result = 0n;
69
+ for (const byte of inner.value) result = (result << 8n) | BigInt(byte);
70
+ return String(-result);
71
+ }
72
+ return "0";
73
+ }
74
+
75
+ export function assetQuantityToString(asset: cardano.Asset): string {
76
+ const q = asset.quantity;
77
+ if (q.case === "outputCoin") return bigIntToString(q.value);
78
+ if (q.case === "mintCoin") return bigIntToString(q.value);
79
+ return "0";
80
+ }
81
+
82
+ export function isMintQuantity(asset: cardano.Asset): boolean {
83
+ return asset.quantity.case === "mintCoin";
84
+ }
@@ -15,6 +15,11 @@ import { nep245Grammar } from "./near-nep245/nep245-grammar.ts";
15
15
  import { nearIntentGrammar } from "./near-intent/near-intent-grammar.ts";
16
16
  import { nearGenericGrammar } from "./near-generic/near-generic-grammar.ts";
17
17
  import { nearAccountWatchGrammar } from "./near-account-watch/near-account-watch-grammar.ts";
18
+ import { mintBurnGrammar } from "./cardano-mint-burn/mint-burn-grammar.ts";
19
+ import { transferGrammar } from "./cardano-transfer/transfer-grammar.ts";
20
+ import { poolDelegationGrammar } from "./cardano-pool-delegation/pool-delegation-grammar.ts";
21
+ import { delayedAssetGrammar } from "./cardano-delayed-asset/delayed-asset-grammar.ts";
22
+ import { projectedNftGrammar } from "./cardano-projected-nft/projected-nft-grammar.ts";
18
23
 
19
24
  export const builtinGrammars = {
20
25
  midnightGeneric: midnightGenericGrammar,
@@ -31,4 +36,9 @@ export const builtinGrammars = {
31
36
  nearIntent: nearIntentGrammar,
32
37
  nearGeneric: nearGenericGrammar,
33
38
  nearAccountWatch: nearAccountWatchGrammar,
39
+ cardanoMintBurn: mintBurnGrammar,
40
+ cardanoTransfer: transferGrammar,
41
+ cardanoPoolDelegation: poolDelegationGrammar,
42
+ cardanoDelayedAsset: delayedAssetGrammar,
43
+ cardanoProjectedNft: projectedNftGrammar,
34
44
  } as const;
@@ -242,6 +242,14 @@ function parseLedgerField(sv: StateValue, type: LedgerFieldType): any {
242
242
  return null;
243
243
  }
244
244
 
245
+ function isCompactMap(data: object): data is Iterable<[unknown, unknown]> {
246
+ return (
247
+ typeof (data as any).member === 'function' &&
248
+ typeof (data as any).lookup === 'function' &&
249
+ Symbol.iterator in data
250
+ );
251
+ }
252
+
245
253
  function makeJsonSafe(data: unknown): any {
246
254
  if (typeof data === 'boolean' || typeof data === 'number' || typeof data === 'string') {
247
255
  return data;
@@ -259,6 +267,13 @@ function makeJsonSafe(data: unknown): any {
259
267
  return uint8ArrayToHexString(data);
260
268
  }
261
269
  if (typeof data === 'object') {
270
+ if (isCompactMap(data)) {
271
+ const entries: [string, any][] = [];
272
+ for (const [k, v] of data) {
273
+ entries.push([String(makeJsonSafe(k)), makeJsonSafe(v)]);
274
+ }
275
+ return Object.fromEntries(entries);
276
+ }
262
277
  return Object.fromEntries(Object.entries(data).map(([k, v]) => [k, makeJsonSafe(v)]));
263
278
  }
264
279
  return data;
@@ -0,0 +1,8 @@
1
+ import { Type } from "@sinclair/typebox";
2
+
3
+ export const midnightUnshieldedSpendGrammar = [
4
+ [
5
+ "payload",
6
+ Type.Any(),
7
+ ],
8
+ ] as const;
@@ -0,0 +1,115 @@
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 { PrimitiveTypeMidnightUnshieldedSpend } from "../builtin.ts";
20
+ import { midnightUnshieldedSpendGrammar } from "./midnight-unshielded-spend-grammar.ts";
21
+
22
+ /**
23
+ * MidnightUnshieldedSpendPrimitive
24
+ *
25
+ * Watches all unshielded UTXO spend events on the Midnight ledger. The
26
+ * indexer's GraphQL `transactions.unshieldedSpentOutputs` field reports the
27
+ * `(owner, intentHash, outputIndex)` triple for each unshielded UTXO
28
+ * consumed in a block. One state-machine input is emitted per spend.
29
+ *
30
+ * Usage:
31
+ * stm.addStateTransition("myPrefix", function* (data) {
32
+ * const { payload } = data.parsedInput;
33
+ * // payload = { owner, intentHash, outputIndex, txHash }
34
+ * });
35
+ */
36
+ export class MidnightUnshieldedSpendPrimitive extends Primitive<
37
+ ConfigSyncProtocolType.MIDNIGHT_PARALLEL,
38
+ typeof midnightUnshieldedSpendGrammar
39
+ > {
40
+ readonly internalTypeName = PrimitiveTypeMidnightUnshieldedSpend;
41
+ override readonly grammar = midnightUnshieldedSpendGrammar;
42
+
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
+ stateMachinePrefix: string;
55
+ }) {
56
+ super(config);
57
+ }
58
+
59
+ override *getPayload(
60
+ _: EffectstreamBlockNumber,
61
+ primitiveTransactionData: FlattenSyncProtocolIOFor<
62
+ ConfigSyncProtocolType.MIDNIGHT_PARALLEL
63
+ >,
64
+ ): SyncStateUpdateStream<{
65
+ isBatched: boolean;
66
+ data: {
67
+ fromAddressAndType: AddressAndType;
68
+ stateMachinePayload:
69
+ | StaticDecode<CommandTuple<string, typeof midnightUnshieldedSpendGrammar>>
70
+ | null;
71
+ accountingPayload: ParamToData<typeof midnightUnshieldedSpendGrammar>;
72
+ }[];
73
+ }> {
74
+ const payload = primitiveTransactionData.output.payload;
75
+
76
+ const accountingPayload: ParamToData<typeof this.grammar> = {
77
+ payload,
78
+ };
79
+
80
+ const stateMachinePayload:
81
+ | StaticDecode<CommandTuple<string, typeof this.grammar>>
82
+ | null = this.stateMachinePrefix
83
+ ? generateRawStmInput(
84
+ this.grammar,
85
+ this.stateMachinePrefix,
86
+ accountingPayload,
87
+ )
88
+ : null;
89
+
90
+ return {
91
+ isBatched: false,
92
+ data: [
93
+ {
94
+ fromAddressAndType: {
95
+ type: AddressType.NONE,
96
+ address: "0x0",
97
+ },
98
+ accountingPayload,
99
+ stateMachinePayload,
100
+ },
101
+ ],
102
+ };
103
+ }
104
+
105
+ override getConfig(): ProtocolPrimitiveMap[
106
+ ConfigSyncProtocolType.MIDNIGHT_PARALLEL
107
+ ] {
108
+ return {
109
+ name: this.instanceName,
110
+ type: this.internalTypeName,
111
+ startBlockHeight: this.startBlockHeight,
112
+ scheduledPrefix: this.stateMachinePrefix ?? "",
113
+ } as const;
114
+ }
115
+ }
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  PrimitiveTypeMidnightGeneric,
3
3
  PrimitiveTypeMidnightNullifier,
4
+ PrimitiveTypeMidnightUnshieldedSpend,
4
5
  PrimitiveTypeEVMEffectstreamL2,
5
6
  PrimitiveTypeEVMERC721,
6
7
  PrimitiveTypeEVMERC20,
@@ -15,11 +16,17 @@ import {
15
16
  PrimitiveTypeNEARIntent,
16
17
  PrimitiveTypeNEARGeneric,
17
18
  PrimitiveTypeNEARAccountWatch,
19
+ PrimitiveTypeCardanoMintBurn,
20
+ PrimitiveTypeCardanoTransfer,
21
+ PrimitiveTypeCardanoPoolDelegation,
22
+ PrimitiveTypeCardanoDelayedAsset,
23
+ PrimitiveTypeCardanoProjectedNFT,
18
24
  // PrimitiveTypeEVMGeneric,
19
25
  } from "./builtin.ts";
20
26
 
21
27
  import { MidnightGenericPrimitive } from "./midnight-generic/midnight-genetic.ts";
22
28
  import { MidnightNullifierPrimitive } from "./midnight-nullifier/midnight-nullifier.ts";
29
+ import { MidnightUnshieldedSpendPrimitive } from "./midnight-unshielded-spend/midnight-unshielded-spend.ts";
23
30
  import { EffectstreamL2Primitive } from "./evm-effectstream-l2/effectstream-l2-primitive.ts";
24
31
  import { Erc721Primitive } from "./evm-erc721/erc721-primitive.ts";
25
32
  import { Erc20Primitive } from "./evm-erc20/erc20-primitive.ts";
@@ -34,11 +41,17 @@ import { Nep245Primitive } from "./near-nep245/nep245-primitive.ts";
34
41
  import { NearIntentPrimitive } from "./near-intent/near-intent-primitive.ts";
35
42
  import { NearGenericPrimitive } from "./near-generic/near-generic-primitive.ts";
36
43
  import { NearAccountWatchPrimitive } from "./near-account-watch/near-account-watch-primitive.ts";
44
+ import { CardanoMintBurnPrimitive } from "./cardano-mint-burn/mint-burn-primitive.ts";
45
+ import { CardanoTransferPrimitive } from "./cardano-transfer/transfer-primitive.ts";
46
+ import { CardanoPoolDelegationPrimitive } from "./cardano-pool-delegation/pool-delegation-primitive.ts";
47
+ import { CardanoDelayedAssetPrimitive } from "./cardano-delayed-asset/delayed-asset-primitive.ts";
48
+ import { CardanoProjectedNftPrimitive } from "./cardano-projected-nft/projected-nft-primitive.ts";
37
49
  // import { EvmGenericPrimitive } from "./evm-generic/evm-generic-primitive.ts";
38
50
 
39
51
  const builtInPrimitivesMap = {
40
52
  [PrimitiveTypeMidnightGeneric]: MidnightGenericPrimitive,
41
53
  [PrimitiveTypeMidnightNullifier]: MidnightNullifierPrimitive,
54
+ [PrimitiveTypeMidnightUnshieldedSpend]: MidnightUnshieldedSpendPrimitive,
42
55
  [PrimitiveTypeEVMEffectstreamL2]: EffectstreamL2Primitive,
43
56
  [PrimitiveTypeEVMERC721]: Erc721Primitive,
44
57
  [PrimitiveTypeEVMERC20]: Erc20Primitive,
@@ -53,6 +66,11 @@ const builtInPrimitivesMap = {
53
66
  [PrimitiveTypeNEARIntent]: NearIntentPrimitive,
54
67
  [PrimitiveTypeNEARGeneric]: NearGenericPrimitive,
55
68
  [PrimitiveTypeNEARAccountWatch]: NearAccountWatchPrimitive,
69
+ [PrimitiveTypeCardanoMintBurn]: CardanoMintBurnPrimitive,
70
+ [PrimitiveTypeCardanoTransfer]: CardanoTransferPrimitive,
71
+ [PrimitiveTypeCardanoPoolDelegation]: CardanoPoolDelegationPrimitive,
72
+ [PrimitiveTypeCardanoDelayedAsset]: CardanoDelayedAssetPrimitive,
73
+ [PrimitiveTypeCardanoProjectedNFT]: CardanoProjectedNftPrimitive,
56
74
  // [PrimitiveTypeEVMGeneric]: EvmGenericPrimitive,
57
75
  } as const;
58
76
 
@@ -63,6 +81,7 @@ export {
63
81
  // Built-in Primitives
64
82
  MidnightGenericPrimitive,
65
83
  MidnightNullifierPrimitive,
84
+ MidnightUnshieldedSpendPrimitive,
66
85
  EffectstreamL2Primitive,
67
86
  Erc721Primitive,
68
87
  Erc20Primitive,
@@ -77,5 +96,10 @@ export {
77
96
  NearIntentPrimitive,
78
97
  NearGenericPrimitive,
79
98
  NearAccountWatchPrimitive,
99
+ CardanoMintBurnPrimitive,
100
+ CardanoTransferPrimitive,
101
+ CardanoPoolDelegationPrimitive,
102
+ CardanoDelayedAssetPrimitive,
103
+ CardanoProjectedNftPrimitive,
80
104
  // EvmGenericPrimitive,
81
105
  };
package/types.ts CHANGED
@@ -1,5 +1,3 @@
1
- //import type { AppEvents } from "@effectstream/events";
2
-
3
1
  import type { PreparedQuery } from "@pgtyped/runtime";
4
2
  import type {
5
3
  AddressType,
@@ -8,11 +6,27 @@ import type {
8
6
  WalletAddress,
9
7
  } from "@effectstream/utils";
10
8
  import type { Prando } from "@effectstream/crypto";
9
+ import type { EventPathAndDef, ResolvedPath } from "@effectstream/event-client";
10
+ import type { Static } from "@sinclair/typebox";
11
11
 
12
- // TODO What is AppEvent type?
13
- export type AppEvents = any;
12
+ /**
13
+ * Closure passed to every state transition for emitting custom app events.
14
+ *
15
+ * - The runtime collects events into a per-input buffer during STF execution.
16
+ * - On STF success, events are promoted to the per-block buffer.
17
+ * - After the block's `COMMIT` succeeds, events are flushed to MQTT.
18
+ * - On STF failure or block-level `ROLLBACK`, events are dropped.
19
+ *
20
+ * `blockHeight` is auto-injected by `registerEvents` and auto-filled by the
21
+ * runtime — apps omit it from the payload.
22
+ *
23
+ * See plan: /Users/edwardalvarado/.claude/plans/wild-kindling-reddy.md
24
+ */
25
+ export type EmitFn = <E extends EventPathAndDef>(
26
+ event: E,
27
+ payload: Omit<Static<E["type"]> & ResolvedPath<E["path"]>, "blockHeight">,
28
+ ) => void;
14
29
 
15
- // TODO: replace any
16
30
  export type BaseStfInput = {
17
31
  blockHeight: EffectstreamBlockNumber;
18
32
  blockTimestamp: TimestampMs;
@@ -21,7 +35,15 @@ export type BaseStfInput = {
21
35
  signerAddress?: WalletAddress;
22
36
  signerAddressType?: AddressType;
23
37
  randomGenerator: Prando;
38
+ /** Emit a custom app event. Delivered to MQTT after the block commits. */
39
+ emit: EmitFn;
24
40
  };
41
+
42
+ // Retained for compatibility with the existing `Stm<Grammar, Events>` generic
43
+ // parameter. Future work: thread a real registered-events union through here so
44
+ // `data.emit` is restricted to the app's declared events.
45
+ export type AppEvents = any;
46
+
25
47
  export type BaseStfOutput<Events extends AppEvents> = {
26
48
  stateTransitions: [PreparedQuery<any, any>, any][];
27
49
  events: Events[];