@midnight-ntwrk/wallet-sdk-dust-wallet 3.0.0-rc.0 → 4.0.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.
@@ -1,61 +1,67 @@
1
- import { type DustParameters, type DustPublicKey, DustSecretKey, type Signature, type SignatureVerifyingKey, type UnprovenTransaction } from '@midnight-ntwrk/ledger-v8';
2
- import { type ProtocolState, ProtocolVersion, SyncProgress } from '@midnight-ntwrk/wallet-sdk-abstractions';
3
- import { DustAddress } from '@midnight-ntwrk/wallet-sdk-address-format';
4
- import { type Variant, type WalletLike } from '@midnight-ntwrk/wallet-sdk-runtime/abstractions';
1
+ import { type DustParameters, type DustPublicKey, DustSecretKey, type FinalizedTransaction, type Signature, type SignatureVerifyingKey, type UnprovenTransaction } from '@midnight-ntwrk/ledger-v8';
2
+ import { type ProtocolState, ProtocolVersion, type SyncProgress } from '@midnight-ntwrk/wallet-sdk-abstractions';
3
+ import { type DustAddress } from '@midnight-ntwrk/wallet-sdk-address-format';
4
+ import { type Variant, type VariantBuilder, type WalletLike } from '@midnight-ntwrk/wallet-sdk-runtime/abstractions';
5
5
  import * as rx from 'rxjs';
6
6
  import { type Balance, type CoinsAndBalancesCapability, type UtxoWithFullDustDetails } from './v1/CoinsAndBalances.js';
7
7
  import { CoreWallet } from './v1/CoreWallet.js';
8
8
  import { type KeysCapability } from './v1/Keys.js';
9
9
  import { type SerializationCapability } from './v1/Serialization.js';
10
- import { type Dust, type DustFullInfo, type UtxoWithMeta } from './v1/types/Dust.js';
10
+ import { type DustFullInfo, type UtxoWithMeta } from './v1/types/Dust.js';
11
11
  import { type AnyTransaction } from './v1/types/ledger.js';
