@midnight-ntwrk/wallet-sdk-unshielded-wallet 2.1.0 → 3.1.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 (38) hide show
  1. package/README.md +7 -8
  2. package/dist/KeyStore.d.ts +3 -3
  3. package/dist/UnshieldedWallet.d.ts +13 -5
  4. package/dist/UnshieldedWallet.js +16 -8
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.js +1 -1
  7. package/dist/v1/CoinsAndBalances.d.ts +3 -3
  8. package/dist/v1/CoreWallet.d.ts +5 -5
  9. package/dist/v1/Keys.d.ts +1 -1
  10. package/dist/v1/RunningV1Variant.d.ts +3 -1
  11. package/dist/v1/RunningV1Variant.js +5 -0
  12. package/dist/v1/Serialization.d.ts +2 -2
  13. package/dist/v1/Sync.d.ts +19 -6
  14. package/dist/v1/Sync.js +58 -6
  15. package/dist/v1/SyncSchema.d.ts +48 -48
  16. package/dist/v1/Transacting.d.ts +40 -16
  17. package/dist/v1/Transacting.js +73 -15
  18. package/dist/v1/TransactionHistory.d.ts +52 -10
  19. package/dist/v1/TransactionHistory.js +47 -24
  20. package/dist/v1/TransactionOps.d.ts +6 -10
  21. package/dist/v1/TransactionOps.js +5 -1
  22. package/dist/v1/UnshieldedState.d.ts +1 -1
  23. package/dist/v1/V1Builder.d.ts +18 -22
  24. package/dist/v1/WalletError.d.ts +19 -11
  25. package/dist/v1/WalletError.js +2 -0
  26. package/dist/v1/index.d.ts +1 -2
  27. package/dist/v1/index.js +1 -2
  28. package/package.json +9 -9
  29. package/dist/storage/InMemoryTransactionHistoryStorage.d.ts +0 -43
  30. package/dist/storage/InMemoryTransactionHistoryStorage.js +0 -65
  31. package/dist/storage/NoOpTransactionHistoryStorage.d.ts +0 -9
  32. package/dist/storage/NoOpTransactionHistoryStorage.js +0 -20
  33. package/dist/storage/TransactionHistoryStorage.d.ts +0 -34
  34. package/dist/storage/TransactionHistoryStorage.js +0 -33
  35. package/dist/storage/index.d.ts +0 -3
  36. package/dist/storage/index.js +0 -15
  37. package/dist/v1/Simulator.d.ts +0 -22
  38. package/dist/v1/Simulator.js +0 -103
package/README.md CHANGED
@@ -24,18 +24,17 @@ Unlike shielded transactions, unshielded operations do not use zero-knowledge pr
24
24
  ### Starting the Wallet
25
25
 