12
- import { type DefaultV1Configuration, type DefaultV1Variant } from './v1/V1Builder.js';
13
- export type DustWalletCapabilities = {
14
- serialization: SerializationCapability<CoreWallet, null, string>;
12
+ import { type BaseV1Configuration, type DefaultV1Configuration, type V1Variant } from './v1/V1Builder.js';
13
+ import { type WalletSyncUpdate } from './v1/Sync.js';
14
+ import { type TransactionHistoryService } from './v1/TransactionHistory.js';
15
+ export type DustWalletCapabilities<TSerialized = string> = {
16
+ serialization: SerializationCapability<CoreWallet, null, TSerialized>;
15
17
  coinsAndBalances: CoinsAndBalancesCapability<CoreWallet>;
16
18
  keys: KeysCapability<CoreWallet>;
17
19
  };
18
- export declare class DustWalletState {
19
- static readonly mapState: (capabilities: DustWalletCapabilities) => (state: ProtocolState.ProtocolState<CoreWallet>) => DustWalletState;
20
+ export type DustWalletServices = {
21
+ transactionHistory: TransactionHistoryService;
22
+ };
23
+ export declare class DustWalletState<TSerialized = string> {
24
+ static readonly mapState: <TSerialized_1 = string>(variant: DustWalletCapabilities<TSerialized_1> & DustWalletServices) => (state: ProtocolState.ProtocolState<CoreWallet>) => DustWalletState<TSerialized_1>;
20
25
  readonly protocolVersion: ProtocolVersion.ProtocolVersion;
21
26
  readonly state: CoreWallet;
22
- readonly capabilities: DustWalletCapabilities;
23
- get totalCoins(): readonly Dust[];
24
- get availableCoins(): readonly Dust[];
25
- get pendingCoins(): readonly Dust[];
27
+ readonly capabilities: DustWalletCapabilities<TSerialized>;
28
+ readonly services: DustWalletServices;
29
+ get totalCoins(): readonly DustFullInfo[];
30
+ get availableCoins(): readonly DustFullInfo[];
31
+ get pendingCoins(): readonly DustFullInfo[];
26
32
  get publicKey(): DustPublicKey;
27
33
  get address(): DustAddress;
28
34
  get progress(): SyncProgress.SyncProgress;
29
- /**
30
- * Transaction history for the wallet.
31
- * @throws Error - Not yet implemented
32
- */
33
- get transactionHistory(): never;
34
- constructor(state: ProtocolState.ProtocolState<CoreWallet>, capabilities: DustWalletCapabilities);
35
+ constructor(state: ProtocolState.ProtocolState<CoreWallet>, capabilities: DustWalletCapabilities<TSerialized>, services: DustWalletServices);
35
36
  balance(time: Date): Balance;
36
- availableCoinsWithFullInfo(time: Date): readonly DustFullInfo[];
37
37
  estimateDustGeneration(nightUtxos: ReadonlyArray<UtxoWithMeta>, currentTime: Date): ReadonlyArray<UtxoWithFullDustDetails>;
38
- serialize(): string;
38
+ serialize(): TSerialized;
39
39
  }
40
- export type DustWalletAPI = {
41
- readonly state: rx.Observable<DustWalletState>;
42
- start(secretKey: DustSecretKey): Promise<void>;
40
+ export type DustWalletAPI<TStartAux = DustSecretKey, TSerialized = string> = {
41
+ readonly state: rx.Observable<DustWalletState<TSerialized>>;
42
+ start(secretKey: TStartAux): Promise<void>;
43
43
  createDustGenerationTransaction(currentTime: Date | undefined, ttl: Date, nightUtxos: Array<UtxoWithMeta>, nightVerifyingKey: SignatureVerifyingKey, dustReceiverAddress: DustAddress | undefined): Promise<UnprovenTransaction>;
44
44
  addDustGenerationSignature(transaction: UnprovenTransaction, signature: Signature): Promise<UnprovenTransaction>;
45
45
  calculateFee(transactions: ReadonlyArray<AnyTransaction>): Promise<bigint>;
46
46
  estimateFee(secretKey: DustSecretKey, transactions: ReadonlyArray<AnyTransaction>, ttl?: Date, currentTime?: Date): Promise<bigint>;
47
47
  balanceTransactions(secretKey: DustSecretKey, transactions: ReadonlyArray<AnyTransaction>, ttl: Date, currentTime?: Date): Promise<UnprovenTransaction>;
48
- serializeState(): Promise<string>;
49
- waitForSyncedState(allowedGap?: bigint): Promise<DustWalletState>;
48
+ serializeState(): Promise<TSerialized>;
49
+ waitForSyncedState(allowedGap?: bigint): Promise<DustWalletState<TSerialized>>;
50
50
  revertTransaction(transaction: AnyTransaction): Promise<void>;
51
51
  getAddress(): Promise<DustAddress>;
52
52
  stop(): Promise<void>;
53
53
  };
54
- export type DustWallet = DustWalletAPI & WalletLike.WalletLike<[Variant.VersionedVariant<DefaultV1Variant>]>;
55
- export interface DustWalletClass extends WalletLike.BaseWalletClass<[Variant.VersionedVariant<DefaultV1Variant>]> {
56
- startWithSeed(seed: Uint8Array, dustParameters: DustParameters): DustWallet;
57
- startWithSecretKey(secretKey: DustSecretKey, dustParameters: DustParameters): DustWallet;
58
- restore(serializedState: string): DustWallet;
59
- }
54
+ export type CustomizedDustWallet<TStartAux = DustSecretKey, TTransaction = FinalizedTransaction, TSyncUpdate = WalletSyncUpdate, TSerialized = string> = DustWalletAPI<TStartAux, TSerialized> & WalletLike.WalletLike<[Variant.VersionedVariant<V1Variant<TSerialized, TSyncUpdate, TTransaction, TStartAux>>]>;
60
55
  export type DefaultDustConfiguration = DefaultV1Configuration;
56
+ export interface CustomizedDustWalletClass<TStartAux = DustSecretKey, TTransaction = FinalizedTransaction, TSyncUpdate = WalletSyncUpdate, TSerialized = string, TConfig extends BaseV1Configuration = DefaultDustConfiguration> extends WalletLike.BaseWalletClass<[
57
+ Variant.VersionedVariant<V1Variant<TSerialized, TSyncUpdate, TTransaction, TStartAux>>
58
+ ]> {
59
+ configuration: TConfig;
60
+ startWithSeed(seed: Uint8Array, dustParameters: DustParameters): CustomizedDustWallet<TStartAux, TTransaction, TSyncUpdate, TSerialized>;
61
+ startWithSecretKey(secretKey: DustSecretKey, dustParameters: DustParameters): CustomizedDustWallet<TStartAux, TTransaction, TSyncUpdate, TSerialized>;
62
+ restore(serializedState: TSerialized): CustomizedDustWallet<TStartAux, TTransaction, TSyncUpdate, TSerialized>;
63
+ }
64
+ export type DustWallet = CustomizedDustWallet<DustSecretKey, FinalizedTransaction, WalletSyncUpdate, string>;
65
+ export type DustWalletClass = CustomizedDustWalletClass<DustSecretKey, FinalizedTransaction, WalletSyncUpdate, string>;
61
66
  export declare function DustWallet(configuration: DefaultDustConfiguration): DustWalletClass;
67
+ export declare function CustomDustWallet<TConfig extends BaseV1Configuration = DefaultDustConfiguration, TStartAux = DustSecretKey, TTransaction = FinalizedTransaction, TSyncUpdate = WalletSyncUpdate, TSerialized = string>(configuration: TConfig, builder: VariantBuilder.VariantBuilder<V1Variant<TSerialized, TSyncUpdate, TTransaction, TStartAux>, TConfig>): CustomizedDustWalletClass<TStartAux, TTransaction, TSyncUpdate, TSerialized, TConfig>;
@@ -19,12 +19,15 @@ import { CoreWallet } from './v1/CoreWallet.js';
19
19
  import { V1Tag } from './v1/RunningV1Variant.js';
20
20
  import { V1Builder } from './v1/V1Builder.js';
21
21
  export class DustWalletState {
22
- static mapState = (capabilities) => (state) => {
23
- return new DustWalletState(state, capabilities);
22
+ static mapState = (variant) => (state) => {
23
+ const { serialization, coinsAndBalances, keys } = variant;
24
+ const { transactionHistory } = variant;
25
+ return new DustWalletState(state, { serialization, coinsAndBalances, keys }, { transactionHistory });
24
26
  };
25
27
  protocolVersion;
26
28
  state;
27
29
  capabilities;
30
+ services;
28
31
  get totalCoins() {
29
32
  return this.capabilities.coinsAndBalances.getTotalCoins(this.state);
30
33
  }
@@ -43,24 +46,15 @@ export class DustWalletState {
43
46
  get progress() {
44
47
  return this.state.progress;
45
48
  }
46
- /**
47
- * Transaction history for the wallet.
48
- * @throws Error - Not yet implemented
49
- */
50
- get transactionHistory() {
51
- throw new Error('Transaction history is not yet implemented for DustWallet');
52
- }
53
- constructor(state, capabilities) {
49
+ constructor(state, capabilities, services) {
54
50
  this.protocolVersion = state.version;
55
51
  this.state = state.state;
56
52
  this.capabilities = capabilities;
53
+ this.services = services;
57
54
  }
58
55
  balance(time) {
59
56
  return this.capabilities.coinsAndBalances.getWalletBalance(this.state, time);
60
57
  }
61
- availableCoinsWithFullInfo(time) {
62
- return this.capabilities.coinsAndBalances.getAvailableCoinsWithFullInfo(this.state, time);
63
- }
64
58
  estimateDustGeneration(nightUtxos, currentTime) {
65
59
  return this.capabilities.coinsAndBalances.estimateDustGeneration(this.state, nightUtxos, currentTime);
66
60
  }
@@ -69,26 +63,30 @@ export class DustWalletState {
69
63
  }
70
64
  }
71
65
  export function DustWallet(configuration) {
66
+ return CustomDustWallet(configuration, new V1Builder().withDefaults());
67
+ }
68
+ export function CustomDustWallet(configuration, builder) {
69
+ const buildArgs = [configuration];
72
70
  const BaseWallet = WalletBuilder.init()
73
- .withVariant(ProtocolVersion.MinSupportedVersion, new V1Builder().withDefaults())
74
- .build(configuration);
75
- return class DustWalletImplementation extends BaseWallet {
71
+ .withVariant(ProtocolVersion.MinSupportedVersion, builder)
72
+ .build(...buildArgs);
73
+ return class CustomDustWalletImplementation extends BaseWallet {
76
74
  static startWithSeed(seed, dustParameters) {
77
75
  const dustSecretKey = DustSecretKey.fromSeed(seed);
78
- return DustWalletImplementation.startFirst(DustWalletImplementation, CoreWallet.initEmpty(dustParameters, dustSecretKey, configuration.networkId));
76
+ return CustomDustWalletImplementation.startFirst(CustomDustWalletImplementation, CoreWallet.initEmpty(dustParameters, dustSecretKey, CustomDustWalletImplementation.configuration.networkId));
79
77
  }
80
78
  static startWithSecretKey(secretKey, dustParameters) {
81
- return DustWalletImplementation.startFirst(DustWalletImplementation, CoreWallet.initEmpty(dustParameters, secretKey, configuration.networkId));
79
+ return CustomDustWalletImplementation.startFirst(CustomDustWalletImplementation, CoreWallet.initEmpty(dustParameters, secretKey, CustomDustWalletImplementation.configuration.networkId));
82
80
  }
83
81
  static restore(serializedState) {
84
- const deserialized = DustWalletImplementation.allVariantsRecord()[V1Tag].variant.deserializeState(serializedState)
82
+ const deserialized = CustomDustWalletImplementation.allVariantsRecord()[V1Tag].variant.deserializeState(serializedState)
85
83
  .pipe(Either.getOrThrow);
86
- return DustWalletImplementation.startFirst(DustWalletImplementation, deserialized);
84
+ return CustomDustWalletImplementation.startFirst(CustomDustWalletImplementation, deserialized);
87
85
  }
88
86
  state;
89
87
  constructor(runtime, scope) {
90
88
  super(runtime, scope);
91
- this.state = this.rawState.pipe(rx.map(DustWalletState.mapState(DustWalletImplementation.allVariantsRecord()[V1Tag].variant)), rx.shareReplay({ refCount: true, bufferSize: 1 }));
89
+ this.state = this.rawState.pipe(rx.map(DustWalletState.mapState(CustomDustWalletImplementation.allVariantsRecord()[V1Tag].variant)), rx.shareReplay({ refCount: true, bufferSize: 1 }));
92
90
  }
93
91
  start(secretKey) {
94
92
  return this.runtime.dispatch({ [V1Tag]: (v1) => v1.startSyncInBackground(secretKey) }).pipe(Effect.runPromise);
@@ -139,10 +137,6 @@ export function DustWallet(configuration) {
139
137
  waitForSyncedState(allowedGap = 0n) {
140
138
  return rx.firstValueFrom(this.state.pipe(rx.filter((state) => state.state.progress.isCompleteWithin(allowedGap))));
141
139
  }
142
- /**
143
- * Serializes the most recent state
144
- * It's preferable to use [[DustWalletState.serialize]] instead, to know exactly, which state is serialized
145
- */
146
140
  serializeState() {
147
141
  return rx.firstValueFrom(this.state).then((state) => state.serialize());
148
142
  }
package/dist/index.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  export * from './DustWallet.js';
2
+ export { type DustTransactionHistoryEntry, DustSectionSchema, mergeDustSections } from './v1/TransactionHistory.js';
package/dist/index.js CHANGED
@@ -11,3 +11,4 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
  export * from './DustWallet.js';
14
+ export { DustSectionSchema, mergeDustSections } from './v1/TransactionHistory.js';
@@ -1,14 +1,14 @@
1
- import { CoreWallet } from './CoreWallet.js';
2
- import { KeysCapability } from './Keys.js';
3
- import { DustGenerationDetails, DustGenerationInfo, Dust, DustFullInfo, UtxoWithMeta } from './types/Dust.js';
1
+ import { type CoreWallet } from './CoreWallet.js';
2
+ import { type KeysCapability } from './Keys.js';
3
+ import { type DustGenerationDetails, type DustGenerationInfo, type Dust, type DustFullInfo, type UtxoWithMeta } from './types/Dust.js';
4
4
  export type Balance = bigint;
5
5
  export type CoinWithValue<TToken> = {
6
6
  token: TToken;
7
7
  value: Balance;
8
8
  };
9
9
  /**
10
- * Type describing a Night UTxO together with details of estimated Dust generation.
11
- * It is meant to be primarily used for fee estimation of Dust registration transaction
10
+ * Type describing a Night UTxO together with details of estimated Dust generation. It is meant to be primarily used for
11
+ * fee estimation of Dust registration transaction
12
12
  */
13
13
  export type UtxoWithFullDustDetails = Readonly<{
14
14
  utxo: UtxoWithMeta;
@@ -18,22 +18,21 @@ export type CoinSelection<TInput> = (coins: readonly CoinWithValue<TInput>[]) =>
18
18
  export declare const chooseCoin: <TInput>(coins: readonly CoinWithValue<TInput>[]) => CoinWithValue<TInput> | undefined;
19
19
  export type CoinsAndBalancesCapability<TState> = {
20
20
  getWalletBalance(state: TState, time: Date): Balance;
21
- getAvailableCoins(state: TState): readonly Dust[];
22
- getPendingCoins(state: TState): readonly Dust[];
23
- getTotalCoins(state: TState): ReadonlyArray<Dust>;
21
+ getAvailableCoins(state: TState, time?: Date): readonly DustFullInfo[];
22
+ getPendingCoins(state: TState, time?: Date): readonly DustFullInfo[];
23
+ getTotalCoins(state: TState, time?: Date): ReadonlyArray<DustFullInfo>;
24
24
  getAvailableCoinsWithGeneratedDust(state: TState, currentTime: Date): ReadonlyArray<CoinWithValue<Dust>>;
25
- getAvailableCoinsWithFullInfo(state: TState, blockTime: Date): readonly DustFullInfo[];
26
25
  getGenerationInfo(state: TState, coin: Dust): DustGenerationInfo | undefined;
27
- /**
28
- * Splits provided Night utxos into the ones that will be used as inputs in the guaranteed and fallible sections
29
- */
26
+ /** Splits provided Night utxos into the ones that will be used as inputs in the guaranteed and fallible sections */
30
27
  splitNightUtxos(nightUtxos: ReadonlyArray<UtxoWithFullDustDetails>): {
31
28
  guaranteed: ReadonlyArray<UtxoWithFullDustDetails>;
32
29
  fallible: ReadonlyArray<UtxoWithFullDustDetails>;
33
30
  };
34
31
  /**
35
- * Estimate how much Dust would be available to use if the Utxos provided were used for Dust generation from their beginning.
36
- * This function is particularly useful for the purpose of registering for Dust generation and selecting the Utxo to be used for paying fees and approving the registration itself.
32
+ * Estimate how much Dust would be available to use if the Utxos provided were used for Dust generation from their
33
+ * beginning. This function is particularly useful for the purpose of registering for Dust generation and selecting
34
+ * the Utxo to be used for paying fees and approving the registration itself.
35
+ *
37
36
  * @param state Current state of the wallet
38
37
  * @param nightUtxos Existing Night utxos
39
38
  * @param currentTime Current time
@@ -19,12 +19,6 @@ export const makeDefaultCoinsAndBalancesCapability = (_config, getContext) => {
19
19
  const getWalletBalance = (state, time) => {
20
20
  return state.state.walletBalance(time);
21
21
  };
22
- const getAvailableCoins = (state) => {
23
- const pendingSpends = new Set([...state.pendingDust.values()].map((coin) => coin.nonce));
24
- return pipe(state.state.utxos, Arr.filter((coin) => !pendingSpends.has(coin.nonce)));
25
- };
26
- const getPendingCoins = (state) => state.pendingDust;
27
- const getTotalCoins = (state) => [...getAvailableCoins(state), ...getPendingCoins(state)];
28
22
  const getGenerationInfo = (state, coin) => {
29
23
  const info = state.state.generationInfo(coin);
30
24
  return info && info.dtime
@@ -34,32 +28,22 @@ export const makeDefaultCoinsAndBalancesCapability = (_config, getContext) => {
34
28
  }
35
29
  : info;
36
30
  };
37
- const getAvailableCoinsWithGeneratedDust = (state, currentTime) => {
38
- const result = [];
39
- const available = getAvailableCoins(state);
40
- for (const coin of available) {
41
- const genInfo = getGenerationInfo(state, coin);
42
- if (genInfo) {
43
- const generatedValue = ledger.updatedValue(coin.ctime, coin.initialValue, genInfo, currentTime, state.state.params);
44
- result.push({ token: coin, value: generatedValue });
45
- }
46
- }
47
- return result;
31
+ const resolveTime = (state, time) => time ?? state.state.syncTime;
32
+ const toFullInfo = (state, coins, time) => coins.flatMap((coin) => {
33
+ const genInfo = getGenerationInfo(state, coin);
34
+ return genInfo ? [{ token: coin, ...getFullDustInfo(state.state.params, genInfo, coin, time) }] : [];
35
+ });
36
+ const availableDustTokens = (state) => {
37
+ const pendingSpends = new Set([...state.pendingDust.values()].map((coin) => coin.nonce));
38
+ return pipe(state.state.utxos, Arr.filter((coin) => !pendingSpends.has(coin.nonce)));
48
39
  };
49
- const getAvailableCoinsWithFullInfo = (state, blockTime) => {
50
- const result = [];
51
- const available = getAvailableCoins(state);
52
- for (const coin of available) {
53
- const genInfo = getGenerationInfo(state, coin);
54
- if (genInfo) {
55
- result.push({
56
- token: coin,
57
- ...getFullDustInfo(state.state.params, genInfo, coin, blockTime),
58
- });
59
- }
60
- }
61
- return result;
40
+ const getAvailableCoins = (state, time) => toFullInfo(state, availableDustTokens(state), resolveTime(state, time));
41
+ const getPendingCoins = (state, time) => toFullInfo(state, state.pendingDust, resolveTime(state, time));
42
+ const getTotalCoins = (state, time) => {
43
+ const effectiveTime = resolveTime(state, time);
44
+ return [...getAvailableCoins(state, effectiveTime), ...getPendingCoins(state, effectiveTime)];
62
45
  };
46
+ const getAvailableCoinsWithGeneratedDust = (state, currentTime) => getAvailableCoins(state, currentTime).map((info) => ({ token: info.token, value: info.generatedNow }));
63
47
  const getFullDustInfo = (parameters, genInfo, coin, currentTime) => {
64
48
  const generatedValue = ledger.updatedValue(coin.ctime, coin.initialValue, genInfo, currentTime, parameters);
65
49
  return {
@@ -79,9 +63,7 @@ export const makeDefaultCoinsAndBalancesCapability = (_config, getContext) => {
79
63
  return { utxo, dust: details };
80
64
  }));
81
65
  };
82
- /**
83
- * Create a fake generation info for a given Utxo. It allows to estimate the Dust generation from it
84
- */
66
+ /** Create a fake generation info for a given Utxo. It allows to estimate the Dust generation from it */
85
67
  const fakeGenerationInfo = (utxo, dustPublicKey) => {
86
68
  return {
87
69
  value: utxo.value,
@@ -90,9 +72,7 @@ export const makeDefaultCoinsAndBalancesCapability = (_config, getContext) => {
90
72
  dtime: undefined,
91
73
  };
92
74
  };
93
- /**
94
- * Create a fake dust coin for a given Utxo. It allows to estimate full details of the Dust generation from it
95
- */
75
+ /** Create a fake dust coin for a given Utxo. It allows to estimate full details of the Dust generation from it */
96
76
  const fakeDustToken = (dustPublicKey, utxo) => ({
97
77
  initialValue: 0n,
98
78
  owner: dustPublicKey,
@@ -112,7 +92,6 @@ export const makeDefaultCoinsAndBalancesCapability = (_config, getContext) => {
112
92
  getPendingCoins,
113
93
  getTotalCoins,
114
94
  getAvailableCoinsWithGeneratedDust,
115
- getAvailableCoinsWithFullInfo,
116
95
  getGenerationInfo,
117
96
  estimateDustGeneration,
118
97
  splitNightUtxos,
@@ -1,8 +1,8 @@
1
- import { Bindingish, DustLocalState, DustNullifier, DustParameters, DustPublicKey, DustSecretKey, Proofish, Signaturish, Transaction, Event } from '@midnight-ntwrk/ledger-v8';
1
+ import { type Bindingish, DustLocalState, type DustNullifier, type DustParameters, type DustPublicKey, type DustSecretKey, type DustStateChanges, type Proofish, type Signaturish, type Transaction, type Event } from '@midnight-ntwrk/ledger-v8';
2
2
  import { ProtocolVersion, SyncProgress } from '@midnight-ntwrk/wallet-sdk-abstractions';
3
- import { Dust, DustWithNullifier } from './types/Dust.js';
4
- import { CoinWithValue } from './CoinsAndBalances.js';
5
- import { NetworkId, UnprovenDustSpend } from './types/ledger.js';
3
+ import { type Dust, type DustWithNullifier } from './types/Dust.js';
4
+ import { type CoinWithValue } from './CoinsAndBalances.js';
5
+ import { type NetworkId, type UnprovenDustSpend } from './types/ledger.js';
6
6
  export type PublicKey = {
7
7
  publicKey: DustPublicKey;
8
8
  };
@@ -22,7 +22,7 @@ export declare const CoreWallet: {
22
22
  initEmpty(dustParameters: DustParameters, secretKey: DustSecretKey, networkId: NetworkId): CoreWallet;
23
23
  empty(localState: DustLocalState, publicKey: PublicKey, networkId: NetworkId): CoreWallet;
24
24
  restore(localState: DustLocalState, publicKey: PublicKey, pendingTokens: Array<DustWithNullifier>, syncProgress: Omit<SyncProgress.SyncProgressData, "isConnected">, protocolVersion: bigint, networkId: NetworkId): CoreWallet;
25
- applyEvents(wallet: CoreWallet, secretKey: DustSecretKey, events: Event[], currentTime: Date): CoreWallet;
25
+ applyEventsWithChanges(wallet: CoreWallet, secretKey: DustSecretKey, events: Event[], currentTime: Date): [CoreWallet, DustStateChanges[]];
26
26
  applyFailed(wallet: CoreWallet, tx: Transaction<Signaturish, Proofish, Bindingish>): CoreWallet;
27
27
  revertTransaction<TTransaction extends Transaction<Signaturish, Proofish, Bindingish>>(wallet: CoreWallet, tx: TTransaction): CoreWallet;
28
28
  updateProgress(wallet: CoreWallet, { appliedIndex, highestRelevantWalletIndex, highestIndex, highestRelevantIndex, isConnected, }: Partial<SyncProgress.SyncProgressData>): CoreWallet;
@@ -48,15 +48,19 @@ export const CoreWallet = {
48
48
  protocolVersion: ProtocolVersion.ProtocolVersion(protocolVersion),
49
49
  };
50
50
  },
51
- applyEvents(wallet, secretKey, events, currentTime) {
51
+ applyEventsWithChanges(wallet, secretKey, events, currentTime) {
52
52
  // TODO: replace currentTime with `updatedState.syncTime` introduced in ledger-6.2.0-rc.1
53
- const updatedState = wallet.state.replayEvents(secretKey, events).processTtls(currentTime);
53
+ const stateWithChanges = wallet.state.replayEventsWithChanges(secretKey, events);
54
+ const updatedState = stateWithChanges.state.processTtls(currentTime);
54
55
  const availableNonces = updatedState.utxos.map((utxo) => utxo.nonce);
55
- return {
56
- ...wallet,
57
- state: updatedState,
58
- pendingDust: wallet.pendingDust.filter((t) => availableNonces.includes(t.nonce)),
59
- };
56
+ return [
57
+ {
58
+ ...wallet,
59
+ state: updatedState,
60
+ pendingDust: wallet.pendingDust.filter((t) => availableNonces.includes(t.nonce)),
61
+ },
62
+ stateWithChanges.changes,
63
+ ];
60
64
  },
61
65
  applyFailed(wallet, tx) {
62
66
  const pendingSpendsMap = CoreWallet.pendingDustToMap(wallet.pendingDust);
package/dist/v1/Keys.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { DustPublicKey } from '@midnight-ntwrk/ledger-v8';
1
+ import { type DustPublicKey } from '@midnight-ntwrk/ledger-v8';
2
2
  import { DustAddress } from '@midnight-ntwrk/wallet-sdk-address-format';
3
- import { CoreWallet } from './CoreWallet.js';
3
+ import { type CoreWallet } from './CoreWallet.js';
4
4
  export type KeysCapability<TState> = {
5
5
  getPublicKey(state: TState): DustPublicKey;
6
6
  getAddress(state: TState): DustAddress;
@@ -1,26 +1,28 @@
1
1
  import { Effect, Stream, Scope } from 'effect';
2
+ import { type TransactionHistoryService } from './TransactionHistory.js';
2
3
  import { type DustSecretKey, type Signature, type SignatureVerifyingKey, type FinalizedTransaction, type UnprovenTransaction } from '@midnight-ntwrk/ledger-v8';
3
- import { WalletError } from './WalletError.js';
4
+ import { type WalletError } from './WalletError.js';
4
5
  import { type WalletRuntimeError, type Variant, StateChange } from '@midnight-ntwrk/wallet-sdk-runtime/abstractions';
5
6
  import { type Dust, type UtxoWithMeta } from './types/Dust.js';
6
7
  import { type KeysCapability } from './Keys.js';
7
- import { type SyncCapability, type SyncService } from './Sync.js';
8
- import { type SimulatorState } from './Simulator.js';
8
+ import { type ChangesResult, type SyncCapability, type SyncService } from './Sync.js';
9
+ import { type SimulatorState } from '@midnight-ntwrk/wallet-sdk-capabilities/simulation';
9
10
  import { type CoinsAndBalancesCapability, type CoinSelection } from './CoinsAndBalances.js';
10
11
  import { type TransactingCapability } from './Transacting.js';
11
12
  import { type CoreWallet } from './CoreWallet.js';
12
13
  import { type SerializationCapability } from './Serialization.js';
13
14
  import { type AnyTransaction } from './types/ledger.js';
14
- import { DustAddress } from '@midnight-ntwrk/wallet-sdk-address-format';
15
+ import { type DustAddress } from '@midnight-ntwrk/wallet-sdk-address-format';
15
16
  export declare namespace RunningV1Variant {
16
17
  type Context<TSerialized, TSyncUpdate, TTransaction, TStartAux> = {
17
18
  serializationCapability: SerializationCapability<CoreWallet, null, TSerialized>;
18
19
  syncService: SyncService<CoreWallet, TStartAux, TSyncUpdate>;
19
- syncCapability: SyncCapability<CoreWallet, TSyncUpdate>;
20
+ syncCapability: SyncCapability<CoreWallet, TSyncUpdate, ChangesResult>;
20
21
  transactingCapability: TransactingCapability<DustSecretKey, CoreWallet, TTransaction>;
21
22
  coinsAndBalancesCapability: CoinsAndBalancesCapability<CoreWallet>;
22
23
  keysCapability: KeysCapability<CoreWallet>;
23
24
  coinSelection: CoinSelection<Dust>;
25
+ transactionHistoryService: TransactionHistoryService;
24
26
  };
25
27
  type AnyContext = Context<any, any, any, any>;
26
28
  }
@@ -62,15 +62,16 @@ export class RunningV1Variant {
62
62
  return this.startSync(startAux).pipe(Stream.runScoped(Sink.drain), Effect.forkScoped, Effect.provideService(Scope.Scope, this.#scope));
63
63
  }
64
64
  startSync(startAux) {
65
- return pipe(SubscriptionRef.get(this.#context.stateRef), Stream.fromEffect, Stream.flatMap((state) => this.#v1Context.syncService.updates(state, startAux)), Stream.mapEffect((update) => {
66
- return SubscriptionRef.updateEffect(this.#context.stateRef, (state) => Effect.try({
67
- try: () => this.#v1Context.syncCapability.applyUpdate(state, update),
68
- catch: (err) => new OtherWalletError({
69
- message: 'Error while applying sync update',
70
- cause: err,
71
- }),
72
- }));
73
- }), Stream.tapError((error) => Console.error(error)), Stream.retry(pipe(Schedule.exponential(Duration.seconds(1), 2), Schedule.map((delay) => {
65
+ return pipe(SubscriptionRef.get(this.#context.stateRef), Stream.fromEffect, Stream.flatMap((state) => this.#v1Context.syncService.updates(state, startAux)), Stream.mapEffect((update) => SubscriptionRef.modifyEffect(this.#context.stateRef, (state) => Effect.try({
66
+ try: () => {
67
+ const [newState, changesResult] = this.#v1Context.syncCapability.applyUpdate(state, update);
68
+ return [changesResult, newState];
69
+ },
70
+ catch: (err) => new OtherWalletError({
71
+ message: 'Error while applying sync update',
72
+ cause: err,
73
+ }),
74
+ })).pipe(Effect.flatMap(({ changes, protocolVersion }) => pipe(Effect.forEach(changes, (change) => pipe(this.#v1Context.transactionHistoryService.getTransactionDetails(change.source), Effect.flatMap((metadata) => this.#v1Context.transactionHistoryService.put(change, metadata, protocolVersion)), Effect.catchAllCause((cause) => Console.error('Error processing tx history metadata', cause))), { discard: true, concurrency: 'unbounded' }), Effect.forkScoped)), Effect.provideService(Scope.Scope, this.#scope))), Stream.tapError((error) => Console.error(error)), Stream.retry(pipe(Schedule.exponential(Duration.seconds(1), 2), Schedule.map((delay) => {
74
75
  const maxDelay = Duration.minutes(2);
75
76
  const jitter = Duration.millis(Math.floor(Math.random() * 1000));
76
77
  const delayWithJitter = Duration.toMillis(delay) + Duration.toMillis(jitter);
@@ -1,5 +1,5 @@
1
1
  import { Either, Schema } from 'effect';
2
- import { WalletError } from './WalletError.js';
2
+ import { type WalletError } from './WalletError.js';
3
3
  import { CoreWallet } from './CoreWallet.js';
4
4
  export type SerializationCapability<TWallet, TAux, TSerialized> = {
5
5
  serialize(wallet: TWallet): TSerialized;
package/dist/v1/Sync.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { Effect, Layer, Schema, Scope, Stream } from 'effect';
2
- import { DustSecretKey, Event as LedgerEvent, LedgerParameters } from '@midnight-ntwrk/ledger-v8';
3
- import { SubscriptionClient, QueryClient } from '@midnight-ntwrk/wallet-sdk-indexer-client/effect';
4
- import { WalletError } from './WalletError.js';
5
- import { Simulator, SimulatorState } from './Simulator.js';
1
+ import { Effect, Layer, Schema, type Scope, Stream } from 'effect';
2
+ import { type DustSecretKey, type DustStateChanges, Event as LedgerEvent, LedgerParameters } from '@midnight-ntwrk/ledger-v8';
3
+ import { type SubscriptionClient, type QueryClient } from '@midnight-ntwrk/wallet-sdk-indexer-client/effect';
4
+ import { type WalletError } from './WalletError.js';
5
+ import { type Simulator, type SimulatorState } from '@midnight-ntwrk/wallet-sdk-capabilities/simulation';
6
6
  import { CoreWallet } from './CoreWallet.js';
7
- import { NetworkId } from './types/ledger.js';
7
+ import { type NetworkId } from './types/ledger.js';
8
8
  export interface SyncService<TState, TStartAux, TUpdate> {
9
9
  updates: (state: TState, auxData: TStartAux) => Stream.Stream<TUpdate, WalletError, Scope.Scope>;
10
10
  blockData: () => Effect.Effect<BlockData, WalletError>;
@@ -15,17 +15,44 @@ export interface BlockData {
15
15
  ledgerParameters: LedgerParameters;
16
16
  timestamp: Date;
17
17
  }
18
- export interface SyncCapability<TState, TUpdate> {
19
- applyUpdate: (state: TState, update: TUpdate) => TState;
18
+ export type ChangesResult = {
19
+ readonly changes: DustStateChanges[];
20
+ readonly protocolVersion: number;
21
+ };
22
+ export interface SyncCapability<TState, TUpdate, TResult> {
23
+ applyUpdate: (state: TState, update: TUpdate) => [TState, TResult];
20
24
  }
21
25
  export type IndexerClientConnection = {
22
26
  indexerHttpUrl: string;
23
27
  indexerWsUrl?: string;
24
28
  keepAlive?: number;
25
29
  };
30
+ export type BatchUpdatesConfig = {
31
+ /**
32
+ * Maximum number of events to collect into a single batch before emitting.
33
+ *
34
+ * @default 10
35
+ */
36
+ readonly size?: number;
37
+ /**
38
+ * Maximum time in milliseconds to wait for a full batch before emitting a partial one. Controls the `groupedWithin`
39
+ * timeout — lower values mean more responsive (but smaller) batches when events arrive slowly.
40
+ *
41
+ * @default 1
42
+ */
43
+ readonly timeout?: number;
44
+ /**
45
+ * Minimum delay in milliseconds injected between consecutive batches. Prevents the sync stream from saturating
46
+ * downstream consumers when many events are available at once. Set to 0 to disable spacing entirely.
47
+ *
48
+ * @default 4
49
+ */
50
+ readonly spacing?: number;
51
+ };
26
52
  export type DefaultSyncConfiguration = {
27
53
  indexerClientConnection: IndexerClientConnection;
28
54
  networkId: NetworkId;
55
+ batchUpdates?: BatchUpdatesConfig;
29
56
  };
30
57
  export type SimulatorSyncConfiguration = {
31
58
  simulator: Simulator;
@@ -35,7 +62,7 @@ export type SimulatorSyncUpdate = {
35
62
  update: SimulatorState;
36
63
  secretKey: DustSecretKey;
37
64
  };
38
- type SecretKeysResource = <A>(cb: (key: DustSecretKey) => A) => A;
65
+ export type SecretKeysResource = <A>(cb: (key: DustSecretKey) => A) => A;
39
66
  export declare const SecretKeysResource: {
40
67
  create: (secretKey: DustSecretKey) => SecretKeysResource;
41
68
  };
@@ -47,7 +74,7 @@ export declare const SyncEventsUpdateSchema: Schema.Struct<{
47
74
  export type WalletSyncSubscription = Schema.Schema.Type<typeof SyncEventsUpdateSchema>;
48
75
  export type WalletSyncUpdate = {
49
76
  updates: WalletSyncSubscription[];
50
- secretKeys: SecretKeysResource;
77
+ secretKey: DustSecretKey;
51
78
  timestamp: Date;
52
79
  };
53
80
  export declare const WalletSyncUpdate: {
@@ -60,7 +87,6 @@ export type IndexerSyncService = {
60
87
  queryClient: () => Layer.Layer<QueryClient, WalletError, Scope.Scope>;
61
88
  };
62
89
  export declare const makeIndexerSyncService: (config: DefaultSyncConfiguration) => IndexerSyncService;
63
- export declare const makeDefaultSyncCapability: () => SyncCapability<CoreWallet, WalletSyncUpdate>;
90
+ export declare const makeDefaultSyncCapability: () => SyncCapability<CoreWallet, WalletSyncUpdate, ChangesResult>;
64
91
  export declare const makeSimulatorSyncService: (config: SimulatorSyncConfiguration) => SyncService<CoreWallet, DustSecretKey, SimulatorSyncUpdate>;
65
- export declare const makeSimulatorSyncCapability: () => SyncCapability<CoreWallet, SimulatorSyncUpdate>;
66
- export {};
92
+ export declare const makeSimulatorSyncCapability: () => SyncCapability<CoreWallet, SimulatorSyncUpdate, ChangesResult>;