26
26
  ```typescript
27
- import {
28
- UnshieldedWallet,
29
- createKeystore,
30
- PublicKey,
31
- InMemoryTransactionHistoryStorage,
32
- } from '@midnight-ntwrk/wallet-sdk-unshielded-wallet';
27
+ import { UnshieldedWallet, createKeystore, PublicKey } from '@midnight-ntwrk/wallet-sdk-unshielded-wallet';
28
+ import { InMemoryTransactionHistoryStorage, NetworkId } from '@midnight-ntwrk/wallet-sdk-abstractions';
33
29
  import { randomBytes } from 'node:crypto';
34
30
 
35
31
  // Configuration for the wallet
36
32
  const configuration = {
37
- networkId: 'testnet', // or 'mainnet', 'undeployed'
38
- indexerUrl: 'ws://localhost:8088/api/v3/graphql/ws',
33
+ networkId: NetworkId.Undeployed, // or NetworkId.Testnet, NetworkId.Mainnet
34
+ indexerClientConnection: {
35
+ indexerWsUrl: 'ws://localhost:8088/api/v4/graphql/ws',
36
+ indexerHttpUrl: 'http://localhost:8088/api/v4/graphql',
37
+ },
39
38
  txHistoryStorage: new InMemoryTransactionHistoryStorage(),
40
39
  };
41
40
 
@@ -1,6 +1,6 @@
1
- import { MidnightBech32m } from '@midnight-ntwrk/wallet-sdk-address-format';
2
- import { Signature, SignatureVerifyingKey, UserAddress } from '@midnight-ntwrk/ledger-v8';
3
- import { NetworkId } from '@midnight-ntwrk/wallet-sdk-abstractions';
1
+ import { type MidnightBech32m } from '@midnight-ntwrk/wallet-sdk-address-format';
2
+ import { type Signature, type SignatureVerifyingKey, type UserAddress } from '@midnight-ntwrk/ledger-v8';
3
+ import { type NetworkId } from '@midnight-ntwrk/wallet-sdk-abstractions';
4
4
  export type PublicKey = {
5
5
  publicKey: SignatureVerifyingKey;
6
6
  addressHex: UserAddress;
@@ -12,27 +12,28 @@ import { type UtxoWithMeta } from './v1/UnshieldedState.js';
12
12
  import { type Variant, type VariantBuilder, type WalletLike } from '@midnight-ntwrk/wallet-sdk-runtime/abstractions';
13
13
  import { type PublicKey } from './KeyStore.js';
14
14
  import { type SyncProgress } from './v1/SyncProgress.js';
15
- import { type UnshieldedUpdate } from './v1/SyncSchema.js';
16
15
  import { type UnshieldedAddress } from '@midnight-ntwrk/wallet-sdk-address-format';
17
16
  export type UnshieldedWalletCapabilities<TSerialized = string> = {
18
17
  serialization: SerializationCapability<CoreWallet, TSerialized>;
19
18
  coinsAndBalances: CoinsAndBalancesCapability<CoreWallet>;
20
19
  keys: KeysCapability<CoreWallet>;
21
- transactionHistory: TransactionHistoryService<UnshieldedUpdate>;
20
+ };
21
+ export type UnshieldedWalletServices = {
22
+ transactionHistory: TransactionHistoryService;
22
23
  };
23
24
  export declare class UnshieldedWalletState<TSerialized = string> {
24
- static readonly mapState: <TSerialized_1 = string>(capabilities: UnshieldedWalletCapabilities<TSerialized_1>) => (state: ProtocolState.ProtocolState<CoreWallet>) => UnshieldedWalletState<TSerialized_1>;
25
+ static readonly mapState: <TSerialized_1 = string>(variant: UnshieldedWalletCapabilities<TSerialized_1> & UnshieldedWalletServices) => (state: ProtocolState.ProtocolState<CoreWallet>) => UnshieldedWalletState<TSerialized_1>;
25
26
  readonly protocolVersion: ProtocolVersion.ProtocolVersion;
26
27
  readonly state: CoreWallet;
27
28
  readonly capabilities: UnshieldedWalletCapabilities<TSerialized>;
29
+ readonly services: UnshieldedWalletServices;
28
30
  get balances(): Record<ledger.RawTokenType, bigint>;
29
31
  get totalCoins(): readonly UtxoWithMeta[];
30
32
  get availableCoins(): readonly UtxoWithMeta[];
31
33
  get pendingCoins(): readonly UtxoWithMeta[];
32
34
  get address(): UnshieldedAddress;
33
35
  get progress(): SyncProgress;
34
- get transactionHistory(): TransactionHistoryService<UnshieldedUpdate>;
35
- constructor(state: ProtocolState.ProtocolState<CoreWallet>, capabilities: UnshieldedWalletCapabilities<TSerialized>);
36
+ constructor(state: ProtocolState.ProtocolState<CoreWallet>, capabilities: UnshieldedWalletCapabilities<TSerialized>, services: UnshieldedWalletServices);
36
37
  serialize(): TSerialized;
37
38
  }
38
39
  export type UnshieldedWallet = CustomizedUnshieldedWallet<WalletSyncUpdate, string>;
@@ -45,6 +46,13 @@ export type UnshieldedWalletAPI<TSerialized = string> = {
45
46
  balanceUnboundTransaction(tx: UnboundTransaction): Promise<UnboundTransactionBalanceResult>;
46
47
  balanceUnprovenTransaction(tx: ledger.UnprovenTransaction): Promise<UnprovenTransactionBalanceResult>;
47
48
  transferTransaction(outputs: readonly TokenTransfer[], ttl: Date): Promise<ledger.UnprovenTransaction>;
49
+ /**
50
+ * Books a caller-supplied set of Night UTxOs and returns an unproven transaction that moves them back to the same
51
+ * owner, split between the guaranteed (segment 0) and fallible (segment 1) sections of a single intent. Booking moves
52
+ * the UTxOs from available to pending so a concurrent build call cannot reuse them. The fallible section is available
53
+ * for callers that want to attach further actions (e.g. a Dust registration) at segment 1.
54
+ */
55
+ rotateUtxos(guaranteedUtxos: readonly UtxoWithMeta[], fallibleUtxos: readonly UtxoWithMeta[], nightVerifyingKey: ledger.SignatureVerifyingKey, ttl: Date): Promise<ledger.UnprovenTransaction>;
48
56
  initSwap(desiredInputs: Record<ledger.RawTokenType, bigint>, desiredOutputs: readonly TokenTransfer[], ttl: Date): Promise<ledger.UnprovenTransaction>;
49
57
  signUnprovenTransaction(transaction: ledger.UnprovenTransaction, signSegment: (data: Uint8Array) => ledger.Signature): Promise<ledger.UnprovenTransaction>;
50
58
  signUnboundTransaction(transaction: UnboundTransaction, signSegment: (data: Uint8Array) => ledger.Signature): Promise<UnboundTransaction>;
@@ -16,12 +16,15 @@ import { Effect, Either } from 'effect';
16
16
  import * as rx from 'rxjs';
17
17
  import { WalletBuilder } from '@midnight-ntwrk/wallet-sdk-runtime';
18
18
  export class UnshieldedWalletState {
19
- static mapState = (capabilities) => (state) => {
20
- return new UnshieldedWalletState(state, capabilities);
19
+ static mapState = (variant) => (state) => {
20
+ const { serialization, coinsAndBalances, keys } = variant;
21
+ const { transactionHistory } = variant;
22
+ return new UnshieldedWalletState(state, { serialization, coinsAndBalances, keys }, { transactionHistory });
21
23
  };
22
24
  protocolVersion;
23
25
  state;
24
26
  capabilities;
27
+ services;
25
28
  get balances() {
26
29
  return this.capabilities.coinsAndBalances.getAvailableBalances(this.state);
27
30
  }
@@ -40,13 +43,11 @@ export class UnshieldedWalletState {
40
43
  get progress() {
41
44
  return this.state.progress;
42
45
  }
43
- get transactionHistory() {
44
- return this.capabilities.transactionHistory;
45
- }
46
- constructor(state, capabilities) {
46
+ constructor(state, capabilities, services) {
47
47
  this.protocolVersion = state.version;
48
48
  this.state = state.state;
49
49
  this.capabilities = capabilities;
50
+ this.services = services;
50
51
  }
51
52
  serialize() {
52
53
  return this.capabilities.serialization.serialize(this.state);
@@ -105,6 +106,13 @@ export function CustomUnshieldedWallet(configuration, builder) {
105
106
  })
106
107
  .pipe(Effect.runPromise);
107
108
  }
109
+ rotateUtxos(guaranteedUtxos, fallibleUtxos, nightVerifyingKey, ttl) {
110
+ return this.runtime
111
+ .dispatch({
112
+ [V1Tag]: (v1) => v1.rotateUtxos(guaranteedUtxos, fallibleUtxos, nightVerifyingKey, ttl),
113
+ })
114
+ .pipe(Effect.runPromise);
115
+ }
108
116
  initSwap(desiredInputs, desiredOutputs, ttl) {
109
117
  return this.runtime
110
118
  .dispatch({ [V1Tag]: (v1) => v1.initSwap(desiredInputs, desiredOutputs, ttl) })
@@ -135,8 +143,8 @@ export function CustomUnshieldedWallet(configuration, builder) {
135
143
  return rx.firstValueFrom(this.state.pipe(rx.filter((state) => state.state.progress.isCompleteWithin(allowedGap))));
136
144
  }
137
145
  /**
138
- * Serializes the most recent state
139
- * It's preferable to use [[UnshieldedWalletState.serialize]] instead, to know exactly, which state is serialized
146
+ * Serializes the most recent state It's preferable to use [[UnshieldedWalletState.serialize]] instead, to know
147
+ * exactly, which state is serialized
140
148
  */
141
149
  serializeState() {
142
150
  return rx.firstValueFrom(this.state).then((state) => state.serialize());
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export * from './UnshieldedWallet.js';
2
- export * from './storage/index.js';
2
+ export { type UnshieldedTransactionHistoryEntry, UnshieldedSectionSchema } from './v1/TransactionHistory.js';
3
3
  export * from './KeyStore.js';
package/dist/index.js CHANGED
@@ -11,5 +11,5 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
  export * from './UnshieldedWallet.js';
14
- export * from './storage/index.js';
14
+ export { UnshieldedSectionSchema } from './v1/TransactionHistory.js';
15
15
  export * from './KeyStore.js';
@@ -1,6 +1,6 @@
1
- import { CoreWallet } from './CoreWallet.js';
2
- import * as ledger from '@midnight-ntwrk/ledger-v8';
3
- import { UtxoWithMeta } from './UnshieldedState.js';
1
+ import { type CoreWallet } from './CoreWallet.js';
2
+ import type * as ledger from '@midnight-ntwrk/ledger-v8';
3
+ import { type UtxoWithMeta } from './UnshieldedState.js';
4
4
  export type Balances = Record<ledger.RawTokenType, bigint>;
5
5
  export type CoinsAndBalancesCapability<TState> = {
6
6
  getAvailableBalances(state: TState): Balances;
@@ -1,10 +1,10 @@
1
1
  import { ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
2
- import { SyncProgress, SyncProgressData } from './SyncProgress.js';
3
- import { PublicKey } from '../KeyStore.js';
4
- import { UnshieldedState, UnshieldedUpdate } from './UnshieldedState.js';
5
- import * as ledger from '@midnight-ntwrk/ledger-v8';
2
+ import { type SyncProgress, type SyncProgressData } from './SyncProgress.js';
3
+ import { type PublicKey } from '../KeyStore.js';
4
+ import { UnshieldedState, type UnshieldedUpdate } from './UnshieldedState.js';
5
+ import type * as ledger from '@midnight-ntwrk/ledger-v8';
6
6
  import { Either } from 'effect';
7
- import { WalletError } from './WalletError.js';
7
+ import { type WalletError } from './WalletError.js';
8
8
  export type CoreWallet = Readonly<{
9
9
  state: UnshieldedState;
10
10
  publicKey: PublicKey;
package/dist/v1/Keys.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { UnshieldedAddress } from '@midnight-ntwrk/wallet-sdk-address-format';
2
2
  import type { CoreWallet } from './CoreWallet.js';
3
- import { SignatureVerifyingKey } from '@midnight-ntwrk/ledger-v8';
3
+ import { type SignatureVerifyingKey } from '@midnight-ntwrk/ledger-v8';
4
4
  export type KeysCapability<TState> = {
5
5
  getPublicKey(state: TState): SignatureVerifyingKey;
6
6
  getAddress(state: TState): UnshieldedAddress;
@@ -4,6 +4,7 @@ import { type SerializationCapability } from './Serialization.js';
4
4
  import { type SyncCapability, type SyncService } from './Sync.js';
5
5
  import { type WalletSyncUpdate } from './SyncSchema.js';
6
6
  import { type TransactingCapability, type TokenTransfer, type FinalizedTransactionBalanceResult, type UnboundTransactionBalanceResult, type UnprovenTransactionBalanceResult } from './Transacting.js';
7
+ import { type UtxoWithMeta } from './UnshieldedState.js';
7
8
  import { type UnboundTransaction } from './TransactionOps.js';
8
9
  import { type WalletError } from './WalletError.js';
9
10
  import { type CoinsAndBalancesCapability } from './CoinsAndBalances.js';
@@ -21,7 +22,7 @@ export declare namespace RunningV1Variant {
21
22
  coinsAndBalancesCapability: CoinsAndBalancesCapability<CoreWallet>;
22
23
  keysCapability: KeysCapability<CoreWallet>;
23
24
  coinSelection: CoinSelection<ledger.Utxo>;
24
- transactionHistoryService: TransactionHistoryService<WalletSyncUpdate>;
25
+ transactionHistoryService: TransactionHistoryService;
25
26
  };
26
27
  type AnyContext = Context<any, any>;
27
28
  }
@@ -38,6 +39,7 @@ export declare class RunningV1Variant<TSerialized, TSyncUpdate> implements Varia
38
39
  balanceUnboundTransaction(tx: UnboundTransaction): Effect.Effect<UnboundTransactionBalanceResult, WalletError>;
39
40
  balanceUnprovenTransaction(tx: ledger.UnprovenTransaction): Effect.Effect<UnprovenTransactionBalanceResult, WalletError>;
40
41
  transferTransaction(outputs: ReadonlyArray<TokenTransfer>, ttl: Date): Effect.Effect<ledger.UnprovenTransaction, WalletError>;
42
+ rotateUtxos(guaranteedUtxos: ReadonlyArray<UtxoWithMeta>, fallibleUtxos: ReadonlyArray<UtxoWithMeta>, nightVerifyingKey: ledger.SignatureVerifyingKey, ttl: Date): Effect.Effect<ledger.UnprovenTransaction, WalletError>;
41
43
  initSwap(desiredInputs: Record<string, bigint>, desiredOutputs: ReadonlyArray<TokenTransfer>, ttl: Date): Effect.Effect<ledger.UnprovenTransaction, WalletError>;
42
44
  signUnprovenTransaction(transaction: ledger.UnprovenTransaction, signSegment: (data: Uint8Array) => ledger.Signature): Effect.Effect<ledger.UnprovenTransaction, WalletError>;
43
45
  signUnboundTransaction(transaction: UnboundTransaction, signSegment: (data: Uint8Array) => ledger.Signature): Effect.Effect<UnboundTransaction, WalletError>;
@@ -87,6 +87,11 @@ export class RunningV1Variant {
87
87
  return pipe(this.#v1Context.transactingCapability.makeTransfer(state, outputs, ttl), EitherOps.toEffect, Effect.map(({ transaction, newState }) => [transaction, newState]));
88
88
  });
89
89
  }
90
+ rotateUtxos(guaranteedUtxos, fallibleUtxos, nightVerifyingKey, ttl) {
91
+ return SubscriptionRef.modifyEffect(this.#context.stateRef, (state) => {
92
+ return pipe(this.#v1Context.transactingCapability.rotateUtxos(state, guaranteedUtxos, fallibleUtxos, nightVerifyingKey, ttl), EitherOps.toEffect, Effect.map(({ transaction, newState }) => [transaction, newState]));
93
+ });
94
+ }
90
95
  initSwap(desiredInputs, desiredOutputs, ttl) {
91
96
  return SubscriptionRef.modifyEffect(this.#context.stateRef, (state) => {
92
97
  return pipe(this.#v1Context.transactingCapability.initSwap(state, desiredInputs, desiredOutputs, ttl), Effect.map(({ transaction, newState }) => [transaction, newState]));
@@ -1,7 +1,7 @@
1
1
  import { Either } from 'effect';
2
- import { WalletError } from './WalletError.js';
2
+ import { type WalletError } from './WalletError.js';
3
3
  import { CoreWallet } from './CoreWallet.js';
4
- import { NetworkId } from '@midnight-ntwrk/wallet-sdk-abstractions';
4
+ import { type NetworkId } from '@midnight-ntwrk/wallet-sdk-abstractions';
5
5
  export type SerializationCapability<TWallet, TSerialized> = {
6
6
  serialize(wallet: TWallet): TSerialized;
7
7
  deserialize(data: TSerialized): Either.Either<TWallet, WalletError>;
package/dist/v1/Sync.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { Scope, Stream, Either } from 'effect';
1
+ import { type Scope, Stream, Either } from 'effect';
2
2
  import { CoreWallet } from './CoreWallet.js';
3
- import { Simulator, SimulatorState } from './Simulator.js';
4
- import { WalletError } from './WalletError.js';
5
- import { TransactionHistoryService } from './TransactionHistory.js';
6
- import { WalletSyncUpdate } from './SyncSchema.js';
3
+ import { type Simulator, type SimulatorState } from '@midnight-ntwrk/wallet-sdk-capabilities/simulation';
4
+ import { type WalletError } from './WalletError.js';
5
+ import { type TransactionHistoryService } from './TransactionHistory.js';
6
+ import { type WalletSyncUpdate } from './SyncSchema.js';
7
7
  export interface SyncService<TState, TUpdate> {
8
8
  updates: (state: TState) => Stream.Stream<TUpdate, WalletError, Scope.Scope>;
9
9
  }
@@ -19,7 +19,7 @@ export type DefaultSyncConfiguration = {
19
19
  indexerClientConnection: IndexerClientConnection;
20
20
  };
21
21
  export type DefaultSyncContext = {
22
- transactionHistoryService: TransactionHistoryService<WalletSyncUpdate>;
22
+ transactionHistoryService: TransactionHistoryService;
23
23
  };
24
24
  export declare const makeDefaultSyncService: (config: DefaultSyncConfiguration) => SyncService<CoreWallet, WalletSyncUpdate>;
25
25
  export declare const makeDefaultSyncCapability: (_config: DefaultSyncConfiguration, getContext: () => DefaultSyncContext) => SyncCapability<CoreWallet, WalletSyncUpdate>;
@@ -30,4 +30,17 @@ export type SimulatorSyncUpdate = {
30
30
  update: SimulatorState;
31
31
  };
32
32
  export declare const makeSimulatorSyncService: (config: SimulatorSyncConfiguration) => SyncService<CoreWallet, SimulatorSyncUpdate>;
33
+ /**
34
+ * Creates a sync capability that extracts UTXOs from the simulator's ledger state and applies them to the wallet.
35
+ *
36
+ * This capability:
37
+ *
38
+ * 1. Extracts all UTXOs for the wallet's address from the simulator ledger
39
+ * 2. Compares with the wallet's current UTXOs to determine created/spent
40
+ * 3. Applies the update to the wallet state
41
+ *
42
+ * Note: The `registeredForDustGeneration` flag is set based on whether the address appears in the ledger's dust
43
+ * delegation table. This is a heuristic that may not perfectly match the indexer's behavior but provides reasonable
44
+ * accuracy.
45
+ */
33
46
  export declare const makeSimulatorSyncCapability: () => SyncCapability<CoreWallet, SimulatorSyncUpdate>;
package/dist/v1/Sync.js CHANGED
@@ -10,14 +10,17 @@
10
10
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
- import { Stream, Schema, pipe, Either } from 'effect';
13
+ import { Effect, Stream, Schema, pipe, Either, HashMap } from 'effect';
14
14
  import { CoreWallet } from './CoreWallet.js';
15
+ import { UtxoWithMeta } from './UnshieldedState.js';
16
+ import { getCurrentBlockNumber, } from '@midnight-ntwrk/wallet-sdk-capabilities/simulation';
15
17
  import { UnshieldedTransactions } from '@midnight-ntwrk/wallet-sdk-indexer-client';
16
18
  import { WsSubscriptionClient, ConnectionHelper } from '@midnight-ntwrk/wallet-sdk-indexer-client/effect';
17
19
  import { SyncWalletError } from './WalletError.js';
18
20
  import { WsURL } from '@midnight-ntwrk/wallet-sdk-utilities/networking';
19
21
  import { EitherOps } from '@midnight-ntwrk/wallet-sdk-utilities';
20
22
  import { WalletSyncUpdateSchema } from './SyncSchema.js';
23
+ import * as ledger from '@midnight-ntwrk/ledger-v8';
21
24
  export const makeDefaultSyncService = (config) => {
22
25
  return {
23
26
  updates: (state) => {
@@ -63,7 +66,7 @@ export const makeDefaultSyncCapability = (_config, getContext) => {
63
66
  appliedId: BigInt(update.transaction.id),
64
67
  });
65
68
  const { transactionHistoryService } = getContext();
66
- void transactionHistoryService.create(update);
69
+ Effect.runFork(transactionHistoryService.put(update));
67
70
  return stateAfterUpdatingProgress;
68
71
  }));
69
72
  }
@@ -72,14 +75,63 @@ export const makeDefaultSyncCapability = (_config, getContext) => {
72
75
  };
73
76
  export const makeSimulatorSyncService = (config) => {
74
77
  return {
75
- updates: (_state) => config.simulator.state$.pipe(Stream.map((state) => ({ update: state }))),
78
+ updates: (_state) => {
79
+ // Get the initial state immediately to ensure we process existing blocks.
80
+ // Then subscribe to state$ for subsequent changes.
81
+ return pipe(Stream.fromEffect(config.simulator.getLatestState()), Stream.concat(config.simulator.state$), Stream.map((state) => ({ update: state })));
82
+ },
76
83
  };
77
84
  };
85
+ /**
86
+ * Creates a sync capability that extracts UTXOs from the simulator's ledger state and applies them to the wallet.
87
+ *
88
+ * This capability:
89
+ *
90
+ * 1. Extracts all UTXOs for the wallet's address from the simulator ledger
91
+ * 2. Compares with the wallet's current UTXOs to determine created/spent
92
+ * 3. Applies the update to the wallet state
93
+ *
94
+ * Note: The `registeredForDustGeneration` flag is set based on whether the address appears in the ledger's dust
95
+ * delegation table. This is a heuristic that may not perfectly match the indexer's behavior but provides reasonable
96
+ * accuracy.
97
+ */
78
98
  export const makeSimulatorSyncCapability = () => {
99
+ const utxoKey = (utxo) => `${utxo.intentHash}#${utxo.outputNo}`;
79
100
  return {
80
- applyUpdate: (state, _update) => {
81
- return Either.right(state);
82
- // return CoreWallet.replayEvents(state, secretKeys, events);
101
+ applyUpdate: (state, update) => {
102
+ const { ledger: ledgerState, currentTime } = update.update;
103
+ const walletAddress = state.publicKey.addressHex;
104
+ const nativeTokenType = ledger.nativeToken().raw;
105
+ // Heuristic: check if address appears in the ledger's dust delegation table
106
+ const isAddressRegisteredForDust = ledgerState.dust.toString().includes(walletAddress);
107
+ // Build a Map of simulator UTXOs keyed by intent hash + output number
108
+ const simulatorUtxoMap = new Map(Array.from(ledgerState.utxo.filter(walletAddress)).map((utxo) => [
109
+ utxoKey(utxo),
110
+ new UtxoWithMeta({
111
+ utxo,
112
+ meta: {
113
+ ctime: currentTime,
114
+ registeredForDustGeneration: utxo.type === nativeTokenType && isAddressRegisteredForDust,
115
+ },
116
+ }),
117
+ ]));
118
+ // Created: in simulator but not in wallet (neither available nor pending)
119
+ const createdUtxos = Array.from(simulatorUtxoMap)
120
+ .filter(([hash]) => !HashMap.has(state.state.availableUtxos, hash) && !HashMap.has(state.state.pendingUtxos, hash))
121
+ .map(([, utxo]) => utxo);
122
+ // Spent: in wallet (pending or available) but no longer in simulator
123
+ const spentUtxos = [
124
+ ...Array.from(HashMap.entries(state.state.pendingUtxos)),
125
+ ...Array.from(HashMap.entries(state.state.availableUtxos)),
126
+ ]
127
+ .filter(([hash]) => !simulatorUtxoMap.has(hash))
128
+ .map(([, utxo]) => utxo);
129
+ const blockNumber = getCurrentBlockNumber(update.update);
130
+ const updateProgress = (wallet) => CoreWallet.updateProgress(wallet, { appliedId: blockNumber, isConnected: true });
131
+ if (createdUtxos.length === 0 && spentUtxos.length === 0) {
132
+ return Either.right(updateProgress(state));
133
+ }
134
+ return pipe(CoreWallet.applyUpdate(state, { createdUtxos, spentUtxos, status: 'SUCCESS' }), Either.map(updateProgress));
83
135
  },
84
136
  };
85
137
  };
@@ -160,32 +160,19 @@ export declare const UnshieldedUpdateSchema: Schema.transform<Schema.Struct<{
160
160
  };
161
161
  }[];
162
162
  readonly status: "SUCCESS" | "FAILURE" | "PARTIAL_SUCCESS";
163
- readonly createdUtxos: readonly {
164
- readonly utxo: {
165
- readonly type: string;
166
- readonly value: bigint;
167
- readonly owner: string;
168
- readonly intentHash: string;
169
- readonly outputNo: number;
170
- };
171
- readonly meta: {
172
- readonly ctime: Date;
173
- readonly registeredForDustGeneration: boolean;
174
- };
175
- }[];
176
163
  readonly transaction: {
177
164
  readonly type: "RegularTransaction" | "SystemTransaction";
178
165
  readonly protocolVersion: number;
179
166
  readonly id: number;
180
167
  readonly hash: string;
181
168
  readonly identifiers?: readonly string[] | undefined;
169
+ readonly block: {
170
+ readonly timestamp: Date;
171
+ };
182
172
  readonly fees?: {
183
173
  readonly paidFees: bigint;
184
174
  readonly estimatedFees: bigint;
185
175
  } | undefined;
186
- readonly block: {
187
- readonly timestamp: Date;
188
- };
189
176
  readonly transactionResult?: {
190
177
  readonly status: "SUCCESS" | "FAILURE" | "PARTIAL_SUCCESS";
191
178
  readonly segments: readonly {
@@ -194,9 +181,7 @@ export declare const UnshieldedUpdateSchema: Schema.transform<Schema.Struct<{
194
181
  }[] | null;
195
182
  } | undefined;
196
183
  };
197
- }, {
198
- readonly type: "UnshieldedTransaction";
199
- readonly spentUtxos: readonly {
184
+ readonly createdUtxos: readonly {
200
185
  readonly utxo: {
201
186
  readonly type: string;
202
187
  readonly value: bigint;
@@ -209,8 +194,9 @@ export declare const UnshieldedUpdateSchema: Schema.transform<Schema.Struct<{
209
194
  readonly registeredForDustGeneration: boolean;
210
195
  };
211
196
  }[];
212
- readonly status: "SUCCESS" | "FAILURE" | "PARTIAL_SUCCESS";
213
- readonly createdUtxos: readonly {
197
+ }, {
198
+ readonly type: "UnshieldedTransaction";
199
+ readonly spentUtxos: readonly {
214
200
  readonly utxo: {
215
201
  readonly type: string;
216
202
  readonly value: bigint;
@@ -223,19 +209,20 @@ export declare const UnshieldedUpdateSchema: Schema.transform<Schema.Struct<{
223
209
  readonly registeredForDustGeneration: boolean;
224
210
  };
225
211
  }[];
212
+ readonly status: "SUCCESS" | "FAILURE" | "PARTIAL_SUCCESS";
226
213
  readonly transaction: {
227
214
  readonly type: "RegularTransaction" | "SystemTransaction";
228
215
  readonly protocolVersion: number;
229
216
  readonly id: number;
230
217
  readonly hash: string;
231
218
  readonly identifiers?: readonly string[] | undefined;
219
+ readonly block: {
220
+ readonly timestamp: Date;
221
+ };
232
222
  readonly fees?: {
233
223
  readonly paidFees: bigint;
234
224
  readonly estimatedFees: bigint;
235
225
  } | undefined;
236
- readonly block: {
237
- readonly timestamp: Date;
238
- };
239
226
  readonly transactionResult?: {
240
227
  readonly status: "SUCCESS" | "FAILURE" | "PARTIAL_SUCCESS";
241
228
  readonly segments: readonly {
@@ -244,6 +231,19 @@ export declare const UnshieldedUpdateSchema: Schema.transform<Schema.Struct<{
244
231
  }[] | null;
245
232
  } | undefined;
246
233
  };
234
+ readonly createdUtxos: readonly {
235
+ readonly utxo: {
236
+ readonly type: string;
237
+ readonly value: bigint;
238
+ readonly owner: string;
239
+ readonly intentHash: string;
240
+ readonly outputNo: number;
241
+ };
242
+ readonly meta: {
243
+ readonly ctime: Date;
244
+ readonly registeredForDustGeneration: boolean;
245
+ };
246
+ }[];
247
247
  }, never>>;
248
248
  export type UnshieldedUpdate = Schema.Schema.Type<typeof UnshieldedUpdateSchema>;
249
249
  export declare const ProgressSchema: Schema.Struct<{
@@ -355,32 +355,19 @@ export declare const WalletSyncUpdateSchema: Schema.Union<[Schema.transform<Sche
355
355
  };
356
356
  }[];
357
357
  readonly status: "SUCCESS" | "FAILURE" | "PARTIAL_SUCCESS";
358
- readonly createdUtxos: readonly {
359
- readonly utxo: {
360
- readonly type: string;
361
- readonly value: bigint;
362
- readonly owner: string;
363
- readonly intentHash: string;
364
- readonly outputNo: number;
365
- };
366
- readonly meta: {
367
- readonly ctime: Date;
368
- readonly registeredForDustGeneration: boolean;
369
- };
370
- }[];
371
358
  readonly transaction: {
372
359
  readonly type: "RegularTransaction" | "SystemTransaction";
373
360
  readonly protocolVersion: number;
374
361
  readonly id: number;
375
362
  readonly hash: string;
376
363
  readonly identifiers?: readonly string[] | undefined;
364
+ readonly block: {
365
+ readonly timestamp: Date;
366
+ };
377
367
  readonly fees?: {
378
368
  readonly paidFees: bigint;
379
369
  readonly estimatedFees: bigint;
380
370
  } | undefined;
381
- readonly block: {
382
- readonly timestamp: Date;
383
- };
384
371
  readonly transactionResult?: {
385
372
  readonly status: "SUCCESS" | "FAILURE" | "PARTIAL_SUCCESS";
386
373
  readonly segments: readonly {
@@ -389,9 +376,7 @@ export declare const WalletSyncUpdateSchema: Schema.Union<[Schema.transform<Sche
389
376
  }[] | null;
390
377
  } | undefined;
391
378
  };
392
- }, {
393
- readonly type: "UnshieldedTransaction";
394
- readonly spentUtxos: readonly {
379
+ readonly createdUtxos: readonly {
395
380
  readonly utxo: {
396
381
  readonly type: string;
397
382
  readonly value: bigint;
@@ -404,8 +389,9 @@ export declare const WalletSyncUpdateSchema: Schema.Union<[Schema.transform<Sche
404
389
  readonly registeredForDustGeneration: boolean;
405
390
  };
406
391
  }[];
407
- readonly status: "SUCCESS" | "FAILURE" | "PARTIAL_SUCCESS";
408
- readonly createdUtxos: readonly {
392
+ }, {
393
+ readonly type: "UnshieldedTransaction";
394
+ readonly spentUtxos: readonly {
409
395
  readonly utxo: {
410
396
  readonly type: string;
411
397
  readonly value: bigint;
@@ -418,19 +404,20 @@ export declare const WalletSyncUpdateSchema: Schema.Union<[Schema.transform<Sche
418
404
  readonly registeredForDustGeneration: boolean;
419
405
  };
420
406
  }[];
407
+ readonly status: "SUCCESS" | "FAILURE" | "PARTIAL_SUCCESS";
421
408
  readonly transaction: {
422
409
  readonly type: "RegularTransaction" | "SystemTransaction";
423
410
  readonly protocolVersion: number;
424
411
  readonly id: number;
425
412
  readonly hash: string;
426
413
  readonly identifiers?: readonly string[] | undefined;
414
+ readonly block: {
415
+ readonly timestamp: Date;
416
+ };
427
417
  readonly fees?: {
428
418
  readonly paidFees: bigint;
429
419
  readonly estimatedFees: bigint;
430
420
  } | undefined;
431
- readonly block: {
432
- readonly timestamp: Date;
433
- };
434
421
  readonly transactionResult?: {
435
422
  readonly status: "SUCCESS" | "FAILURE" | "PARTIAL_SUCCESS";
436
423
  readonly segments: readonly {
@@ -439,6 +426,19 @@ export declare const WalletSyncUpdateSchema: Schema.Union<[Schema.transform<Sche
439
426
  }[] | null;
440
427
  } | undefined;
441
428
  };
429
+ readonly createdUtxos: readonly {
430
+ readonly utxo: {
431
+ readonly type: string;
432
+ readonly value: bigint;
433
+ readonly owner: string;
434
+ readonly intentHash: string;
435
+ readonly outputNo: number;
436
+ };
437
+ readonly meta: {
438
+ readonly ctime: Date;
439
+ readonly registeredForDustGeneration: boolean;
440
+ };
441
+ }[];
442
442
  }, never>>, Schema.Struct<{
443
443
  type: Schema.Literal<["UnshieldedTransactionsProgress"]>;
444
444
  highestTransactionId: typeof Schema.Number;